@cofhe/sdk 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/adapters/{ethers5.test.ts → test/ethers5.test.ts} +2 -2
  3. package/adapters/{ethers6.test.ts → test/ethers6.test.ts} +2 -2
  4. package/adapters/{hardhat.hh2.test.ts → test/hardhat.hh2.test.ts} +2 -2
  5. package/adapters/{index.test.ts → test/index.test.ts} +1 -1
  6. package/adapters/{wagmi.test.ts → test/wagmi.test.ts} +1 -1
  7. package/chains/{chains.test.ts → test/chains.test.ts} +1 -1
  8. package/core/client.ts +11 -1
  9. package/core/clientTypes.ts +3 -1
  10. package/core/consts.ts +9 -0
  11. package/core/decrypt/cofheMocksDecryptForTx.ts +14 -3
  12. package/core/decrypt/decryptForTxBuilder.ts +16 -2
  13. package/core/decrypt/decryptForViewBuilder.ts +14 -7
  14. package/core/decrypt/polling.ts +14 -0
  15. package/core/decrypt/tnDecryptV2.ts +250 -110
  16. package/core/decrypt/tnSealOutputV2.ts +245 -104
  17. package/core/decrypt/verifyDecryptResult.ts +65 -0
  18. package/core/encrypt/cofheMocksZkVerifySign.ts +6 -6
  19. package/core/encrypt/zkPackProveVerify.ts +10 -19
  20. package/core/fetchKeys.ts +0 -2
  21. package/core/index.ts +9 -1
  22. package/core/keyStore.ts +5 -2
  23. package/core/permits.ts +5 -0
  24. package/core/{client.test.ts → test/client.test.ts} +7 -7
  25. package/core/{config.test.ts → test/config.test.ts} +1 -1
  26. package/core/test/decrypt.test.ts +252 -0
  27. package/core/test/decryptBuilders.test.ts +390 -0
  28. package/core/{encrypt → test}/encryptInputsBuilder.test.ts +61 -6
  29. package/core/{fetchKeys.test.ts → test/fetchKeys.test.ts} +3 -3
  30. package/core/{keyStore.test.ts → test/keyStore.test.ts} +5 -3
  31. package/core/{permits.test.ts → test/permits.test.ts} +42 -1
  32. package/core/test/pollCallbacks.test.ts +563 -0
  33. package/core/types.ts +13 -0
  34. package/dist/chains.d.cts +2 -2
  35. package/dist/chains.d.ts +2 -2
  36. package/dist/chunk-4FP4V35O.js +13 -0
  37. package/dist/{chunk-NWDKXBIP.js → chunk-MRCKUMOS.js} +62 -22
  38. package/dist/{chunk-MXND5SVN.js → chunk-S7OKGLFD.js} +485 -207
  39. package/dist/{clientTypes-kkrRdawm.d.ts → clientTypes-BSbwairE.d.cts} +23 -6
  40. package/dist/{clientTypes-ACVWbrXL.d.cts → clientTypes-DDmcgZ0a.d.ts} +23 -6
  41. package/dist/core.cjs +561 -244
  42. package/dist/core.d.cts +24 -6
  43. package/dist/core.d.ts +24 -6
  44. package/dist/core.js +3 -2
  45. package/dist/node.cjs +566 -246
  46. package/dist/node.d.cts +3 -3
  47. package/dist/node.d.ts +3 -3
  48. package/dist/node.js +14 -7
  49. package/dist/{permit-MZ502UBl.d.cts → permit-DnVMDT5h.d.cts} +34 -4
  50. package/dist/{permit-MZ502UBl.d.ts → permit-DnVMDT5h.d.ts} +34 -4
  51. package/dist/permits.cjs +66 -29
  52. package/dist/permits.d.cts +18 -13
  53. package/dist/permits.d.ts +18 -13
  54. package/dist/permits.js +2 -1
  55. package/dist/web.cjs +604 -256
  56. package/dist/web.d.cts +8 -4
  57. package/dist/web.d.ts +8 -4
  58. package/dist/web.js +49 -14
  59. package/dist/zkProve.worker.cjs +72 -64
  60. package/dist/zkProve.worker.js +71 -64
  61. package/node/index.ts +13 -4
  62. package/node/test/client.test.ts +25 -0
  63. package/node/test/config.test.ts +16 -0
  64. package/node/test/inherited.test.ts +244 -0
  65. package/node/test/tfheinit.test.ts +56 -0
  66. package/package.json +24 -22
  67. package/permits/permit.ts +31 -5
  68. package/permits/sealing.ts +1 -1
  69. package/permits/{localstorage.test.ts → test/localstorage.test.ts} +2 -2
  70. package/permits/{permit.test.ts → test/permit.test.ts} +35 -1
  71. package/permits/{sealing.test.ts → test/sealing.test.ts} +1 -1
  72. package/permits/{store.test.ts → test/store.test.ts} +2 -2
  73. package/permits/{validation.test.ts → test/validation.test.ts} +82 -6
  74. package/permits/types.ts +1 -1
  75. package/permits/validation.ts +42 -2
  76. package/web/const.ts +2 -0
  77. package/web/index.ts +40 -11
  78. package/web/storage.ts +18 -3
  79. package/web/{client.web.test.ts → test/client.web.test.ts} +13 -1
  80. package/web/test/config.web.test.ts +16 -0
  81. package/web/test/inherited.web.test.ts +245 -0
  82. package/web/test/tfheinit.web.test.ts +62 -0
  83. package/web/{worker.config.web.test.ts → test/worker.config.web.test.ts} +1 -1
  84. package/web/{worker.output.web.test.ts → test/worker.output.web.test.ts} +1 -1
  85. package/web/{workerManager.test.ts → test/workerManager.test.ts} +1 -1
  86. package/web/{workerManager.web.test.ts → test/workerManager.web.test.ts} +1 -1
  87. package/web/zkProve.worker.ts +94 -84
  88. package/node/client.test.ts +0 -147
  89. package/node/config.test.ts +0 -68
  90. package/node/encryptInputs.test.ts +0 -155
  91. package/web/config.web.test.ts +0 -69
  92. package/web/encryptInputs.web.test.ts +0 -172
  93. package/web/worker.builder.web.test.ts +0 -148
  94. /package/dist/{types-YiAC4gig.d.cts → types-C07FK-cL.d.cts} +0 -0
  95. /package/dist/{types-YiAC4gig.d.ts → types-C07FK-cL.d.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @cofhe/sdk Changelog
2
2
 
3
+ ## 0.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 342fd0f: Fix SSR compatibility (`@cofhe/sdk/web` no longer crashes Next.js builds with `self is not defined`) by lazy-loading `tfhe`. Align `@cofhe/mock-contracts` with `@fhenixprotocol/cofhe-contracts@^0.1.3` (updated `TestBed.sol` to use current decrypt API, added missing `ITaskManager` batch methods to `MockTaskManager.sol`).
8
+
9
+ ## 0.5.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 788a6e2: Add `onPoll` callback support for decrypt polling (tx + view) so consumers can observe poll progress.
14
+
15
+ - SDK decrypt helpers accept `onPoll` and emit `{ operation, requestId, attemptIndex, elapsedMs, intervalMs, timeoutMs }` once per poll attempt.
16
+ - React wiring supports passing the callback end-to-end.
17
+ - Docs updated with usage examples.
18
+
19
+ - 9a06012: Tighten permit validation and treat invalid permits as missing.
20
+
21
+ - SDK: `PermitUtils.validate` now enforces schema + signed + not-expired (use `PermitUtils.validateSchema` for schema-only validation).
22
+ - SDK: `ValidationResult.error` is now a typed union (`'invalid-schema' | 'expired' | 'not-signed' | null`).
23
+ - React: rename `disabledDueToMissingPermit` to `disabledDueToMissingValidPermit` in read/decrypt hooks and token balance helpers, and disable reads when the active permit is invalid.
24
+
25
+ ### Patch Changes
26
+
27
+ - 6c4084f: Add submit retries to the threshold-network decrypt flows used by both `decryptForTx` and `decryptForView` when the backend responds with `204 No Content` before a `request_id` is available.
28
+
29
+ - When the submit endpoint returns `204` without a body, the SDK now retries until it receives a `request_id` or the existing poll timeout budget is exhausted.
30
+ - These submit retries now emit `onPoll` callbacks, so consumers can observe retry progress before a request id exists.
31
+ - Submit retries and status polling now share the same overall timeout budget.
32
+
33
+ - 503536a: Improve logging ergonomics across React + web SDK.
34
+
35
+ - Add a configurable internal logger to `@cofhe/react` via `createCofheConfig({ react: { logger } })`.
36
+ - Make `@cofhe/sdk` `createWebStorage` logging opt-in via `createWebStorage({ enableLog })`.
37
+
38
+ - a685cd4: **Breaking change: upgraded to tfhe v1.5.3.**
39
+ Previous cofhesdk versions will no longer function.
40
+
3
41
  ## 0.4.0
4
42
 
5
43
  ### Patch Changes
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import { parseEther } from 'viem';
3
- import { Ethers5Adapter } from './ethers5.js';
4
- import { createMockEIP1193Provider } from './test-utils.js';
3
+ import { Ethers5Adapter } from '../ethers5.js';
4
+ import { createMockEIP1193Provider } from '../test-utils.js';
5
5
  import * as ethers5 from 'ethers5';
6
6
 
7
7
  describe('Ethers5Adapter', () => {
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import { parseEther } from 'viem';
3
- import { Ethers6Adapter } from './ethers6.js';
4
- import { createMockEIP1193Provider } from './test-utils.js';
3
+ import { Ethers6Adapter } from '../ethers6.js';
4
+ import { createMockEIP1193Provider } from '../test-utils.js';
5
5
  import * as ethers6 from 'ethers6';
6
6
 
7
7
  describe('Ethers6Adapter', () => {
@@ -1,11 +1,11 @@
1
1
  import { describe, it, expect, beforeEach, beforeAll, afterAll } from 'vitest';
2
2
  import { parseEther } from 'viem';
3
3
  import { hardhat } from 'viem/chains';
4
- import { HardhatSignerAdapter } from './hardhat.js';
4
+ import { HardhatSignerAdapter } from '../hardhat.js';
5
5
  import hre from 'hardhat';
6
6
  import '@nomicfoundation/hardhat-ethers';
7
7
  import type { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers.js';
8
- import { hardhatNode } from './hardhat-node.js';
8
+ import { hardhatNode } from '../hardhat-node.js';
9
9
 
10
10
  describe('HardhatSignerAdapter', () => {
11
11
  const HARDHAT_CHAIN_ID = 31337; // Hardhat local network
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import * as adapters from './index.js';
2
+ import * as adapters from '../index.js';
3
3
 
4
4
  describe('Index Exports', () => {
5
5
  it('should export main adapter functions', () => {
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import { parseEther, createPublicClient, createWalletClient, http, type PublicClient, type WalletClient } from 'viem';
3
3
  import { privateKeyToAccount } from 'viem/accounts';
4
- import { WagmiAdapter } from './wagmi.js';
4
+ import { WagmiAdapter } from '../wagmi.js';
5
5
 
6
6
  describe('WagmiAdapter', () => {
7
7
  const testRpcUrl = 'https://ethereum-sepolia.rpc.subquery.network/public';
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { sepolia, arbSepolia, baseSepolia, hardhat, chains, getChainById, getChainByName } from './index.js';
2
+ import { sepolia, arbSepolia, baseSepolia, hardhat, chains, getChainById, getChainByName } from '../index.js';
3
3
 
4
4
  describe('Chains', () => {
5
5
  it('should export all chains', () => {
package/core/client.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  import type { CreateSelfPermitOptions, CreateSharingPermitOptions, ImportSharedPermitOptions } from '@/permits';
2
2
 
3
3
  import { createStore } from 'zustand/vanilla';
4
- import { type PublicClient, type WalletClient } from 'viem';
4
+ import { type Hex, type PublicClient, type WalletClient } from 'viem';
5
5
  import { CofheError, CofheErrorCode } from './error.js';
6
6
  import { EncryptInputsBuilder } from './encrypt/encryptInputsBuilder.js';
7
7
  import { createKeysStore } from './keyStore.js';
8
8
  import { permits } from './permits.js';
9
9
  import { DecryptForViewBuilder } from './decrypt/decryptForViewBuilder.js';
10
10
  import { DecryptForTxBuilder, type DecryptForTxBuilderUnset } from './decrypt/decryptForTxBuilder.js';
11
+ import { verifyDecryptResult as verifyDecryptResultStandalone } from './decrypt/verifyDecryptResult.js';
11
12
  import { getPublicClientChainID, getWalletClientAccount } from './utils.js';
12
13
  import type { CofheClientConnectionState, CofheClientParams, CofheClient, CofheClientPermits } from './clientTypes.js';
13
14
  import type { EncryptableItem, FheTypes } from './types.js';
@@ -179,6 +180,13 @@ export function createCofheClientBase<TConfig extends CofheConfig>(
179
180
  });
180
181
  }
181
182
 
183
+ // VERIFY DECRYPT RESULT
184
+ function verifyDecryptResult(handle: bigint | string, cleartext: bigint, signature: Hex): Promise<boolean> {
185
+ _requireConnected();
186
+ const { publicClient } = connectStore.getState();
187
+ return verifyDecryptResultStandalone(handle, cleartext, signature, publicClient!);
188
+ }
189
+
182
190
  // PERMITS - Context-aware wrapper
183
191
 
184
192
  const _getChainIdAndAccount = (chainId?: number, account?: string) => {
@@ -288,6 +296,7 @@ export function createCofheClientBase<TConfig extends CofheConfig>(
288
296
 
289
297
  // Utils (no context needed)
290
298
  getHash: permits.getHash,
299
+ export: permits.export,
291
300
  serialize: permits.serialize,
292
301
  deserialize: permits.deserialize,
293
302
  };
@@ -320,6 +329,7 @@ export function createCofheClientBase<TConfig extends CofheConfig>(
320
329
  */
321
330
  decryptHandle: decryptForView,
322
331
  decryptForTx,
332
+ verifyDecryptResult,
323
333
  permits: clientPermits,
324
334
 
325
335
  // Add SDK-specific methods below that require connection
@@ -1,5 +1,5 @@
1
1
  // TODO: Extract client types to its own file, keep this one as primitives
2
- import { type PublicClient, type WalletClient } from 'viem';
2
+ import { type Hex, type PublicClient, type WalletClient } from 'viem';
3
3
  import { type CofheConfig } from './config.js';
4
4
  import { type DecryptForViewBuilder } from './decrypt/decryptForViewBuilder.js';
5
5
  import { type DecryptForTxBuilderUnset } from './decrypt/decryptForTxBuilder.js';
@@ -51,6 +51,7 @@ export type CofheClient<TConfig extends CofheConfig = CofheConfig> = {
51
51
  decryptHandle<U extends FheTypes>(ctHash: bigint | string, utype: U): DecryptForViewBuilder<U>;
52
52
  decryptForView<U extends FheTypes>(ctHash: bigint | string, utype: U): DecryptForViewBuilder<U>;
53
53
  decryptForTx(ctHash: bigint | string): DecryptForTxBuilderUnset;
54
+ verifyDecryptResult(handle: bigint | string, cleartext: bigint, signature: Hex): Promise<boolean>;
54
55
  permits: CofheClientPermits;
55
56
  };
56
57
 
@@ -104,6 +105,7 @@ export type CofheClientPermits = {
104
105
 
105
106
  // Utils
106
107
  getHash: typeof PermitUtils.getHash;
108
+ export: typeof PermitUtils.export;
107
109
  serialize: typeof PermitUtils.serialize;
108
110
  deserialize: typeof PermitUtils.deserialize;
109
111
  };
package/core/consts.ts CHANGED
@@ -20,3 +20,12 @@ export const MOCKS_ZK_VERIFIER_SIGNER_ADDRESS = '0x6E12D8C87503D4287c294f2Fdef96
20
20
  /** Private key for the Mock decrypt result signer account */
21
21
  export const MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY =
22
22
  '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d' as const;
23
+
24
+ /** Maximum total bits for ZK proof packing */
25
+ export const TFHE_RS_ZK_MAX_BITS = 2048 as const;
26
+
27
+ /** Size limit for safe_serialize/safe_deserialize (1 GB) */
28
+ export const TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT = BigInt(1 << 30);
29
+
30
+ /** TFHE.rs key version (invalidates cached keys) */
31
+ export const TFHE_RS_KEY_VERSION = 2;
@@ -8,6 +8,11 @@ import { CofheError, CofheErrorCode } from '../error.js';
8
8
  import { MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY } from '../consts.js';
9
9
  import { MOCKS_THRESHOLD_NETWORK_ADDRESS } from '../consts.js';
10
10
 
11
+ const UINT_TYPE_MASK = 0x7fn;
12
+ const TYPE_BYTE_OFFSET = 8n;
13
+
14
+ const getEncryptionTypeFromCtHash = (ctHash: bigint) => Number((ctHash >> TYPE_BYTE_OFFSET) & UINT_TYPE_MASK);
15
+
11
16
  export type DecryptForTxMocksResult = {
12
17
  ctHash: bigint | string;
13
18
  decryptedValue: bigint;
@@ -64,9 +69,15 @@ export async function cofheMocksDecryptForTx(
64
69
  }
65
70
 
66
71
  // decryptForTx returns plaintext directly (no sealing/unsealing needed)
67
- // Generate a mock threshold network signature (in production, this would be the actual signature)
68
- // The signature must be valid for MockTaskManager verification.
69
- const packed = encodePacked(['uint256', 'uint256'], [BigInt(ctHash), decryptedValue]);
72
+ // Generate a mock threshold network signature using the same payload format
73
+ // that TaskManager expects in production and in mocks.
74
+ const chainId = publicClient.chain?.id ?? (await publicClient.getChainId());
75
+ const normalizedCtHash = BigInt(ctHash);
76
+ const encryptionType = getEncryptionTypeFromCtHash(normalizedCtHash);
77
+ const packed = encodePacked(
78
+ ['uint256', 'uint32', 'uint64', 'uint256'],
79
+ [decryptedValue, encryptionType, BigInt(chainId), normalizedCtHash]
80
+ );
70
81
  const messageHash = keccak256(packed);
71
82
 
72
83
  // Raw digest signature (no EIP-191 prefix). Must verify against OpenZeppelin ECDSA.recover(messageHash, signature).
@@ -9,6 +9,7 @@ import { permits } from '../permits';
9
9
  import { BaseBuilder, type BaseBuilderParams } from '../baseBuilder';
10
10
  import { cofheMocksDecryptForTx } from './cofheMocksDecryptForTx';
11
11
  import { getPublicClientChainID, sleep } from '../utils';
12
+ import { type DecryptPollCallbackFunction } from '../types';
12
13
  import { tnDecryptV2 } from './tnDecryptV2';
13
14
 
14
15
  /**
@@ -60,6 +61,7 @@ export class DecryptForTxBuilder extends BaseBuilder {
60
61
  private permitHash?: string;
61
62
  private permit?: Permit;
62
63
  private permitSelection: DecryptForTxPermitSelection = 'unset';
64
+ private pollCallback?: DecryptPollCallbackFunction;
63
65
 
64
66
  constructor(params: DecryptForTxBuilderParams) {
65
67
  super({
@@ -124,6 +126,13 @@ export class DecryptForTxBuilder extends BaseBuilder {
124
126
  return this.account;
125
127
  }
126
128
 
129
+ onPoll(this: DecryptForTxBuilderUnset, callback: DecryptPollCallbackFunction): DecryptForTxBuilderUnset;
130
+ onPoll(this: DecryptForTxBuilderSelected, callback: DecryptPollCallbackFunction): DecryptForTxBuilderSelected;
131
+ onPoll(callback: DecryptPollCallbackFunction): DecryptForTxBuilder {
132
+ this.pollCallback = callback;
133
+ return this;
134
+ }
135
+
127
136
  /**
128
137
  * Select "use permit" mode.
129
138
  *
@@ -291,7 +300,13 @@ export class DecryptForTxBuilder extends BaseBuilder {
291
300
  const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
292
301
 
293
302
  const permission = permit ? PermitUtils.getPermission(permit, true) : null;
294
- const { decryptedValue, signature } = await tnDecryptV2(this.ctHash, this.chainId, permission, thresholdNetworkUrl);
303
+ const { decryptedValue, signature } = await tnDecryptV2({
304
+ ctHash: this.ctHash,
305
+ chainId: this.chainId,
306
+ permission,
307
+ thresholdNetworkUrl,
308
+ onPoll: this.pollCallback,
309
+ });
295
310
 
296
311
  return {
297
312
  ctHash: this.ctHash,
@@ -315,7 +330,6 @@ export class DecryptForTxBuilder extends BaseBuilder {
315
330
  if (permit !== null) {
316
331
  // Ensure permit validity
317
332
  PermitUtils.validate(permit);
318
- PermitUtils.isValid(permit);
319
333
 
320
334
  // Extract chainId from signed permit
321
335
  const chainId = permit._signedDomain!.chainId;
@@ -12,6 +12,7 @@ import { cofheMocksDecryptForView } from './cofheMocksDecryptForView.js';
12
12
  // import { tnSealOutputV1 } from './tnSealOutputV1.js';
13
13
  import { tnSealOutputV2 } from './tnSealOutputV2.js';
14
14
  import { sleep } from '../utils.js';
15
+ import { type DecryptPollCallbackFunction } from '../types.js';
15
16
 
16
17
  /**
17
18
  * API
@@ -46,6 +47,7 @@ export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
46
47
  private utype: U;
47
48
  private permitHash?: string;
48
49
  private permit?: Permit;
50
+ private pollCallback?: DecryptPollCallbackFunction;
49
51
 
50
52
  constructor(params: DecryptForViewBuilderParams<U>) {
51
53
  super({
@@ -109,6 +111,11 @@ export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
109
111
  return this.account;
110
112
  }
111
113
 
114
+ onPoll(callback: DecryptPollCallbackFunction): DecryptForViewBuilder<U> {
115
+ this.pollCallback = callback;
116
+ return this;
117
+ }
118
+
112
119
  /**
113
120
  * Select "use permit" mode (optional).
114
121
  *
@@ -264,7 +271,13 @@ export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
264
271
  const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
265
272
  const permission = PermitUtils.getPermission(permit, true);
266
273
  // const sealed = await tnSealOutputV1(this.ctHash, this.chainId, permission, thresholdNetworkUrl);
267
- const sealed = await tnSealOutputV2(this.ctHash, this.chainId, permission, thresholdNetworkUrl);
274
+ const sealed = await tnSealOutputV2({
275
+ ctHash: this.ctHash,
276
+ chainId: this.chainId,
277
+ permission,
278
+ thresholdNetworkUrl,
279
+ onPoll: this.pollCallback,
280
+ });
268
281
  return PermitUtils.unseal(permit, sealed);
269
282
  }
270
283
 
@@ -297,14 +310,8 @@ export class DecryptForViewBuilder<U extends FheTypes> extends BaseBuilder {
297
310
  const permit = await this.getResolvedPermit();
298
311
 
299
312
  // Ensure permit validity
300
- // TODO: This doesn't validate permit expiration
301
- // TODO: This doesn't throw, returns a validation result instead
302
313
  PermitUtils.validate(permit);
303
314
 
304
- // TODO: Add this further validation step for the permit
305
- // TODO: Ensure this throws if the permit is invalid
306
- PermitUtils.isValid(permit);
307
-
308
315
  // Extract chainId from signed permit
309
316
  // Use this chainId to fetch the threshold network URL since this.chainId may be undefined
310
317
  const chainId = permit._signedDomain!.chainId;
@@ -0,0 +1,14 @@
1
+ export function computeMinuteRampPollIntervalMs(
2
+ elapsedMs: number,
3
+ params: {
4
+ minIntervalMs: number;
5
+ maxIntervalMs: number;
6
+ }
7
+ ): number {
8
+ // Increase interval by 1 second every minute: 1s, 2s, 3s... capped.
9
+ const elapsedSeconds = Math.floor(elapsedMs / 1000);
10
+ const intervalSeconds = 1 + Math.floor(elapsedSeconds / 60);
11
+ const intervalMs = intervalSeconds * 1000;
12
+
13
+ return Math.min(params.maxIntervalMs, Math.max(params.minIntervalMs, intervalMs));
14
+ }