@cofhe/sdk 1.0.0 → 1.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @cofhe/sdk Changelog
2
2
 
3
+ ## 1.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - fcc819c: **Breaking:** `encryptInputs(...).execute()` now binds the consuming contract into the ZK proof itself, and the CoFHE verifier requires it. Upgrade to 1.0.1: 1.0.0 is deprecated because of an issue in ZK verification, and once the verifier is updated a proof from 1.0.0 fails with `ZK_VERIFY_FAILED`. No code change is needed beyond the upgrade; `setConsumingContract(...)` was already required.
8
+
9
+ `execute()` now throws `CofheError` with code `CONSUMING_CONTRACT_INVALID` when `setConsumingContract(...)` was given something that is not an address, before TFHE is initialised or any proving starts. The check is byte-level, 20 bytes of hex in any case, and does not validate the EIP-55 checksum, so every address 1.0.0 accepted still passes. Previously the bad address reached the verifier and failed there, after the proof was generated. `CofheErrorCode` gains `ConsumingContractInvalid`; an exhaustive `switch` over the enum needs the new arm.
10
+
3
11
  ## 1.0.0
4
12
 
5
13
  ### Major Changes
@@ -21,7 +21,7 @@ import { cofheMocksCheckEncryptableBits, cofheMocksZkVerifySign } from './cofheM
21
21
  import { hardhat } from 'viem/chains';
22
22
  import { fetchKeys, type FheKeyDeserializer } from '../fetchKeys.js';
23
23
  import { getZkVerifierUrlOrThrow } from '../config.js';
24
- import { type WalletClient } from 'viem';
24
+ import { type WalletClient, isAddress } from 'viem';
25
25
  import { sleep } from '../utils.js';
26
26
  import { BaseBuilder, type BaseBuilderParams } from '../baseBuilder.js';
27
27
  import { type KeysStorage } from '../keyStore.js';
@@ -256,19 +256,37 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
256
256
  }
257
257
 
258
258
  /**
259
- * Asserts that this.consumingContract is populated
260
- * @throws {CofheError} If consumingContract is not set
259
+ * Asserts that this.consumingContract is populated and is an address.
260
+ * Runs before TFHE init, key fetching and proving, so a bad address costs
261
+ * nothing: the contract is bound into the proof, and a proof built for a
262
+ * non-address could only fail at the verifier, after the expensive step.
263
+ * @throws {CofheError} If consumingContract is not set or is not an address
261
264
  */
262
265
  private assertConsumingContract(): asserts this is this & { consumingContract: string } {
263
- if (this.consumingContract) return;
264
- throw new CofheError({
265
- code: CofheErrorCode.ConsumingContractUninitialized,
266
- message: 'Consuming contract is not set',
267
- hint: 'Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.',
268
- context: {
269
- consumingContract: this.consumingContract,
270
- },
271
- });
266
+ if (!this.consumingContract) {
267
+ throw new CofheError({
268
+ code: CofheErrorCode.ConsumingContractUninitialized,
269
+ message: 'Consuming contract is not set',
270
+ hint: 'Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.',
271
+ context: {
272
+ consumingContract: this.consumingContract,
273
+ },
274
+ });
275
+ }
276
+ // strict: false — 20 bytes of hex in any case, no EIP-55 checksum check. The
277
+ // verifier decodes the address case-insensitively and the chain compares bytes,
278
+ // so a mixed-case address with a wrong checksum works end to end and a patch
279
+ // release must keep accepting it.
280
+ if (!isAddress(this.consumingContract, { strict: false })) {
281
+ throw new CofheError({
282
+ code: CofheErrorCode.ConsumingContractInvalid,
283
+ message: 'Consuming contract is not an address',
284
+ hint: 'setConsumingContract(...) takes the 20-byte address of the contract that will consume the encrypted inputs.',
285
+ context: {
286
+ consumingContract: this.consumingContract,
287
+ },
288
+ });
289
+ }
272
290
  }
273
291
 
274
292
  /**
@@ -567,7 +585,7 @@ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuild
567
585
  this.fireStepStart(EncryptStep.Prove);
568
586
 
569
587
  // Construct metadata once (used by both worker and main thread paths)
570
- const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId);
588
+ const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId, this.consumingContract);
571
589
 
572
590
  let proof: Uint8Array | null = null;
573
591
  let usedWorker = false;
@@ -239,13 +239,24 @@ export const zkProve = async (
239
239
  };
240
240
 
241
241
  /**
242
- * Constructs the ZK Proof of Knowledge metadata for the proof
242
+ * Constructs the ZK Proof of Knowledge metadata for the proof:
243
+ * `securityZone (1) || account (20) || chainId (32, big-endian) || consumingContract (20)`.
244
+ *
245
+ * The verifier rebuilds the same bytes from the request fields before it checks the proof, so the
246
+ * proof is bound to the consuming contract itself, not only to the signature the verifier returns.
243
247
  * @internal - Used internally within the encrypt module
244
248
  */
245
- export const constructZkPoKMetadata = (accountAddr: string, securityZone: number, chainId: number): Uint8Array => {
246
- // Decode the account address from hex
249
+ export const constructZkPoKMetadata = (
250
+ accountAddr: string,
251
+ securityZone: number,
252
+ chainId: number,
253
+ consumingContract: string
254
+ ): Uint8Array => {
255
+ // Decode the account and contract addresses from hex
247
256
  const accountAddrNoPrefix = accountAddr.startsWith('0x') ? accountAddr.slice(2) : accountAddr;
248
257
  const accountBytes = hexToBytes(accountAddrNoPrefix);
258
+ const contractNoPrefix = consumingContract.startsWith('0x') ? consumingContract.slice(2) : consumingContract;
259
+ const contractBytes = hexToBytes(contractNoPrefix);
249
260
 
250
261
  // Encode chainId as 32 bytes (u256) in big-endian format
251
262
  const chainIdBytes = new Uint8Array(32);
@@ -259,10 +270,11 @@ export const constructZkPoKMetadata = (accountAddr: string, securityZone: number
259
270
  value = value >>> 8;
260
271
  }
261
272
 
262
- const metadata = new Uint8Array(1 + accountBytes.length + 32);
273
+ const metadata = new Uint8Array(1 + accountBytes.length + 32 + contractBytes.length);
263
274
  metadata[0] = securityZone;
264
275
  metadata.set(accountBytes, 1);
265
276
  metadata.set(chainIdBytes, 1 + accountBytes.length);
277
+ metadata.set(contractBytes, 1 + accountBytes.length + 32);
266
278
 
267
279
  return metadata;
268
280
  };
package/core/error.ts CHANGED
@@ -16,6 +16,7 @@ export enum CofheErrorCode {
16
16
  AccountUninitialized = 'ACCOUNT_UNINITIALIZED',
17
17
  ChainIdUninitialized = 'CHAIN_ID_UNINITIALIZED',
18
18
  ConsumingContractUninitialized = 'CONSUMING_CONTRACT_UNINITIALIZED',
19
+ ConsumingContractInvalid = 'CONSUMING_CONTRACT_INVALID',
19
20
  SealOutputFailed = 'SEAL_OUTPUT_FAILED',
20
21
  SealOutputReturnedNull = 'SEAL_OUTPUT_RETURNED_NULL',
21
22
  InvalidUtype = 'INVALID_UTYPE',
@@ -10,7 +10,7 @@ import {
10
10
  } from '../types.js';
11
11
  import { CofheError, CofheErrorCode } from '../error.js';
12
12
  import { fromHexString, toHexString } from '../utils.js';
13
- import { type PublicClient, createPublicClient, http, type WalletClient, createWalletClient } from 'viem';
13
+ import { type PublicClient, createPublicClient, http, type WalletClient, createWalletClient, isAddress } from 'viem';
14
14
  import { privateKeyToAccount } from 'viem/accounts';
15
15
  import { arbitrumSepolia } from 'viem/chains';
16
16
  import { type CofheConfig, createCofheConfigBase } from '../config.js';
@@ -53,9 +53,9 @@ const unpackMetadata = (metadata: string) => {
53
53
 
54
54
  export const deconstructZkPoKMetadata = (
55
55
  metadata: Uint8Array
56
- ): { accountAddr: string; securityZone: number; chainId: number } => {
57
- if (metadata.length < 53) {
58
- // 1 + 20 + 32 = 53 bytes minimum
56
+ ): { accountAddr: string; securityZone: number; chainId: number; contractAddr: string } => {
57
+ if (metadata.length < 73) {
58
+ // 1 + 20 + 32 + 20 = 73 bytes minimum
59
59
  throw new CofheError({
60
60
  code: CofheErrorCode.InternalError,
61
61
  message: 'Invalid metadata: insufficient length',
@@ -78,10 +78,14 @@ export const deconstructZkPoKMetadata = (
78
78
  chainId = (chainId << 8) | chainIdBytes[i];
79
79
  }
80
80
 
81
+ // Extract the consuming contract (last 20 bytes)
82
+ const contractAddr = '0x' + toHexString(metadata.slice(53, 73));
83
+
81
84
  return {
82
85
  accountAddr,
83
86
  securityZone,
84
87
  chainId,
88
+ contractAddr,
85
89
  };
86
90
  };
87
91
 
@@ -445,6 +449,38 @@ describe('EncryptInputsBuilder', () => {
445
449
  expect((error as CofheError).code).toBe(CofheErrorCode.ConsumingContractUninitialized);
446
450
  }
447
451
  });
452
+
453
+ it('accepts a mixed-case consuming contract whose checksum is wrong', async () => {
454
+ // The verifier decodes the address case-insensitively and the chain compares
455
+ // 20 bytes, so a 1.0.0 dApp with such an address must keep working.
456
+ const wrongChecksum = '0xBEEFbeefBEEFbeefBEEFbeefBEEFbeefBEEFbeef';
457
+ expect(isAddress(wrongChecksum)).toBe(false);
458
+ expect(isAddress(wrongChecksum, { strict: false })).toBe(true);
459
+
460
+ const result = await new EncryptInputsBuilder(createDefaultParams())
461
+ .setConsumingContract(wrongChecksum)
462
+ .execute();
463
+
464
+ expect(Array.isArray(result)).toBe(true);
465
+ });
466
+
467
+ it('rejects a consuming contract that is not an address before any encrypt step runs', async () => {
468
+ for (const notAnAddress of ['0x0', 'not-an-address', '0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbee']) {
469
+ const steps = vi.fn();
470
+ const invalid = new EncryptInputsBuilder(createDefaultParams())
471
+ .setConsumingContract(notAnAddress)
472
+ .onStep(steps);
473
+ try {
474
+ await invalid.execute();
475
+ expect.unreachable(`execute() should have thrown for ${notAnAddress}`);
476
+ } catch (error) {
477
+ expect(error, notAnAddress).toBeInstanceOf(CofheError);
478
+ expect((error as CofheError).code, notAnAddress).toBe(CofheErrorCode.ConsumingContractInvalid);
479
+ }
480
+ // Rejected before initTfhe, so no step (and no proving) ever started.
481
+ expect(steps, notAnAddress).not.toHaveBeenCalled();
482
+ }
483
+ });
448
484
  });
449
485
 
450
486
  describe('zkVerifierUrl', () => {
@@ -595,6 +631,22 @@ describe('EncryptInputsBuilder', () => {
595
631
  expect(encryptedMetadata.chainId).toBe(defaultChainId);
596
632
  });
597
633
 
634
+ it('binds the consuming contract into the proof metadata', async () => {
635
+ mockFetch.mockClear();
636
+
637
+ await builder.execute();
638
+
639
+ const verifyCall = mockFetch.mock.calls.find(([url]) => url === `${MockZkVerifierUrl}/verifyBatch`);
640
+ expect(verifyCall).toBeDefined();
641
+ const body = JSON.parse(verifyCall![1].body as string);
642
+ const { metadata } = parseWithBigInt(new TextDecoder().decode(fromHexString(body.packed_list)));
643
+ const embedded = deconstructZkPoKMetadata(Uint8Array.from(Object.values(metadata) as number[]));
644
+ expect(embedded.accountAddr).toBe(defaultSender);
645
+ expect(embedded.securityZone).toBe(0);
646
+ expect(embedded.chainId).toBe(defaultChainId);
647
+ expect(embedded.contractAddr).toBe(defaultConsumingContract);
648
+ });
649
+
598
650
  it('should use overridden account when set', async () => {
599
651
  const overriddenSender = '0x5555555555555555555555555555555555555555';
600
652
  builder.setAccount(overriddenSender);
@@ -0,0 +1,27 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { constructZkPoKMetadata } from '../encrypt/zkPackProveVerify.js';
3
+ import { toHexString } from '../utils.js';
4
+
5
+ describe('constructZkPoKMetadata', () => {
6
+ const account = '0x1234567890123456789012345678901234567890';
7
+ const contract = '0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef';
8
+
9
+ it('lays out security zone, account, chain id and the consuming contract', () => {
10
+ const metadata = constructZkPoKMetadata(account, 3, 0x010203, contract);
11
+
12
+ expect(metadata.length).toBe(1 + 20 + 32 + 20);
13
+ expect(metadata[0]).toBe(3);
14
+ expect('0x' + toHexString(metadata.slice(1, 21))).toBe(account);
15
+ const chainId = metadata.slice(21, 53);
16
+ expect(Array.from(chainId.slice(0, 29))).toEqual(new Array(29).fill(0));
17
+ expect(Array.from(chainId.slice(29))).toEqual([1, 2, 3]);
18
+ expect('0x' + toHexString(metadata.slice(53))).toBe(contract);
19
+ });
20
+
21
+ it('accepts the consuming contract with or without the 0x prefix', () => {
22
+ const withPrefix = constructZkPoKMetadata(account, 0, 1, contract);
23
+ const withoutPrefix = constructZkPoKMetadata(account, 0, 1, contract.slice(2));
24
+
25
+ expect(Array.from(withoutPrefix)).toEqual(Array.from(withPrefix));
26
+ });
27
+ });
package/dist/acps.js CHANGED
@@ -1,2 +1,2 @@
1
- export { ACPScope, ACPUtils, ACP_REVOKER_ABI, ACP_SHARE_REGISTRY_ABI, ACP_SHARE_TUPLE_ABI, ACP_STORE_DEFAULTS, ERC7984_ABI, GenerateSealingKey, ImportACPOptionsValidator, ImportACPValidator, LAYOUT_VERSION as SHARE_METADATA_VERSION, SelfACPOptionsValidator, SelfACPValidator, SharingACPOptionsValidator, SharingACPValidator, SignatureTypes, SignatureUtils, UNLABELLED, ValidationUtils, _acpStore, acpStore, addressNotZeroSchema, addressSchema, bytesNotEmptySchema, bytesSchema, clearAclServedAddresses, clearStaleStore, computeShareId, contractsSchema, decodeShareAppKey, decodeShareMetadata, decodeShareMetadataFull, describeShareLabels, describeShareMetadata, encodeShareMetadata, eventLogLabel, getACP, getACPs, getAclAddress, getAclServedAddresses, getActiveACP, getActiveACPHash, getSignatureTypesAndMessage, handlesSchema, readPostedShares, readShare, readSharesFor, removeACP, removeActiveACPHash, resetStore, seal, setACP, setActiveACPHash, shareAppKey, shareIdOfChainShare, toChainShare, unsealWithPrivateKey, validateImportACP, validateImportACPOptions, validateSelfACP, validateSelfACPOptions, validateSharingACP, validateSharingACPOptions, verifyShareLabel, verifyShareLabels, viewFunctionLabel } from './chunk-3L2IVPMI.js';
2
- import './chunk-J23FYAI2.js';
1
+ export { ACPScope, ACPUtils, ACP_REVOKER_ABI, ACP_SHARE_REGISTRY_ABI, ACP_SHARE_TUPLE_ABI, ACP_STORE_DEFAULTS, ERC7984_ABI, GenerateSealingKey, ImportACPOptionsValidator, ImportACPValidator, LAYOUT_VERSION as SHARE_METADATA_VERSION, SelfACPOptionsValidator, SelfACPValidator, SharingACPOptionsValidator, SharingACPValidator, SignatureTypes, SignatureUtils, UNLABELLED, ValidationUtils, _acpStore, acpStore, addressNotZeroSchema, addressSchema, bytesNotEmptySchema, bytesSchema, clearAclServedAddresses, clearStaleStore, computeShareId, contractsSchema, decodeShareAppKey, decodeShareMetadata, decodeShareMetadataFull, describeShareLabels, describeShareMetadata, encodeShareMetadata, eventLogLabel, getACP, getACPs, getAclAddress, getAclServedAddresses, getActiveACP, getActiveACPHash, getSignatureTypesAndMessage, handlesSchema, readPostedShares, readShare, readSharesFor, removeACP, removeActiveACPHash, resetStore, seal, setACP, setActiveACPHash, shareAppKey, shareIdOfChainShare, toChainShare, unsealWithPrivateKey, validateImportACP, validateImportACPOptions, validateSelfACP, validateSelfACPOptions, validateSharingACP, validateSharingACPOptions, verifyShareLabel, verifyShareLabels, viewFunctionLabel } from './chunk-WAA2U7CT.js';
2
+ import './chunk-OC5TGQGG.js';
@@ -1,4 +1,4 @@
1
- import { CofheError } from './chunk-J23FYAI2.js';
1
+ import { CofheError } from './chunk-OC5TGQGG.js';
2
2
  import 'viem';
3
3
 
4
4
  // core/types.ts
@@ -1,9 +1,9 @@
1
1
  import { hardhat as hardhat$1 } from './chunk-UKQAJK4N.js';
2
- import { acpStore, clearAclServedAddresses, getAclServedAddresses, computeShareId, ACPUtils, getTaskManagerAddress, verifyShareLabels, decodeShareMetadata, shareAppKey, ACP_SHARE_REGISTRY_ABI, readShare, readSharesFor, toChainShare, ACP_REVOKER_ABI, decodeShareAppKey, encodeShareMetadata } from './chunk-3L2IVPMI.js';
3
- import { sleep, getPublicClientChainID, getWalletClientAccount, validateBigIntInRange, toBigIntOrThrow, hexToBytes, toHexString, FheUintUTypes } from './chunk-WJGTPW7K.js';
4
- import { TFHE_RS_KEY_VERSION, CofheError, TFHE_RS_ZK_MAX_BITS, TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT, MOCKS_THRESHOLD_NETWORK_ADDRESS, MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY, MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY, MOCKS_ZK_VERIFIER_ADDRESS } from './chunk-J23FYAI2.js';
2
+ import { acpStore, clearAclServedAddresses, getAclServedAddresses, computeShareId, ACPUtils, getTaskManagerAddress, verifyShareLabels, decodeShareMetadata, shareAppKey, ACP_SHARE_REGISTRY_ABI, readShare, readSharesFor, toChainShare, ACP_REVOKER_ABI, decodeShareAppKey, encodeShareMetadata } from './chunk-WAA2U7CT.js';
3
+ import { sleep, getPublicClientChainID, getWalletClientAccount, validateBigIntInRange, toBigIntOrThrow, hexToBytes, toHexString, FheUintUTypes } from './chunk-3SD2UCRE.js';
4
+ import { TFHE_RS_KEY_VERSION, CofheError, TFHE_RS_ZK_MAX_BITS, TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT, MOCKS_THRESHOLD_NETWORK_ADDRESS, MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY, MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY, MOCKS_ZK_VERIFIER_ADDRESS } from './chunk-OC5TGQGG.js';
5
5
  import { createStore } from 'zustand/vanilla';
6
- import { parseAbi, isAddressEqual, zeroAddress, recoverAddress, encodePacked, keccak256, createWalletClient, http, getAddress, parseSignature, serializeSignature } from 'viem';
6
+ import { parseAbi, isAddress, isAddressEqual, zeroAddress, recoverAddress, encodePacked, keccak256, createWalletClient, http, getAddress, parseSignature, serializeSignature } from 'viem';
7
7
  import { hardhat } from 'viem/chains';
8
8
  import { sign, privateKeyToAccount } from 'viem/accounts';
9
9
  import { z } from 'zod';
@@ -110,19 +110,22 @@ var zkProve = async (builder, crs, metadata) => {
110
110
  }, 0);
111
111
  });
112
112
  };
113
- var constructZkPoKMetadata = (accountAddr, securityZone, chainId) => {
113
+ var constructZkPoKMetadata = (accountAddr, securityZone, chainId, consumingContract) => {
114
114
  const accountAddrNoPrefix = accountAddr.startsWith("0x") ? accountAddr.slice(2) : accountAddr;
115
115
  const accountBytes = hexToBytes(accountAddrNoPrefix);
116
+ const contractNoPrefix = consumingContract.startsWith("0x") ? consumingContract.slice(2) : consumingContract;
117
+ const contractBytes = hexToBytes(contractNoPrefix);
116
118
  const chainIdBytes = new Uint8Array(32);
117
119
  let value = chainId;
118
120
  for (let i = 31; i >= 0 && value > 0; i--) {
119
121
  chainIdBytes[i] = value & 255;
120
122
  value = value >>> 8;
121
123
  }
122
- const metadata = new Uint8Array(1 + accountBytes.length + 32);
124
+ const metadata = new Uint8Array(1 + accountBytes.length + 32 + contractBytes.length);
123
125
  metadata[0] = securityZone;
124
126
  metadata.set(accountBytes, 1);
125
127
  metadata.set(chainIdBytes, 1 + accountBytes.length);
128
+ metadata.set(contractBytes, 1 + accountBytes.length + 32);
126
129
  return metadata;
127
130
  };
128
131
  var zkVerifyBatch = async (verifierUrl, serializedBytes, address, securityZone, chainId, consumingContract) => {
@@ -1002,20 +1005,33 @@ var EncryptInputsBuilder = class extends BaseBuilder {
1002
1005
  return this.consumingContract;
1003
1006
  }
1004
1007
  /**
1005
- * Asserts that this.consumingContract is populated
1006
- * @throws {CofheError} If consumingContract is not set
1008
+ * Asserts that this.consumingContract is populated and is an address.
1009
+ * Runs before TFHE init, key fetching and proving, so a bad address costs
1010
+ * nothing: the contract is bound into the proof, and a proof built for a
1011
+ * non-address could only fail at the verifier, after the expensive step.
1012
+ * @throws {CofheError} If consumingContract is not set or is not an address
1007
1013
  */
1008
1014
  assertConsumingContract() {
1009
- if (this.consumingContract)
1010
- return;
1011
- throw new CofheError({
1012
- code: "CONSUMING_CONTRACT_UNINITIALIZED" /* ConsumingContractUninitialized */,
1013
- message: "Consuming contract is not set",
1014
- hint: "Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.",
1015
- context: {
1016
- consumingContract: this.consumingContract
1017
- }
1018
- });
1015
+ if (!this.consumingContract) {
1016
+ throw new CofheError({
1017
+ code: "CONSUMING_CONTRACT_UNINITIALIZED" /* ConsumingContractUninitialized */,
1018
+ message: "Consuming contract is not set",
1019
+ hint: "Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.",
1020
+ context: {
1021
+ consumingContract: this.consumingContract
1022
+ }
1023
+ });
1024
+ }
1025
+ if (!isAddress(this.consumingContract, { strict: false })) {
1026
+ throw new CofheError({
1027
+ code: "CONSUMING_CONTRACT_INVALID" /* ConsumingContractInvalid */,
1028
+ message: "Consuming contract is not an address",
1029
+ hint: "setConsumingContract(...) takes the 20-byte address of the contract that will consume the encrypted inputs.",
1030
+ context: {
1031
+ consumingContract: this.consumingContract
1032
+ }
1033
+ });
1034
+ }
1019
1035
  }
1020
1036
  setUseWorker(useWorker) {
1021
1037
  this.useWorker = useWorker;
@@ -1231,7 +1247,7 @@ var EncryptInputsBuilder = class extends BaseBuilder {
1231
1247
  zkBuilder = zkPack(this.inputItems, zkBuilder);
1232
1248
  this.fireStepEnd("pack" /* Pack */);
1233
1249
  this.fireStepStart("prove" /* Prove */);
1234
- const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId);
1250
+ const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId, this.consumingContract);
1235
1251
  let proof = null;
1236
1252
  let usedWorker = false;
1237
1253
  let workerFailedError;
@@ -17,6 +17,7 @@ var CofheErrorCode = /* @__PURE__ */ ((CofheErrorCode2) => {
17
17
  CofheErrorCode2["AccountUninitialized"] = "ACCOUNT_UNINITIALIZED";
18
18
  CofheErrorCode2["ChainIdUninitialized"] = "CHAIN_ID_UNINITIALIZED";
19
19
  CofheErrorCode2["ConsumingContractUninitialized"] = "CONSUMING_CONTRACT_UNINITIALIZED";
20
+ CofheErrorCode2["ConsumingContractInvalid"] = "CONSUMING_CONTRACT_INVALID";
20
21
  CofheErrorCode2["SealOutputFailed"] = "SEAL_OUTPUT_FAILED";
21
22
  CofheErrorCode2["SealOutputReturnedNull"] = "SEAL_OUTPUT_RETURNED_NULL";
22
23
  CofheErrorCode2["InvalidUtype"] = "INVALID_UTYPE";
@@ -1,4 +1,4 @@
1
- import { CofheError, TASK_MANAGER_ID, COFHE_ADDRESS_BOOK_ADDRESS } from './chunk-J23FYAI2.js';
1
+ import { CofheError, TASK_MANAGER_ID, COFHE_ADDRESS_BOOK_ADDRESS } from './chunk-OC5TGQGG.js';
2
2
  import { isAddress, getAddress, zeroAddress, isHex, parseAbi, keccak256, padHex, numberToHex, concatHex, toFunctionSelector, size, sliceHex, toHex, encodeAbiParameters, hexToBigInt, stringToHex, hexToNumber, toEventSignature, toFunctionSignature, decodeErrorResult, toEventSelector, decodeAbiParameters } from 'viem';
3
3
  import nacl from 'tweetnacl';
4
4
  import { z } from 'zod';
@@ -896,8 +896,11 @@ declare class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuil
896
896
  setConsumingContract(address: string): EncryptInputsBuilder<T>;
897
897
  getConsumingContract(): string | undefined;
898
898
  /**
899
- * Asserts that this.consumingContract is populated
900
- * @throws {CofheError} If consumingContract is not set
899
+ * Asserts that this.consumingContract is populated and is an address.
900
+ * Runs before TFHE init, key fetching and proving, so a bad address costs
901
+ * nothing: the contract is bound into the proof, and a proof built for a
902
+ * non-address could only fail at the verifier, after the expensive step.
903
+ * @throws {CofheError} If consumingContract is not set or is not an address
901
904
  */
902
905
  private assertConsumingContract;
903
906
  /**
@@ -896,8 +896,11 @@ declare class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuil
896
896
  setConsumingContract(address: string): EncryptInputsBuilder<T>;
897
897
  getConsumingContract(): string | undefined;
898
898
  /**
899
- * Asserts that this.consumingContract is populated
900
- * @throws {CofheError} If consumingContract is not set
899
+ * Asserts that this.consumingContract is populated and is an address.
900
+ * Runs before TFHE init, key fetching and proving, so a bad address costs
901
+ * nothing: the contract is bound into the proof, and a proof built for a
902
+ * non-address could only fail at the verifier, after the expensive step.
903
+ * @throws {CofheError} If consumingContract is not set or is not an address
901
904
  */
902
905
  private assertConsumingContract;
903
906
  /**
package/dist/core.cjs CHANGED
@@ -60,6 +60,7 @@ var CofheErrorCode = /* @__PURE__ */ ((CofheErrorCode2) => {
60
60
  CofheErrorCode2["AccountUninitialized"] = "ACCOUNT_UNINITIALIZED";
61
61
  CofheErrorCode2["ChainIdUninitialized"] = "CHAIN_ID_UNINITIALIZED";
62
62
  CofheErrorCode2["ConsumingContractUninitialized"] = "CONSUMING_CONTRACT_UNINITIALIZED";
63
+ CofheErrorCode2["ConsumingContractInvalid"] = "CONSUMING_CONTRACT_INVALID";
63
64
  CofheErrorCode2["SealOutputFailed"] = "SEAL_OUTPUT_FAILED";
64
65
  CofheErrorCode2["SealOutputReturnedNull"] = "SEAL_OUTPUT_RETURNED_NULL";
65
66
  CofheErrorCode2["InvalidUtype"] = "INVALID_UTYPE";
@@ -2129,19 +2130,22 @@ var zkProve = async (builder, crs, metadata) => {
2129
2130
  }, 0);
2130
2131
  });
2131
2132
  };
2132
- var constructZkPoKMetadata = (accountAddr, securityZone, chainId) => {
2133
+ var constructZkPoKMetadata = (accountAddr, securityZone, chainId, consumingContract) => {
2133
2134
  const accountAddrNoPrefix = accountAddr.startsWith("0x") ? accountAddr.slice(2) : accountAddr;
2134
2135
  const accountBytes = hexToBytes(accountAddrNoPrefix);
2136
+ const contractNoPrefix = consumingContract.startsWith("0x") ? consumingContract.slice(2) : consumingContract;
2137
+ const contractBytes = hexToBytes(contractNoPrefix);
2135
2138
  const chainIdBytes = new Uint8Array(32);
2136
2139
  let value = chainId;
2137
2140
  for (let i = 31; i >= 0 && value > 0; i--) {
2138
2141
  chainIdBytes[i] = value & 255;
2139
2142
  value = value >>> 8;
2140
2143
  }
2141
- const metadata = new Uint8Array(1 + accountBytes.length + 32);
2144
+ const metadata = new Uint8Array(1 + accountBytes.length + 32 + contractBytes.length);
2142
2145
  metadata[0] = securityZone;
2143
2146
  metadata.set(accountBytes, 1);
2144
2147
  metadata.set(chainIdBytes, 1 + accountBytes.length);
2148
+ metadata.set(contractBytes, 1 + accountBytes.length + 32);
2145
2149
  return metadata;
2146
2150
  };
2147
2151
  var zkVerifyBatch = async (verifierUrl, serializedBytes, address, securityZone, chainId, consumingContract) => {
@@ -3057,20 +3061,33 @@ var EncryptInputsBuilder = class extends BaseBuilder {
3057
3061
  return this.consumingContract;
3058
3062
  }
3059
3063
  /**
3060
- * Asserts that this.consumingContract is populated
3061
- * @throws {CofheError} If consumingContract is not set
3064
+ * Asserts that this.consumingContract is populated and is an address.
3065
+ * Runs before TFHE init, key fetching and proving, so a bad address costs
3066
+ * nothing: the contract is bound into the proof, and a proof built for a
3067
+ * non-address could only fail at the verifier, after the expensive step.
3068
+ * @throws {CofheError} If consumingContract is not set or is not an address
3062
3069
  */
3063
3070
  assertConsumingContract() {
3064
- if (this.consumingContract)
3065
- return;
3066
- throw new CofheError({
3067
- code: "CONSUMING_CONTRACT_UNINITIALIZED" /* ConsumingContractUninitialized */,
3068
- message: "Consuming contract is not set",
3069
- hint: "Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.",
3070
- context: {
3071
- consumingContract: this.consumingContract
3072
- }
3073
- });
3071
+ if (!this.consumingContract) {
3072
+ throw new CofheError({
3073
+ code: "CONSUMING_CONTRACT_UNINITIALIZED" /* ConsumingContractUninitialized */,
3074
+ message: "Consuming contract is not set",
3075
+ hint: "Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.",
3076
+ context: {
3077
+ consumingContract: this.consumingContract
3078
+ }
3079
+ });
3080
+ }
3081
+ if (!viem.isAddress(this.consumingContract, { strict: false })) {
3082
+ throw new CofheError({
3083
+ code: "CONSUMING_CONTRACT_INVALID" /* ConsumingContractInvalid */,
3084
+ message: "Consuming contract is not an address",
3085
+ hint: "setConsumingContract(...) takes the 20-byte address of the contract that will consume the encrypted inputs.",
3086
+ context: {
3087
+ consumingContract: this.consumingContract
3088
+ }
3089
+ });
3090
+ }
3074
3091
  }
3075
3092
  setUseWorker(useWorker) {
3076
3093
  this.useWorker = useWorker;
@@ -3286,7 +3303,7 @@ var EncryptInputsBuilder = class extends BaseBuilder {
3286
3303
  zkBuilder = zkPack(this.inputItems, zkBuilder);
3287
3304
  this.fireStepEnd("pack" /* Pack */);
3288
3305
  this.fireStepStart("prove" /* Prove */);
3289
- const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId);
3306
+ const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId, this.consumingContract);
3290
3307
  let proof = null;
3291
3308
  let usedWorker = false;
3292
3309
  let workerFailedError;
package/dist/core.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as CofheConfig, c as CofheClientParams, b as CofheClient, d as CofheClientConnectionState, F as FheTypes } from './clientTypes-IO6b5Um8.cjs';
2
- export { A as AnyExternalHash, i as CofheClientACPs, C as CofheInputConfig, h as CofheInternalConfig, K as DecryptEndpoint, a4 as DecryptForTxBuilder, a5 as DecryptForTxResult, a3 as DecryptForViewBuilder, J as DecryptPollCallbackContext, G as DecryptPollCallbackFunction, a1 as EncryptInputsBuilder, a2 as EncryptInputsBuilderUnset, M as EncryptSetStateFn, W as EncryptStep, N as EncryptStepCallbackContext, S as Encryptable, p as EncryptableAddress, j as EncryptableBool, E as EncryptableItem, B as EncryptableToExternalHashMap, o as EncryptableUint128, l as EncryptableUint16, m as EncryptableUint32, n as EncryptableUint64, k as EncryptableUint8, q as EncryptedNumber, y as ExternalAddressHash, s as ExternalBoolHash, z as ExternalHashProof, D as ExternalItemHashes, x as ExternalUint128Hash, u as ExternalUint16Hash, v as ExternalUint32Hash, w as ExternalUint64Hash, t as ExternalUint8Hash, R as FheAllUTypes, Z as FheKeyDeserializer, r as FheTypeValue, Q as FheUintUTypes, H as HashPlusProofResult, I as IStorage, $ as KeysStorage, a0 as KeysStore, L as LiteralToPrimitive, P as Primitive, T as TfheInitializer, O as TfheThreadsSetting, U as UnsealedItem, a6 as ZkBuilderAndCrsGenerator, a7 as ZkProveWorkerFunction, a8 as ZkProveWorkerRequest, a9 as ZkProveWorkerResponse, f as assertNoRenamedConfigKeys, e as createCofheConfigBase, _ as createKeysStore, Y as fetchKeys, g as getCofheConfigItem, V as isEncryptableItem, X as isLastEncryptionStep, aa as zkProveWithWorker } from './clientTypes-IO6b5Um8.cjs';
1
+ import { a as CofheConfig, c as CofheClientParams, b as CofheClient, d as CofheClientConnectionState, F as FheTypes } from './clientTypes-C2b_qub5.cjs';
2
+ export { A as AnyExternalHash, i as CofheClientACPs, C as CofheInputConfig, h as CofheInternalConfig, K as DecryptEndpoint, a4 as DecryptForTxBuilder, a5 as DecryptForTxResult, a3 as DecryptForViewBuilder, J as DecryptPollCallbackContext, G as DecryptPollCallbackFunction, a1 as EncryptInputsBuilder, a2 as EncryptInputsBuilderUnset, M as EncryptSetStateFn, W as EncryptStep, N as EncryptStepCallbackContext, S as Encryptable, p as EncryptableAddress, j as EncryptableBool, E as EncryptableItem, B as EncryptableToExternalHashMap, o as EncryptableUint128, l as EncryptableUint16, m as EncryptableUint32, n as EncryptableUint64, k as EncryptableUint8, q as EncryptedNumber, y as ExternalAddressHash, s as ExternalBoolHash, z as ExternalHashProof, D as ExternalItemHashes, x as ExternalUint128Hash, u as ExternalUint16Hash, v as ExternalUint32Hash, w as ExternalUint64Hash, t as ExternalUint8Hash, R as FheAllUTypes, Z as FheKeyDeserializer, r as FheTypeValue, Q as FheUintUTypes, H as HashPlusProofResult, I as IStorage, $ as KeysStorage, a0 as KeysStore, L as LiteralToPrimitive, P as Primitive, T as TfheInitializer, O as TfheThreadsSetting, U as UnsealedItem, a6 as ZkBuilderAndCrsGenerator, a7 as ZkProveWorkerFunction, a8 as ZkProveWorkerRequest, a9 as ZkProveWorkerResponse, f as assertNoRenamedConfigKeys, e as createCofheConfigBase, _ as createKeysStore, Y as fetchKeys, g as getCofheConfigItem, V as isEncryptableItem, X as isLastEncryptionStep, aa as zkProveWithWorker } from './clientTypes-C2b_qub5.cjs';
3
3
  import { PublicClient, Address, Hex } from 'viem';
4
4
  import './types-C07FK-cL.cjs';
5
5
  import 'zod';
@@ -55,6 +55,7 @@ declare enum CofheErrorCode {
55
55
  AccountUninitialized = "ACCOUNT_UNINITIALIZED",
56
56
  ChainIdUninitialized = "CHAIN_ID_UNINITIALIZED",
57
57
  ConsumingContractUninitialized = "CONSUMING_CONTRACT_UNINITIALIZED",
58
+ ConsumingContractInvalid = "CONSUMING_CONTRACT_INVALID",
58
59
  SealOutputFailed = "SEAL_OUTPUT_FAILED",
59
60
  SealOutputReturnedNull = "SEAL_OUTPUT_RETURNED_NULL",
60
61
  InvalidUtype = "INVALID_UTYPE",
package/dist/core.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as CofheConfig, c as CofheClientParams, b as CofheClient, d as CofheClientConnectionState, F as FheTypes } from './clientTypes-b1pOxOUE.js';
2
- export { A as AnyExternalHash, i as CofheClientACPs, C as CofheInputConfig, h as CofheInternalConfig, K as DecryptEndpoint, a4 as DecryptForTxBuilder, a5 as DecryptForTxResult, a3 as DecryptForViewBuilder, J as DecryptPollCallbackContext, G as DecryptPollCallbackFunction, a1 as EncryptInputsBuilder, a2 as EncryptInputsBuilderUnset, M as EncryptSetStateFn, W as EncryptStep, N as EncryptStepCallbackContext, S as Encryptable, p as EncryptableAddress, j as EncryptableBool, E as EncryptableItem, B as EncryptableToExternalHashMap, o as EncryptableUint128, l as EncryptableUint16, m as EncryptableUint32, n as EncryptableUint64, k as EncryptableUint8, q as EncryptedNumber, y as ExternalAddressHash, s as ExternalBoolHash, z as ExternalHashProof, D as ExternalItemHashes, x as ExternalUint128Hash, u as ExternalUint16Hash, v as ExternalUint32Hash, w as ExternalUint64Hash, t as ExternalUint8Hash, R as FheAllUTypes, Z as FheKeyDeserializer, r as FheTypeValue, Q as FheUintUTypes, H as HashPlusProofResult, I as IStorage, $ as KeysStorage, a0 as KeysStore, L as LiteralToPrimitive, P as Primitive, T as TfheInitializer, O as TfheThreadsSetting, U as UnsealedItem, a6 as ZkBuilderAndCrsGenerator, a7 as ZkProveWorkerFunction, a8 as ZkProveWorkerRequest, a9 as ZkProveWorkerResponse, f as assertNoRenamedConfigKeys, e as createCofheConfigBase, _ as createKeysStore, Y as fetchKeys, g as getCofheConfigItem, V as isEncryptableItem, X as isLastEncryptionStep, aa as zkProveWithWorker } from './clientTypes-b1pOxOUE.js';
1
+ import { a as CofheConfig, c as CofheClientParams, b as CofheClient, d as CofheClientConnectionState, F as FheTypes } from './clientTypes-DEKYh0cw.js';
2
+ export { A as AnyExternalHash, i as CofheClientACPs, C as CofheInputConfig, h as CofheInternalConfig, K as DecryptEndpoint, a4 as DecryptForTxBuilder, a5 as DecryptForTxResult, a3 as DecryptForViewBuilder, J as DecryptPollCallbackContext, G as DecryptPollCallbackFunction, a1 as EncryptInputsBuilder, a2 as EncryptInputsBuilderUnset, M as EncryptSetStateFn, W as EncryptStep, N as EncryptStepCallbackContext, S as Encryptable, p as EncryptableAddress, j as EncryptableBool, E as EncryptableItem, B as EncryptableToExternalHashMap, o as EncryptableUint128, l as EncryptableUint16, m as EncryptableUint32, n as EncryptableUint64, k as EncryptableUint8, q as EncryptedNumber, y as ExternalAddressHash, s as ExternalBoolHash, z as ExternalHashProof, D as ExternalItemHashes, x as ExternalUint128Hash, u as ExternalUint16Hash, v as ExternalUint32Hash, w as ExternalUint64Hash, t as ExternalUint8Hash, R as FheAllUTypes, Z as FheKeyDeserializer, r as FheTypeValue, Q as FheUintUTypes, H as HashPlusProofResult, I as IStorage, $ as KeysStorage, a0 as KeysStore, L as LiteralToPrimitive, P as Primitive, T as TfheInitializer, O as TfheThreadsSetting, U as UnsealedItem, a6 as ZkBuilderAndCrsGenerator, a7 as ZkProveWorkerFunction, a8 as ZkProveWorkerRequest, a9 as ZkProveWorkerResponse, f as assertNoRenamedConfigKeys, e as createCofheConfigBase, _ as createKeysStore, Y as fetchKeys, g as getCofheConfigItem, V as isEncryptableItem, X as isLastEncryptionStep, aa as zkProveWithWorker } from './clientTypes-DEKYh0cw.js';
3
3
  import { PublicClient, Address, Hex } from 'viem';
4
4
  import './types-C07FK-cL.js';
5
5
  import 'zod';
@@ -55,6 +55,7 @@ declare enum CofheErrorCode {
55
55
  AccountUninitialized = "ACCOUNT_UNINITIALIZED",
56
56
  ChainIdUninitialized = "CHAIN_ID_UNINITIALIZED",
57
57
  ConsumingContractUninitialized = "CONSUMING_CONTRACT_UNINITIALIZED",
58
+ ConsumingContractInvalid = "CONSUMING_CONTRACT_INVALID",
58
59
  SealOutputFailed = "SEAL_OUTPUT_FAILED",
59
60
  SealOutputReturnedNull = "SEAL_OUTPUT_RETURNED_NULL",
60
61
  InvalidUtype = "INVALID_UTYPE",
package/dist/core.js CHANGED
@@ -1,5 +1,5 @@
1
- export { InitialConnectStore as CONNECT_STORE_DEFAULTS, DecryptForTxBuilder, DecryptForViewBuilder, EncryptInputsBuilder, assertNoRenamedConfigKeys, cofheFetch, createCofheClientBase, createCofheConfigBase, createKeysStore, fetchKeys, getCofheConfigItem, getCofheDebugInterceptors, setCofheDebugInterceptors, verifyDecryptResult, zkProveWithWorker } from './chunk-O2ITEUEU.js';
1
+ export { InitialConnectStore as CONNECT_STORE_DEFAULTS, DecryptForTxBuilder, DecryptForViewBuilder, EncryptInputsBuilder, assertNoRenamedConfigKeys, cofheFetch, createCofheClientBase, createCofheConfigBase, createKeysStore, fetchKeys, getCofheConfigItem, getCofheDebugInterceptors, setCofheDebugInterceptors, verifyDecryptResult, zkProveWithWorker } from './chunk-GKEJHZ52.js';
2
2
  import './chunk-UKQAJK4N.js';
3
- export { getTaskManagerAddress } from './chunk-3L2IVPMI.js';
4
- export { EncryptStep, Encryptable, FheAllUTypes, FheTypes, FheUintUTypes, fheTypeToString, isEncryptableItem, isLastEncryptionStep } from './chunk-WJGTPW7K.js';
5
- export { COFHE_ADDRESS_BOOK_ADDRESS, CofheError, CofheErrorCode, MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY, MOCKS_TASK_MANAGER_ADDRESS, MOCKS_THRESHOLD_NETWORK_ADDRESS, MOCKS_ZK_VERIFIER_ADDRESS, MOCKS_ZK_VERIFIER_SIGNER_ADDRESS, MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY, TASK_MANAGER_ID, TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT, TFHE_RS_ZK_MAX_BITS, isCofheError } from './chunk-J23FYAI2.js';
3
+ export { getTaskManagerAddress } from './chunk-WAA2U7CT.js';
4
+ export { EncryptStep, Encryptable, FheAllUTypes, FheTypes, FheUintUTypes, fheTypeToString, isEncryptableItem, isLastEncryptionStep } from './chunk-3SD2UCRE.js';
5
+ export { COFHE_ADDRESS_BOOK_ADDRESS, CofheError, CofheErrorCode, MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY, MOCKS_TASK_MANAGER_ADDRESS, MOCKS_THRESHOLD_NETWORK_ADDRESS, MOCKS_ZK_VERIFIER_ADDRESS, MOCKS_ZK_VERIFIER_SIGNER_ADDRESS, MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY, TASK_MANAGER_ID, TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT, TFHE_RS_ZK_MAX_BITS, isCofheError } from './chunk-OC5TGQGG.js';
package/dist/node.cjs CHANGED
@@ -1925,19 +1925,22 @@ var zkProve = async (builder, crs, metadata) => {
1925
1925
  }, 0);
1926
1926
  });
1927
1927
  };
1928
- var constructZkPoKMetadata = (accountAddr, securityZone, chainId) => {
1928
+ var constructZkPoKMetadata = (accountAddr, securityZone, chainId, consumingContract) => {
1929
1929
  const accountAddrNoPrefix = accountAddr.startsWith("0x") ? accountAddr.slice(2) : accountAddr;
1930
1930
  const accountBytes = hexToBytes(accountAddrNoPrefix);
1931
+ const contractNoPrefix = consumingContract.startsWith("0x") ? consumingContract.slice(2) : consumingContract;
1932
+ const contractBytes = hexToBytes(contractNoPrefix);
1931
1933
  const chainIdBytes = new Uint8Array(32);
1932
1934
  let value = chainId;
1933
1935
  for (let i = 31; i >= 0 && value > 0; i--) {
1934
1936
  chainIdBytes[i] = value & 255;
1935
1937
  value = value >>> 8;
1936
1938
  }
1937
- const metadata = new Uint8Array(1 + accountBytes.length + 32);
1939
+ const metadata = new Uint8Array(1 + accountBytes.length + 32 + contractBytes.length);
1938
1940
  metadata[0] = securityZone;
1939
1941
  metadata.set(accountBytes, 1);
1940
1942
  metadata.set(chainIdBytes, 1 + accountBytes.length);
1943
+ metadata.set(contractBytes, 1 + accountBytes.length + 32);
1941
1944
  return metadata;
1942
1945
  };
1943
1946
  var zkVerifyBatch = async (verifierUrl, serializedBytes, address, securityZone, chainId, consumingContract) => {
@@ -2850,20 +2853,33 @@ var EncryptInputsBuilder = class extends BaseBuilder {
2850
2853
  return this.consumingContract;
2851
2854
  }
2852
2855
  /**
2853
- * Asserts that this.consumingContract is populated
2854
- * @throws {CofheError} If consumingContract is not set
2856
+ * Asserts that this.consumingContract is populated and is an address.
2857
+ * Runs before TFHE init, key fetching and proving, so a bad address costs
2858
+ * nothing: the contract is bound into the proof, and a proof built for a
2859
+ * non-address could only fail at the verifier, after the expensive step.
2860
+ * @throws {CofheError} If consumingContract is not set or is not an address
2855
2861
  */
2856
2862
  assertConsumingContract() {
2857
- if (this.consumingContract)
2858
- return;
2859
- throw new CofheError({
2860
- code: "CONSUMING_CONTRACT_UNINITIALIZED" /* ConsumingContractUninitialized */,
2861
- message: "Consuming contract is not set",
2862
- hint: "Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.",
2863
- context: {
2864
- consumingContract: this.consumingContract
2865
- }
2866
- });
2863
+ if (!this.consumingContract) {
2864
+ throw new CofheError({
2865
+ code: "CONSUMING_CONTRACT_UNINITIALIZED" /* ConsumingContractUninitialized */,
2866
+ message: "Consuming contract is not set",
2867
+ hint: "Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.",
2868
+ context: {
2869
+ consumingContract: this.consumingContract
2870
+ }
2871
+ });
2872
+ }
2873
+ if (!viem.isAddress(this.consumingContract, { strict: false })) {
2874
+ throw new CofheError({
2875
+ code: "CONSUMING_CONTRACT_INVALID" /* ConsumingContractInvalid */,
2876
+ message: "Consuming contract is not an address",
2877
+ hint: "setConsumingContract(...) takes the 20-byte address of the contract that will consume the encrypted inputs.",
2878
+ context: {
2879
+ consumingContract: this.consumingContract
2880
+ }
2881
+ });
2882
+ }
2867
2883
  }
2868
2884
  setUseWorker(useWorker) {
2869
2885
  this.useWorker = useWorker;
@@ -3079,7 +3095,7 @@ var EncryptInputsBuilder = class extends BaseBuilder {
3079
3095
  zkBuilder = zkPack(this.inputItems, zkBuilder);
3080
3096
  this.fireStepEnd("pack" /* Pack */);
3081
3097
  this.fireStepStart("prove" /* Prove */);
3082
- const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId);
3098
+ const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId, this.consumingContract);
3083
3099
  let proof = null;
3084
3100
  let usedWorker = false;
3085
3101
  let workerFailedError;
package/dist/node.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as CofheInputConfig, a as CofheConfig, b as CofheClient } from './clientTypes-IO6b5Um8.cjs';
1
+ import { C as CofheInputConfig, a as CofheConfig, b as CofheClient } from './clientTypes-C2b_qub5.cjs';
2
2
  import 'viem';
3
3
  import './types-C07FK-cL.cjs';
4
4
  import 'zod';
package/dist/node.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as CofheInputConfig, a as CofheConfig, b as CofheClient } from './clientTypes-b1pOxOUE.js';
1
+ import { C as CofheInputConfig, a as CofheConfig, b as CofheClient } from './clientTypes-DEKYh0cw.js';
2
2
  import 'viem';
3
3
  import './types-C07FK-cL.js';
4
4
  import 'zod';
package/dist/node.js CHANGED
@@ -1,8 +1,8 @@
1
- import { createCofheConfigBase, createCofheClientBase } from './chunk-O2ITEUEU.js';
1
+ import { createCofheConfigBase, createCofheClientBase } from './chunk-GKEJHZ52.js';
2
2
  import './chunk-UKQAJK4N.js';
3
- import './chunk-3L2IVPMI.js';
4
- import { fromHexString } from './chunk-WJGTPW7K.js';
5
- import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-J23FYAI2.js';
3
+ import './chunk-WAA2U7CT.js';
4
+ import { fromHexString } from './chunk-3SD2UCRE.js';
5
+ import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-OC5TGQGG.js';
6
6
  import { promises } from 'fs';
7
7
  import { join } from 'path';
8
8
  import { init_panic_hook, ProvenCompactCiphertextList, CompactPkeCrs, TfheCompactPublicKey } from 'node-tfhe';
package/dist/web.cjs CHANGED
@@ -1960,19 +1960,22 @@ var zkProve = async (builder, crs, metadata) => {
1960
1960
  }, 0);
1961
1961
  });
1962
1962
  };
1963
- var constructZkPoKMetadata = (accountAddr, securityZone, chainId) => {
1963
+ var constructZkPoKMetadata = (accountAddr, securityZone, chainId, consumingContract) => {
1964
1964
  const accountAddrNoPrefix = accountAddr.startsWith("0x") ? accountAddr.slice(2) : accountAddr;
1965
1965
  const accountBytes = hexToBytes(accountAddrNoPrefix);
1966
+ const contractNoPrefix = consumingContract.startsWith("0x") ? consumingContract.slice(2) : consumingContract;
1967
+ const contractBytes = hexToBytes(contractNoPrefix);
1966
1968
  const chainIdBytes = new Uint8Array(32);
1967
1969
  let value = chainId;
1968
1970
  for (let i = 31; i >= 0 && value > 0; i--) {
1969
1971
  chainIdBytes[i] = value & 255;
1970
1972
  value = value >>> 8;
1971
1973
  }
1972
- const metadata = new Uint8Array(1 + accountBytes.length + 32);
1974
+ const metadata = new Uint8Array(1 + accountBytes.length + 32 + contractBytes.length);
1973
1975
  metadata[0] = securityZone;
1974
1976
  metadata.set(accountBytes, 1);
1975
1977
  metadata.set(chainIdBytes, 1 + accountBytes.length);
1978
+ metadata.set(contractBytes, 1 + accountBytes.length + 32);
1976
1979
  return metadata;
1977
1980
  };
1978
1981
  var zkVerifyBatch = async (verifierUrl, serializedBytes, address, securityZone, chainId, consumingContract) => {
@@ -2885,20 +2888,33 @@ var EncryptInputsBuilder = class extends BaseBuilder {
2885
2888
  return this.consumingContract;
2886
2889
  }
2887
2890
  /**
2888
- * Asserts that this.consumingContract is populated
2889
- * @throws {CofheError} If consumingContract is not set
2891
+ * Asserts that this.consumingContract is populated and is an address.
2892
+ * Runs before TFHE init, key fetching and proving, so a bad address costs
2893
+ * nothing: the contract is bound into the proof, and a proof built for a
2894
+ * non-address could only fail at the verifier, after the expensive step.
2895
+ * @throws {CofheError} If consumingContract is not set or is not an address
2890
2896
  */
2891
2897
  assertConsumingContract() {
2892
- if (this.consumingContract)
2893
- return;
2894
- throw new CofheError({
2895
- code: "CONSUMING_CONTRACT_UNINITIALIZED" /* ConsumingContractUninitialized */,
2896
- message: "Consuming contract is not set",
2897
- hint: "Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.",
2898
- context: {
2899
- consumingContract: this.consumingContract
2900
- }
2901
- });
2898
+ if (!this.consumingContract) {
2899
+ throw new CofheError({
2900
+ code: "CONSUMING_CONTRACT_UNINITIALIZED" /* ConsumingContractUninitialized */,
2901
+ message: "Consuming contract is not set",
2902
+ hint: "Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.",
2903
+ context: {
2904
+ consumingContract: this.consumingContract
2905
+ }
2906
+ });
2907
+ }
2908
+ if (!viem.isAddress(this.consumingContract, { strict: false })) {
2909
+ throw new CofheError({
2910
+ code: "CONSUMING_CONTRACT_INVALID" /* ConsumingContractInvalid */,
2911
+ message: "Consuming contract is not an address",
2912
+ hint: "setConsumingContract(...) takes the 20-byte address of the contract that will consume the encrypted inputs.",
2913
+ context: {
2914
+ consumingContract: this.consumingContract
2915
+ }
2916
+ });
2917
+ }
2902
2918
  }
2903
2919
  setUseWorker(useWorker) {
2904
2920
  this.useWorker = useWorker;
@@ -3114,7 +3130,7 @@ var EncryptInputsBuilder = class extends BaseBuilder {
3114
3130
  zkBuilder = zkPack(this.inputItems, zkBuilder);
3115
3131
  this.fireStepEnd("pack" /* Pack */);
3116
3132
  this.fireStepStart("prove" /* Prove */);
3117
- const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId);
3133
+ const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId, this.consumingContract);
3118
3134
  let proof = null;
3119
3135
  let usedWorker = false;
3120
3136
  let workerFailedError;
package/dist/web.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { I as IStorage, C as CofheInputConfig, a as CofheConfig, b as CofheClient, E as EncryptableItem } from './clientTypes-IO6b5Um8.cjs';
1
+ import { I as IStorage, C as CofheInputConfig, a as CofheConfig, b as CofheClient, E as EncryptableItem } from './clientTypes-C2b_qub5.cjs';
2
2
  import 'viem';
3
3
  import './types-C07FK-cL.cjs';
4
4
  import 'zod';
package/dist/web.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { I as IStorage, C as CofheInputConfig, a as CofheConfig, b as CofheClient, E as EncryptableItem } from './clientTypes-b1pOxOUE.js';
1
+ import { I as IStorage, C as CofheInputConfig, a as CofheConfig, b as CofheClient, E as EncryptableItem } from './clientTypes-DEKYh0cw.js';
2
2
  import 'viem';
3
3
  import './types-C07FK-cL.js';
4
4
  import 'zod';
package/dist/web.js CHANGED
@@ -1,9 +1,9 @@
1
- import { createCofheConfigBase, createCofheClientBase } from './chunk-O2ITEUEU.js';
1
+ import { createCofheConfigBase, createCofheClientBase } from './chunk-GKEJHZ52.js';
2
2
  import './chunk-UKQAJK4N.js';
3
- import './chunk-3L2IVPMI.js';
3
+ import './chunk-WAA2U7CT.js';
4
4
  import { initTfheThreadPool } from './chunk-KSOV5B4B.js';
5
- import { fheTypeToString, fromHexString } from './chunk-WJGTPW7K.js';
6
- import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-J23FYAI2.js';
5
+ import { fheTypeToString, fromHexString } from './chunk-3SD2UCRE.js';
6
+ import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-OC5TGQGG.js';
7
7
  import { constructClient } from 'iframe-shared-storage';
8
8
 
9
9
  // web/const.ts
@@ -1,6 +1,6 @@
1
1
  import { initTfheThreadPool } from './chunk-KSOV5B4B.js';
2
- import { fromHexString } from './chunk-WJGTPW7K.js';
3
- import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-J23FYAI2.js';
2
+ import { fromHexString } from './chunk-3SD2UCRE.js';
3
+ import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-OC5TGQGG.js';
4
4
 
5
5
  // web/zkProve.worker.ts
6
6
  var tfheModule = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cofhe/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "description": "SDK for Fhenix COFHE coprocessor interaction",
6
6
  "main": "./dist/core.cjs",
@@ -102,8 +102,8 @@
102
102
  "typescript": "5.5.4",
103
103
  "vitest": "3.2.4",
104
104
  "@cofhe/eslint-config": "0.2.1",
105
- "@cofhe/tsconfig": "0.1.2",
106
- "@cofhe/test-setup": "0.0.0"
105
+ "@cofhe/test-setup": "0.0.0",
106
+ "@cofhe/tsconfig": "0.1.2"
107
107
  },
108
108
  "publishConfig": {
109
109
  "access": "public",