@cofhe/sdk 0.2.0 → 0.2.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 (57) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/chains/defineChain.ts +2 -2
  3. package/chains/types.ts +3 -3
  4. package/core/client.test.ts +111 -0
  5. package/core/client.ts +22 -2
  6. package/core/clientTypes.ts +7 -1
  7. package/core/config.test.ts +8 -0
  8. package/core/config.ts +10 -4
  9. package/core/consts.ts +18 -0
  10. package/core/decrypt/cofheMocksSealOutput.ts +2 -4
  11. package/core/encrypt/cofheMocksZkVerifySign.ts +4 -11
  12. package/core/index.ts +9 -1
  13. package/core/permits.test.ts +5 -6
  14. package/core/permits.ts +5 -4
  15. package/dist/chains.cjs +4 -7
  16. package/dist/chains.d.cts +12 -12
  17. package/dist/chains.d.ts +12 -12
  18. package/dist/chains.js +1 -1
  19. package/dist/{chunk-WGCRJCBR.js → chunk-I5WFEYXX.js} +33 -19
  20. package/dist/{chunk-UGBVZNRT.js → chunk-R3B5TMVX.js} +308 -189
  21. package/dist/{chunk-WEAZ25JO.js → chunk-TBLR7NNE.js} +4 -7
  22. package/dist/{clientTypes-Es7fyi65.d.ts → clientTypes-RqkgkV2i.d.ts} +34 -93
  23. package/dist/{clientTypes-5_1nwtUe.d.cts → clientTypes-e4filDzK.d.cts} +34 -93
  24. package/dist/core.cjs +343 -208
  25. package/dist/core.d.cts +17 -6
  26. package/dist/core.d.ts +17 -6
  27. package/dist/core.js +3 -3
  28. package/dist/node.cjs +337 -208
  29. package/dist/node.d.cts +3 -3
  30. package/dist/node.d.ts +3 -3
  31. package/dist/node.js +3 -3
  32. package/dist/{permit-fUSe6KKq.d.cts → permit-MZ502UBl.d.cts} +30 -33
  33. package/dist/{permit-fUSe6KKq.d.ts → permit-MZ502UBl.d.ts} +30 -33
  34. package/dist/permits.cjs +305 -187
  35. package/dist/permits.d.cts +111 -812
  36. package/dist/permits.d.ts +111 -812
  37. package/dist/permits.js +1 -1
  38. package/dist/types-YiAC4gig.d.cts +33 -0
  39. package/dist/types-YiAC4gig.d.ts +33 -0
  40. package/dist/web.cjs +337 -208
  41. package/dist/web.d.cts +3 -3
  42. package/dist/web.d.ts +3 -3
  43. package/dist/web.js +3 -3
  44. package/package.json +3 -3
  45. package/permits/localstorage.test.ts +9 -13
  46. package/permits/onchain-utils.ts +221 -0
  47. package/permits/permit.test.ts +51 -5
  48. package/permits/permit.ts +28 -74
  49. package/permits/store.test.ts +10 -50
  50. package/permits/store.ts +4 -14
  51. package/permits/test-utils.ts +10 -2
  52. package/permits/types.ts +22 -9
  53. package/permits/utils.ts +0 -4
  54. package/permits/validation.test.ts +29 -32
  55. package/permits/validation.ts +112 -194
  56. package/dist/types-KImPrEIe.d.cts +0 -48
  57. package/dist/types-KImPrEIe.d.ts +0 -48
package/permits/permit.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  type EIP712Domain,
12
12
  type Permission,
13
13
  type EthEncryptedData,
14
+ type PermitHashFields,
14
15
  } from './types.js';
15
16
  import {
16
17
  validateSelfPermitOptions,
@@ -21,8 +22,10 @@ import {
21
22
  validateImportPermit,
22
23
  ValidationUtils,
23
24
  } from './validation.js';
25
+ import * as z from 'zod';
24
26
  import { SignatureUtils } from './signature.js';
25
27
  import { GenerateSealingKey, SealingKey } from './sealing.js';
28
+ import { checkPermitValidityOnChain, getAclEIP712Domain } from './onchain-utils.js';
26
29
 
27
30
  /**
28
31
  * Main Permit utilities - functional approach for React compatibility
@@ -34,17 +37,12 @@ export const PermitUtils = {
34
37
  createSelf: (options: CreateSelfPermitOptions): SelfPermit => {
35
38
  const validation = validateSelfPermitOptions(options);
36
39
 
37
- if (!validation.success) {
38
- throw new Error(
39
- 'PermitUtils :: createSelf :: Parsing SelfPermitOptions failed ' + JSON.stringify(validation.error, null, 2)
40
- );
41
- }
42
-
43
40
  // Always generate a new sealing key - users cannot provide their own
44
41
  const sealingPair = GenerateSealingKey();
45
42
 
46
43
  const permit = {
47
- ...validation.data,
44
+ hash: PermitUtils.getHash(validation),
45
+ ...validation,
48
46
  sealingPair,
49
47
  _signedDomain: undefined,
50
48
  } satisfies SelfPermit;
@@ -58,18 +56,12 @@ export const PermitUtils = {
58
56
  createSharing: (options: CreateSharingPermitOptions): SharingPermit => {
59
57
  const validation = validateSharingPermitOptions(options);
60
58
 
61
- if (!validation.success) {
62
- throw new Error(
63
- 'PermitUtils :: createSharing :: Parsing SharingPermitOptions failed ' +
64
- JSON.stringify(validation.error, null, 2)
65
- );
66
- }
67
-
68
59
  // Always generate a new sealing key - users cannot provide their own
69
60
  const sealingPair = GenerateSealingKey();
70
61
 
71
62
  const permit = {
72
- ...validation.data,
63
+ hash: PermitUtils.getHash(validation),
64
+ ...validation,
73
65
  sealingPair,
74
66
  _signedDomain: undefined,
75
67
  } satisfies SharingPermit;
@@ -89,35 +81,28 @@ export const PermitUtils = {
89
81
  try {
90
82
  parsedOptions = JSON.parse(options);
91
83
  } catch (error) {
92
- throw new Error(`PermitUtils :: importShared :: Failed to parse JSON string: ${error}`);
84
+ throw new Error(`Failed to parse JSON string: ${error}`);
93
85
  }
94
86
  } else if (typeof options === 'object' && options !== null) {
95
87
  // Handle both ImportSharedPermitOptions and any object
96
88
  parsedOptions = options;
97
89
  } else {
98
- throw new Error(
99
- 'PermitUtils :: importShared :: Invalid input type, expected ImportSharedPermitOptions, object, or string'
100
- );
90
+ throw new Error('Invalid input type, expected ImportSharedPermitOptions, object, or string');
101
91
  }
102
92
 
103
93
  // Validate type if provided
104
94
  if (parsedOptions.type != null && parsedOptions.type !== 'sharing') {
105
- throw new Error(`PermitUtils :: importShared :: Invalid permit type <${parsedOptions.type}>, must be "sharing"`);
95
+ throw new Error(`Invalid permit type <${parsedOptions.type}>, must be "sharing"`);
106
96
  }
107
97
 
108
98
  const validation = validateImportPermitOptions({ ...parsedOptions, type: 'recipient' });
109
99
 
110
- if (!validation.success) {
111
- throw new Error(
112
- 'PermitUtils :: importShared :: Parsing ImportPermitOptions failed ' + JSON.stringify(validation.error, null, 2)
113
- );
114
- }
115
-
116
100
  // Always generate a new sealing key - users cannot provide their own
117
101
  const sealingPair = GenerateSealingKey();
118
102
 
119
103
  const permit = {
120
- ...validation.data,
104
+ hash: PermitUtils.getHash(validation),
105
+ ...validation,
121
106
  sealingPair,
122
107
  _signedDomain: undefined,
123
108
  } satisfies RecipientPermit;
@@ -131,12 +116,12 @@ export const PermitUtils = {
131
116
  sign: async <T extends Permit>(permit: T, publicClient: PublicClient, walletClient: WalletClient): Promise<T> => {
132
117
  if (walletClient == null || walletClient.account == null) {
133
118
  throw new Error(
134
- 'PermitUtils :: sign - walletClient undefined, you must pass in a `walletClient` for the connected user to create a permit signature'
119
+ 'Missing walletClient, you must pass in a `walletClient` for the connected user to create a permit signature'
135
120
  );
136
121
  }
137
122
 
138
123
  const primaryType = SignatureUtils.getPrimaryType(permit.type);
139
- const domain = await PermitUtils.fetchEIP712Domain(publicClient);
124
+ const domain = await getAclEIP712Domain(publicClient);
140
125
  const { types, message } = SignatureUtils.getSignatureParams(PermitUtils.getPermission(permit, true), primaryType);
141
126
 
142
127
  const signature = await walletClient.signTypedData({
@@ -216,6 +201,7 @@ export const PermitUtils = {
216
201
  */
217
202
  serialize: (permit: Permit): SerializedPermit => {
218
203
  return {
204
+ hash: permit.hash,
219
205
  name: permit.name,
220
206
  type: permit.type,
221
207
  issuer: permit.issuer,
@@ -241,7 +227,7 @@ export const PermitUtils = {
241
227
  } else if (permit.type === 'recipient') {
242
228
  return validateImportPermit(permit);
243
229
  } else {
244
- throw new Error('PermitUtils :: validate :: Invalid permit type');
230
+ throw new Error('Invalid permit type');
245
231
  }
246
232
  },
247
233
 
@@ -250,13 +236,7 @@ export const PermitUtils = {
250
236
  */
251
237
  getPermission: (permit: Permit, skipValidation = false): Permission => {
252
238
  if (!skipValidation) {
253
- const validationResult = PermitUtils.validate(permit);
254
-
255
- if (!validationResult.success) {
256
- throw new Error(
257
- `PermitUtils :: getPermission :: permit validation failed - ${JSON.stringify(validationResult.error, null, 2)} ${JSON.stringify(permit, null, 2)}`
258
- );
259
- }
239
+ PermitUtils.validate(permit);
260
240
  }
261
241
 
262
242
  return {
@@ -274,7 +254,7 @@ export const PermitUtils = {
274
254
  /**
275
255
  * Get a stable hash for the permit (used as key in storage)
276
256
  */
277
- getHash: (permit: Permit): string => {
257
+ getHash: (permit: PermitHashFields): string => {
278
258
  const data = JSON.stringify({
279
259
  type: permit.type,
280
260
  issuer: permit.issuer,
@@ -345,41 +325,7 @@ export const PermitUtils = {
345
325
  * Fetch EIP712 domain from the blockchain
346
326
  */
347
327
  fetchEIP712Domain: async (publicClient: PublicClient): Promise<EIP712Domain> => {
348
- // Hardcoded constants from the original implementation
349
- const TASK_MANAGER_ADDRESS = '0xeA30c4B8b44078Bbf8a6ef5b9f1eC1626C7848D9';
350
- const ACL_IFACE = 'function acl() view returns (address)';
351
- const EIP712_DOMAIN_IFACE =
352
- 'function eip712Domain() public view returns (bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions)';
353
-
354
- // Parse the ABI for the ACL function
355
- const aclAbi = parseAbi([ACL_IFACE]);
356
-
357
- // Get the ACL address
358
- const aclAddress = (await publicClient.readContract({
359
- address: TASK_MANAGER_ADDRESS as `0x${string}`,
360
- abi: aclAbi,
361
- functionName: 'acl',
362
- })) as `0x${string}`;
363
-
364
- // Parse the ABI for the EIP712 domain function
365
- const domainAbi = parseAbi([EIP712_DOMAIN_IFACE]);
366
-
367
- // Get the EIP712 domain
368
- const domain = await publicClient.readContract({
369
- address: aclAddress,
370
- abi: domainAbi,
371
- functionName: 'eip712Domain',
372
- });
373
-
374
- // eslint-disable-next-line no-unused-vars
375
- const [_fields, name, version, chainId, verifyingContract, _salt, _extensions] = domain;
376
-
377
- return {
378
- name,
379
- version,
380
- chainId: Number(chainId),
381
- verifyingContract,
382
- };
328
+ return getAclEIP712Domain(publicClient);
383
329
  },
384
330
 
385
331
  /**
@@ -399,7 +345,15 @@ export const PermitUtils = {
399
345
  */
400
346
  checkSignedDomainValid: async (permit: Permit, publicClient: PublicClient): Promise<boolean> => {
401
347
  if (permit._signedDomain == null) return false;
402
- const domain = await PermitUtils.fetchEIP712Domain(publicClient);
348
+ const domain = await getAclEIP712Domain(publicClient);
403
349
  return PermitUtils.matchesDomain(permit, domain);
404
350
  },
351
+
352
+ /**
353
+ * Check if permit passes the on-chain validation
354
+ */
355
+ checkValidityOnChain: async (permit: Permit, publicClient: PublicClient): Promise<boolean> => {
356
+ const permission = PermitUtils.getPermission(permit);
357
+ return checkPermitValidityOnChain(permission, publicClient);
358
+ },
405
359
  };
@@ -32,10 +32,9 @@ describe('Storage Tests', () => {
32
32
  describe('Permit Storage', () => {
33
33
  it('should store and retrieve permits', async () => {
34
34
  const permit = await createMockPermit();
35
- const hash = PermitUtils.getHash(permit);
36
35
 
37
36
  setPermit(chainId, account, permit);
38
- const retrieved = getPermit(chainId, account, hash);
37
+ const retrieved = getPermit(chainId, account, permit.hash);
39
38
 
40
39
  expect(retrieved).toBeDefined();
41
40
  expect(PermitUtils.serialize(retrieved!)).toEqual(PermitUtils.serialize(permit));
@@ -43,13 +42,9 @@ describe('Storage Tests', () => {
43
42
 
44
43
  it('should handle multiple permits per account', async () => {
45
44
  const permit1 = await createMockPermit();
46
- const permit2 = {
47
- ...(await createMockPermit()),
45
+ const permit2 = await createMockPermit({
48
46
  issuer: '0x0987654321098765432109876543210987654321' as `0x${string}`,
49
- };
50
-
51
- const hash1 = PermitUtils.getHash(permit1);
52
- const hash2 = PermitUtils.getHash(permit2);
47
+ });
53
48
 
54
49
  setPermit(chainId, account, permit1);
55
50
  setPermit(chainId, account, permit2);
@@ -57,19 +52,18 @@ describe('Storage Tests', () => {
57
52
  const permits = getPermits(chainId, account);
58
53
  expect(Object.keys(permits)).toHaveLength(2);
59
54
 
60
- expect(PermitUtils.serialize(permits[hash1])).toEqual(PermitUtils.serialize(permit1));
61
- expect(PermitUtils.serialize(permits[hash2])).toEqual(PermitUtils.serialize(permit2));
55
+ expect(PermitUtils.serialize(permits[permit1.hash])).toEqual(PermitUtils.serialize(permit1));
56
+ expect(PermitUtils.serialize(permits[permit2.hash])).toEqual(PermitUtils.serialize(permit2));
62
57
  });
63
58
 
64
59
  it('should handle active permit hash', async () => {
65
60
  const permit = await createMockPermit();
66
- const hash = PermitUtils.getHash(permit);
67
61
 
68
62
  setPermit(chainId, account, permit);
69
- setActivePermitHash(chainId, account, hash);
63
+ setActivePermitHash(chainId, account, permit.hash);
70
64
 
71
65
  const activeHash = getActivePermitHash(chainId, account);
72
- expect(activeHash).toBe(hash);
66
+ expect(activeHash).toBe(permit.hash);
73
67
 
74
68
  const activePermit = getActivePermit(chainId, account);
75
69
  expect(activePermit).toBeDefined();
@@ -78,51 +72,17 @@ describe('Storage Tests', () => {
78
72
 
79
73
  it('should remove permits', async () => {
80
74
  const permit = await createMockPermit();
81
- const hash = PermitUtils.getHash(permit);
82
75
 
83
76
  setPermit(chainId, account, permit);
84
- setActivePermitHash(chainId, account, hash);
77
+ setActivePermitHash(chainId, account, permit.hash);
85
78
 
86
- removePermit(chainId, account, hash, true);
79
+ removePermit(chainId, account, permit.hash);
87
80
 
88
- const retrieved = getPermit(chainId, account, hash);
81
+ const retrieved = getPermit(chainId, account, permit.hash);
89
82
  expect(retrieved).toBeUndefined();
90
83
 
91
84
  const activeHash = getActivePermitHash(chainId, account);
92
85
  expect(activeHash).toBeUndefined();
93
86
  });
94
-
95
- it('should prevent removing last permit without force', async () => {
96
- const permit = await createMockPermit();
97
- const hash = PermitUtils.getHash(permit);
98
-
99
- setPermit(chainId, account, permit);
100
- setActivePermitHash(chainId, account, hash);
101
-
102
- expect(() => {
103
- removePermit(chainId, account, hash, false);
104
- }).toThrow('Cannot remove the last permit without force flag');
105
- });
106
-
107
- it('should switch active permit when removing current active', async () => {
108
- const permit1 = await createMockPermit();
109
- const permit2 = {
110
- ...(await createMockPermit()),
111
- name: 'Second Permit',
112
- issuer: '0x0987654321098765432109876543210987654321' as `0x${string}`, // Different issuer
113
- };
114
-
115
- const hash1 = PermitUtils.getHash(permit1);
116
- const hash2 = PermitUtils.getHash(permit2);
117
-
118
- setPermit(chainId, account, permit1);
119
- setPermit(chainId, account, permit2);
120
- setActivePermitHash(chainId, account, hash1);
121
-
122
- removePermit(chainId, account, hash1, false);
123
-
124
- const activeHash = getActivePermitHash(chainId, account);
125
- expect(activeHash).toBe(hash2);
126
- });
127
87
  });
128
88
  });
package/permits/store.ts CHANGED
@@ -82,12 +82,12 @@ export const setPermit = (chainId: number, account: string, permit: Permit) => {
82
82
  produce<PermitsStore>((state) => {
83
83
  if (state.permits[chainId] == null) state.permits[chainId] = {};
84
84
  if (state.permits[chainId][account] == null) state.permits[chainId][account] = {};
85
- state.permits[chainId][account][PermitUtils.getHash(permit)] = PermitUtils.serialize(permit);
85
+ state.permits[chainId][account][permit.hash] = PermitUtils.serialize(permit);
86
86
  })
87
87
  );
88
88
  };
89
89
 
90
- export const removePermit = (chainId: number, account: string, hash: string, force?: boolean) => {
90
+ export const removePermit = (chainId: number, account: string, hash: string) => {
91
91
  clearStaleStore();
92
92
  _permitStore.setState(
93
93
  produce<PermitsStore>((state) => {
@@ -100,18 +100,8 @@ export const removePermit = (chainId: number, account: string, hash: string, for
100
100
  if (accountPermits[hash] == null) return;
101
101
 
102
102
  if (state.activePermitHash[chainId][account] === hash) {
103
- // Find other permits before removing
104
- const otherPermitHash = Object.keys(accountPermits).find((key) => key !== hash && accountPermits[key] != null);
105
-
106
- if (otherPermitHash) {
107
- state.activePermitHash[chainId][account] = otherPermitHash;
108
- } else {
109
- if (!force) {
110
- throw new Error('Cannot remove the last permit without force flag');
111
- }
112
- // Clear the active hash when removing the last permit with force
113
- state.activePermitHash[chainId][account] = undefined;
114
- }
103
+ // if the active permit is the one to be removed - unset it
104
+ state.activePermitHash[chainId][account] = undefined;
115
105
  }
116
106
  // Remove the permit
117
107
  accountPermits[hash] = undefined;
@@ -1,9 +1,10 @@
1
1
  import { type Permit, type SerializedPermit, GenerateSealingKey, PermitUtils } from './index.js';
2
2
 
3
3
  // Mock permit for testing - using Bob's address as issuer
4
- export const createMockPermit = async (): Promise<Permit> => {
4
+ export const createMockPermit = async (overrides: Partial<Permit> = {}): Promise<Permit> => {
5
5
  const sealingPair = GenerateSealingKey();
6
- const serializedPermit: SerializedPermit = {
6
+
7
+ const fields = {
7
8
  name: 'Test Permit',
8
9
  type: 'self',
9
10
  issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
@@ -15,6 +16,13 @@ export const createMockPermit = async (): Promise<Permit> => {
15
16
  issuerSignature: '0x',
16
17
  recipientSignature: '0x',
17
18
  _signedDomain: undefined,
19
+ ...overrides,
20
+ } as const;
21
+
22
+ const serializedPermit: SerializedPermit = {
23
+ hash: PermitUtils.getHash(fields),
24
+ ...fields,
18
25
  };
26
+
19
27
  return PermitUtils.deserialize(serializedPermit);
20
28
  };
package/permits/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { SealingKey as SealingKeyClass, type EthEncryptedData } from './sealing.js';
2
+ import { type Hex } from 'viem';
2
3
 
3
4
  /**
4
5
  * EIP712 related types
@@ -9,7 +10,7 @@ export type EIP712Message = Record<string, string>;
9
10
  export type EIP712Domain = {
10
11
  chainId: number;
11
12
  name: string;
12
- verifyingContract: `0x${string}`;
13
+ verifyingContract: Hex;
13
14
  version: string;
14
15
  };
15
16
 
@@ -29,6 +30,10 @@ export type { EthEncryptedData };
29
30
  * Core Permit interface - immutable design for React compatibility
30
31
  */
31
32
  export interface Permit {
33
+ /**
34
+ * Stable hash of relevant permit data, used as key in storage
35
+ */
36
+ hash: string;
32
37
  /**
33
38
  * Name for this permit, for organization and UI usage, not included in signature.
34
39
  */
@@ -43,7 +48,7 @@ export interface Permit {
43
48
  /**
44
49
  * (base) User that initially created the permission, target of data fetching
45
50
  */
46
- issuer: `0x${string}`;
51
+ issuer: Hex;
47
52
  /**
48
53
  * (base) Expiration timestamp
49
54
  */
@@ -52,7 +57,7 @@ export interface Permit {
52
57
  * (sharing) The user that this permission will be shared with
53
58
  * ** optional, use `address(0)` to disable **
54
59
  */
55
- recipient: `0x${string}`;
60
+ recipient: Hex;
56
61
  /**
57
62
  * (issuer defined validation) An id used to query a contract to check this permissions validity
58
63
  * ** optional, use `0` to disable **
@@ -62,7 +67,7 @@ export interface Permit {
62
67
  * (issuer defined validation) The contract to query to determine permission validity
63
68
  * ** optional, user `address(0)` to disable **
64
69
  */
65
- validatorContract: `0x${string}`;
70
+ validatorContract: Hex;
66
71
  /**
67
72
  * (base) The publicKey of a sealingPair used to re-encrypt `issuer`s confidential data
68
73
  * (non-sharing) Populated by `issuer`
@@ -75,13 +80,13 @@ export interface Permit {
75
80
  * (non-sharing) < issuer, expiration, recipient, validatorId, validatorContract, sealingKey >
76
81
  * (sharing) < issuer, expiration, recipient, validatorId, validatorContract >
77
82
  */
78
- issuerSignature: `0x${string}`;
83
+ issuerSignature: Hex;
79
84
  /**
80
85
  * (sharing) `signTypedData` signature created by `recipient` with format:
81
86
  * (sharing) < sealingKey, issuerSignature>
82
87
  * ** required for shared permits **
83
88
  */
84
- recipientSignature: `0x${string}`;
89
+ recipientSignature: Hex;
85
90
  /**
86
91
  * EIP712 domain used to sign this permit.
87
92
  * Should not be set manually, included in metadata as part of serialization flows.
@@ -149,7 +154,7 @@ export type ImportSharedPermitOptions = {
149
154
  recipient: string;
150
155
  issuerSignature: string;
151
156
  name?: string;
152
- expiration?: number;
157
+ expiration: number;
153
158
  validatorId?: number;
154
159
  validatorContract?: string;
155
160
  };
@@ -166,11 +171,19 @@ export type SerializedPermit = Omit<Permit, 'sealingPair'> & {
166
171
  * A type representing the Permission struct that is passed to Permissioned.sol to grant encrypted data access.
167
172
  */
168
173
  export type Permission = Expand<
169
- Omit<Permit, 'name' | 'type' | 'sealingPair'> & {
170
- sealingKey: `0x${string}`;
174
+ Omit<Permit, 'name' | 'type' | 'sealingPair' | 'hash'> & {
175
+ sealingKey: Hex;
171
176
  }
172
177
  >;
173
178
 
179
+ /**
180
+ * A type representing the permit fields that are used to generate the hash
181
+ */
182
+ export type PermitHashFields = Pick<
183
+ Permit,
184
+ 'type' | 'issuer' | 'expiration' | 'recipient' | 'validatorId' | 'validatorContract'
185
+ >;
186
+
174
187
  /**
175
188
  * Validation result type
176
189
  */
package/permits/utils.ts CHANGED
@@ -56,7 +56,3 @@ export function isBigIntOrNumber(value: unknown) {
56
56
  }
57
57
  }
58
58
  }
59
-
60
- export function is0xPrefixed(value: string): value is `0x${string}` {
61
- return value.startsWith('0x');
62
- }
@@ -23,9 +23,9 @@ describe('Validation Tests', () => {
23
23
  name: 'Test Permit',
24
24
  };
25
25
 
26
+ expect(() => validateSelfPermitOptions(options)).not.toThrow();
26
27
  const result = validateSelfPermitOptions(options);
27
- expect(result.success).toBe(true);
28
- expect(result.data).toBeDefined();
28
+ expect(result).toBeDefined();
29
29
  });
30
30
 
31
31
  it('should reject invalid address', () => {
@@ -35,9 +35,7 @@ describe('Validation Tests', () => {
35
35
  name: 'Test Permit',
36
36
  };
37
37
 
38
- const result = validateSelfPermitOptions(options);
39
- expect(result.success).toBe(false);
40
- expect(result.error).toBeDefined();
38
+ expect(() => validateSelfPermitOptions(options)).toThrow();
41
39
  });
42
40
 
43
41
  it('should reject zero address', () => {
@@ -47,9 +45,7 @@ describe('Validation Tests', () => {
47
45
  name: 'Test Permit',
48
46
  };
49
47
 
50
- const result = validateSelfPermitOptions(options);
51
- expect(result.success).toBe(false);
52
- expect(result.error).toBeDefined();
48
+ expect(() => validateSelfPermitOptions(options)).toThrow();
53
49
  });
54
50
  });
55
51
 
@@ -62,8 +58,7 @@ describe('Validation Tests', () => {
62
58
  name: 'Sharing Permit',
63
59
  };
64
60
 
65
- const result = validateSharingPermitOptions(options);
66
- expect(result.success).toBe(true);
61
+ expect(() => validateSharingPermitOptions(options)).not.toThrow();
67
62
  });
68
63
 
69
64
  it('should reject sharing permit with zero recipient', () => {
@@ -74,8 +69,7 @@ describe('Validation Tests', () => {
74
69
  name: 'Sharing Permit',
75
70
  };
76
71
 
77
- const result = validateSharingPermitOptions(options);
78
- expect(result.success).toBe(false);
72
+ expect(() => validateSharingPermitOptions(options)).toThrow();
79
73
  });
80
74
 
81
75
  it('should reject sharing permit with invalid recipient', () => {
@@ -86,8 +80,7 @@ describe('Validation Tests', () => {
86
80
  name: 'Sharing Permit',
87
81
  };
88
82
 
89
- const result = validateSharingPermitOptions(options);
90
- expect(result.success).toBe(false);
83
+ expect(() => validateSharingPermitOptions(options)).toThrow();
91
84
  });
92
85
  });
93
86
 
@@ -95,37 +88,47 @@ describe('Validation Tests', () => {
95
88
  it('should validate valid import permit options', () => {
96
89
  const options: ImportSharedPermitOptions = {
97
90
  issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
91
+ expiration: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now
98
92
  recipient: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', // Alice's address
99
93
  issuerSignature: '0x1234567890abcdef',
100
94
  name: 'Import Permit',
101
95
  };
102
96
 
103
- const result = validateImportPermitOptions(options);
104
- expect(result.success).toBe(true);
97
+ expect(() => validateImportPermitOptions(options)).not.toThrow();
98
+ });
99
+
100
+ it('should reject import permit with missing expiration', () => {
101
+ const options = {
102
+ issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
103
+ recipient: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', // Alice's address
104
+ issuerSignature: '0x1234567890abcdef',
105
+ name: 'Import Permit',
106
+ };
107
+ expect(() => validateImportPermitOptions(options)).toThrow();
105
108
  });
106
109
 
107
110
  it('should reject import permit with empty signature', () => {
108
111
  const options: ImportSharedPermitOptions = {
109
112
  issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
113
+ expiration: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now
110
114
  recipient: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', // Alice's address
111
115
  issuerSignature: '0x',
112
116
  name: 'Import Permit',
113
117
  };
114
118
 
115
- const result = validateImportPermitOptions(options);
116
- expect(result.success).toBe(false);
119
+ expect(() => validateImportPermitOptions(options)).toThrow();
117
120
  });
118
121
 
119
122
  it('should reject import permit with invalid signature', () => {
120
123
  const options: ImportSharedPermitOptions = {
121
124
  issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
125
+ expiration: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now
122
126
  recipient: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', // Alice's address
123
127
  issuerSignature: '0x',
124
128
  name: 'Import Permit',
125
129
  };
126
130
 
127
- const result = validateImportPermitOptions(options);
128
- expect(result.success).toBe(false);
131
+ expect(() => validateImportPermitOptions(options)).toThrow();
129
132
  });
130
133
  });
131
134
 
@@ -135,15 +138,13 @@ describe('Validation Tests', () => {
135
138
  permit.type = 'self';
136
139
  permit.issuerSignature = '0x1234567890abcdef';
137
140
 
138
- const result = validateSelfPermit(permit);
139
- expect(result.success).toBe(true);
141
+ expect(() => validateSelfPermit(permit)).not.toThrow();
140
142
  });
141
143
 
142
144
  it('should reject self permit with missing sealing pair', async () => {
143
145
  const permit = { ...(await createMockPermit()), sealingPair: undefined };
144
146
  permit.type = 'self';
145
- const result = validateSelfPermit(permit as unknown as Permit);
146
- expect(result.success).toBe(false);
147
+ expect(() => validateSelfPermit(permit as unknown as Permit)).toThrow();
147
148
  });
148
149
  });
149
150
 
@@ -154,8 +155,7 @@ describe('Validation Tests', () => {
154
155
  permit.issuerSignature = '0x1234567890abcdef';
155
156
  permit.recipient = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'; // Alice's address
156
157
 
157
- const result = validateSharingPermit(permit);
158
- expect(result.success).toBe(true);
158
+ expect(() => validateSharingPermit(permit)).not.toThrow();
159
159
  });
160
160
 
161
161
  it('should reject sharing permit with zero recipient', async () => {
@@ -164,8 +164,7 @@ describe('Validation Tests', () => {
164
164
  permit.issuerSignature = '0x1234567890abcdef';
165
165
  permit.recipient = '0x0000000000000000000000000000000000000000';
166
166
 
167
- const result = validateSharingPermit(permit);
168
- expect(result.success).toBe(false);
167
+ expect(() => validateSharingPermit(permit)).toThrow();
169
168
  });
170
169
  });
171
170
 
@@ -177,8 +176,7 @@ describe('Validation Tests', () => {
177
176
  permit.recipient = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'; // Alice's address
178
177
  permit.recipientSignature = '0xabcdef1234567890';
179
178
 
180
- const result = validateImportPermit(permit);
181
- expect(result.success).toBe(true);
179
+ expect(() => validateImportPermit(permit)).not.toThrow();
182
180
  });
183
181
 
184
182
  it('should reject import permit with empty recipient signature', async () => {
@@ -188,8 +186,7 @@ describe('Validation Tests', () => {
188
186
  permit.recipient = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'; // Alice's address
189
187
  permit.recipientSignature = '0x';
190
188
 
191
- const result = validateImportPermit(permit);
192
- expect(result.success).toBe(false);
189
+ expect(() => validateImportPermit(permit)).toThrow();
193
190
  });
194
191
  });
195
192