@cofhe/sdk 0.0.0-beta-20251027110729

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 (110) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/adapters/ethers5.test.ts +174 -0
  3. package/adapters/ethers5.ts +36 -0
  4. package/adapters/ethers6.test.ts +169 -0
  5. package/adapters/ethers6.ts +36 -0
  6. package/adapters/hardhat-node.ts +167 -0
  7. package/adapters/hardhat.hh2.test.ts +159 -0
  8. package/adapters/hardhat.ts +37 -0
  9. package/adapters/index.test.ts +25 -0
  10. package/adapters/index.ts +5 -0
  11. package/adapters/smartWallet.ts +91 -0
  12. package/adapters/test-utils.ts +53 -0
  13. package/adapters/types.ts +6 -0
  14. package/adapters/wagmi.test.ts +156 -0
  15. package/adapters/wagmi.ts +17 -0
  16. package/chains/chains/arbSepolia.ts +14 -0
  17. package/chains/chains/baseSepolia.ts +14 -0
  18. package/chains/chains/hardhat.ts +15 -0
  19. package/chains/chains/sepolia.ts +14 -0
  20. package/chains/chains.test.ts +49 -0
  21. package/chains/defineChain.ts +18 -0
  22. package/chains/index.ts +33 -0
  23. package/chains/types.ts +32 -0
  24. package/core/baseBuilder.ts +138 -0
  25. package/core/client.test.ts +298 -0
  26. package/core/client.ts +308 -0
  27. package/core/config.test.ts +224 -0
  28. package/core/config.ts +213 -0
  29. package/core/decrypt/MockQueryDecrypterAbi.ts +129 -0
  30. package/core/decrypt/cofheMocksSealOutput.ts +57 -0
  31. package/core/decrypt/decryptHandleBuilder.ts +281 -0
  32. package/core/decrypt/decryptUtils.ts +28 -0
  33. package/core/decrypt/tnSealOutput.ts +59 -0
  34. package/core/encrypt/MockZkVerifierAbi.ts +106 -0
  35. package/core/encrypt/cofheMocksZkVerifySign.ts +278 -0
  36. package/core/encrypt/encryptInputsBuilder.test.ts +735 -0
  37. package/core/encrypt/encryptInputsBuilder.ts +512 -0
  38. package/core/encrypt/encryptUtils.ts +64 -0
  39. package/core/encrypt/zkPackProveVerify.ts +273 -0
  40. package/core/error.ts +170 -0
  41. package/core/fetchKeys.test.ts +212 -0
  42. package/core/fetchKeys.ts +170 -0
  43. package/core/index.ts +77 -0
  44. package/core/keyStore.test.ts +226 -0
  45. package/core/keyStore.ts +127 -0
  46. package/core/permits.test.ts +242 -0
  47. package/core/permits.ts +136 -0
  48. package/core/result.test.ts +180 -0
  49. package/core/result.ts +67 -0
  50. package/core/test-utils.ts +45 -0
  51. package/core/types.ts +352 -0
  52. package/core/utils.ts +88 -0
  53. package/dist/adapters.cjs +88 -0
  54. package/dist/adapters.d.cts +14558 -0
  55. package/dist/adapters.d.ts +14558 -0
  56. package/dist/adapters.js +83 -0
  57. package/dist/chains.cjs +101 -0
  58. package/dist/chains.d.cts +99 -0
  59. package/dist/chains.d.ts +99 -0
  60. package/dist/chains.js +1 -0
  61. package/dist/chunk-GZCQQYVI.js +93 -0
  62. package/dist/chunk-KFGPTJ6X.js +2295 -0
  63. package/dist/chunk-LU7BMUUT.js +804 -0
  64. package/dist/core.cjs +3174 -0
  65. package/dist/core.d.cts +16 -0
  66. package/dist/core.d.ts +16 -0
  67. package/dist/core.js +3 -0
  68. package/dist/node.cjs +3090 -0
  69. package/dist/node.d.cts +22 -0
  70. package/dist/node.d.ts +22 -0
  71. package/dist/node.js +90 -0
  72. package/dist/permit-S9CnI6MF.d.cts +333 -0
  73. package/dist/permit-S9CnI6MF.d.ts +333 -0
  74. package/dist/permits.cjs +856 -0
  75. package/dist/permits.d.cts +1056 -0
  76. package/dist/permits.d.ts +1056 -0
  77. package/dist/permits.js +1 -0
  78. package/dist/types-KImPrEIe.d.cts +48 -0
  79. package/dist/types-KImPrEIe.d.ts +48 -0
  80. package/dist/types-PhwGgQvs.d.ts +953 -0
  81. package/dist/types-bB7wLj0q.d.cts +953 -0
  82. package/dist/web.cjs +3067 -0
  83. package/dist/web.d.cts +22 -0
  84. package/dist/web.d.ts +22 -0
  85. package/dist/web.js +64 -0
  86. package/node/client.test.ts +152 -0
  87. package/node/config.test.ts +68 -0
  88. package/node/encryptInputs.test.ts +175 -0
  89. package/node/index.ts +96 -0
  90. package/node/storage.ts +51 -0
  91. package/package.json +120 -0
  92. package/permits/index.ts +67 -0
  93. package/permits/localstorage.test.ts +118 -0
  94. package/permits/permit.test.ts +474 -0
  95. package/permits/permit.ts +396 -0
  96. package/permits/sealing.test.ts +84 -0
  97. package/permits/sealing.ts +131 -0
  98. package/permits/signature.ts +79 -0
  99. package/permits/store.test.ts +128 -0
  100. package/permits/store.ts +168 -0
  101. package/permits/test-utils.ts +20 -0
  102. package/permits/types.ts +174 -0
  103. package/permits/utils.ts +63 -0
  104. package/permits/validation.test.ts +288 -0
  105. package/permits/validation.ts +349 -0
  106. package/web/client.web.test.ts +152 -0
  107. package/web/config.web.test.ts +71 -0
  108. package/web/encryptInputs.web.test.ts +195 -0
  109. package/web/index.ts +97 -0
  110. package/web/storage.ts +20 -0
@@ -0,0 +1,288 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ ValidationUtils,
4
+ validateSelfPermitOptions,
5
+ validateSharingPermitOptions,
6
+ validateImportPermitOptions,
7
+ validateSelfPermit,
8
+ validateSharingPermit,
9
+ validateImportPermit,
10
+ type Permit,
11
+ type CreateSelfPermitOptions,
12
+ type CreateSharingPermitOptions,
13
+ type ImportSharedPermitOptions,
14
+ } from './index.js';
15
+ import { createMockPermit } from './test-utils.js';
16
+
17
+ describe('Validation Tests', () => {
18
+ describe('validateSelfPermitOptions', () => {
19
+ it('should validate valid self permit options', () => {
20
+ const options: CreateSelfPermitOptions = {
21
+ type: 'self',
22
+ issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
23
+ name: 'Test Permit',
24
+ };
25
+
26
+ const result = validateSelfPermitOptions(options);
27
+ expect(result.success).toBe(true);
28
+ expect(result.data).toBeDefined();
29
+ });
30
+
31
+ it('should reject invalid address', () => {
32
+ const options: CreateSelfPermitOptions = {
33
+ type: 'self',
34
+ issuer: 'invalid-address',
35
+ name: 'Test Permit',
36
+ };
37
+
38
+ const result = validateSelfPermitOptions(options);
39
+ expect(result.success).toBe(false);
40
+ expect(result.error).toBeDefined();
41
+ });
42
+
43
+ it('should reject zero address', () => {
44
+ const options: CreateSelfPermitOptions = {
45
+ type: 'self',
46
+ issuer: '0x0000000000000000000000000000000000000000',
47
+ name: 'Test Permit',
48
+ };
49
+
50
+ const result = validateSelfPermitOptions(options);
51
+ expect(result.success).toBe(false);
52
+ expect(result.error).toBeDefined();
53
+ });
54
+ });
55
+
56
+ describe('validateSharingPermitOptions', () => {
57
+ it('should validate valid sharing permit options', () => {
58
+ const options: CreateSharingPermitOptions = {
59
+ type: 'sharing',
60
+ issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
61
+ recipient: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', // Alice's address
62
+ name: 'Sharing Permit',
63
+ };
64
+
65
+ const result = validateSharingPermitOptions(options);
66
+ expect(result.success).toBe(true);
67
+ });
68
+
69
+ it('should reject sharing permit with zero recipient', () => {
70
+ const options: CreateSharingPermitOptions = {
71
+ type: 'sharing',
72
+ issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
73
+ recipient: '0x0000000000000000000000000000000000000000',
74
+ name: 'Sharing Permit',
75
+ };
76
+
77
+ const result = validateSharingPermitOptions(options);
78
+ expect(result.success).toBe(false);
79
+ });
80
+
81
+ it('should reject sharing permit with invalid recipient', () => {
82
+ const options: CreateSharingPermitOptions = {
83
+ type: 'sharing',
84
+ issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
85
+ recipient: 'invalid-address',
86
+ name: 'Sharing Permit',
87
+ };
88
+
89
+ const result = validateSharingPermitOptions(options);
90
+ expect(result.success).toBe(false);
91
+ });
92
+ });
93
+
94
+ describe('validateImportPermitOptions', () => {
95
+ it('should validate valid import permit options', () => {
96
+ const options: ImportSharedPermitOptions = {
97
+ issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
98
+ recipient: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', // Alice's address
99
+ issuerSignature: '0x1234567890abcdef',
100
+ name: 'Import Permit',
101
+ };
102
+
103
+ const result = validateImportPermitOptions(options);
104
+ expect(result.success).toBe(true);
105
+ });
106
+
107
+ it('should reject import permit with empty signature', () => {
108
+ const options: ImportSharedPermitOptions = {
109
+ issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
110
+ recipient: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', // Alice's address
111
+ issuerSignature: '0x',
112
+ name: 'Import Permit',
113
+ };
114
+
115
+ const result = validateImportPermitOptions(options);
116
+ expect(result.success).toBe(false);
117
+ });
118
+
119
+ it('should reject import permit with invalid signature', () => {
120
+ const options: ImportSharedPermitOptions = {
121
+ issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
122
+ recipient: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', // Alice's address
123
+ issuerSignature: '0x',
124
+ name: 'Import Permit',
125
+ };
126
+
127
+ const result = validateImportPermitOptions(options);
128
+ expect(result.success).toBe(false);
129
+ });
130
+ });
131
+
132
+ describe('validateSelfPermit', () => {
133
+ it('should validate valid self permit', async () => {
134
+ const permit = await createMockPermit();
135
+ permit.type = 'self';
136
+ permit.issuerSignature = '0x1234567890abcdef';
137
+
138
+ const result = validateSelfPermit(permit);
139
+ expect(result.success).toBe(true);
140
+ });
141
+
142
+ it('should reject self permit with missing sealing pair', async () => {
143
+ const permit = { ...(await createMockPermit()), sealingPair: undefined };
144
+ permit.type = 'self';
145
+ const result = validateSelfPermit(permit as unknown as Permit);
146
+ expect(result.success).toBe(false);
147
+ });
148
+ });
149
+
150
+ describe('validateSharingPermit', () => {
151
+ it('should validate valid sharing permit', async () => {
152
+ const permit = await createMockPermit();
153
+ permit.type = 'sharing';
154
+ permit.issuerSignature = '0x1234567890abcdef';
155
+ permit.recipient = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'; // Alice's address
156
+
157
+ const result = validateSharingPermit(permit);
158
+ expect(result.success).toBe(true);
159
+ });
160
+
161
+ it('should reject sharing permit with zero recipient', async () => {
162
+ const permit = await createMockPermit();
163
+ permit.type = 'sharing';
164
+ permit.issuerSignature = '0x1234567890abcdef';
165
+ permit.recipient = '0x0000000000000000000000000000000000000000';
166
+
167
+ const result = validateSharingPermit(permit);
168
+ expect(result.success).toBe(false);
169
+ });
170
+ });
171
+
172
+ describe('validateImportPermit', () => {
173
+ it('should validate valid import permit', async () => {
174
+ const permit = await createMockPermit();
175
+ permit.type = 'recipient';
176
+ permit.issuerSignature = '0x1234567890abcdef';
177
+ permit.recipient = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'; // Alice's address
178
+ permit.recipientSignature = '0xabcdef1234567890';
179
+
180
+ const result = validateImportPermit(permit);
181
+ expect(result.success).toBe(true);
182
+ });
183
+
184
+ it('should reject import permit with empty recipient signature', async () => {
185
+ const permit = await createMockPermit();
186
+ permit.type = 'recipient';
187
+ permit.issuerSignature = '0x1234567890abcdef';
188
+ permit.recipient = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'; // Alice's address
189
+ permit.recipientSignature = '0x';
190
+
191
+ const result = validateImportPermit(permit);
192
+ expect(result.success).toBe(false);
193
+ });
194
+ });
195
+
196
+ describe('ValidationUtils', () => {
197
+ describe('isExpired', () => {
198
+ it('should return true for expired permit', async () => {
199
+ const permit = {
200
+ ...(await createMockPermit()),
201
+ expiration: Math.floor(Date.now() / 1000) - 3600, // 1 hour ago
202
+ };
203
+ expect(ValidationUtils.isExpired(permit)).toBe(true);
204
+ });
205
+
206
+ it('should return false for non-expired permit', async () => {
207
+ const permit = {
208
+ ...(await createMockPermit()),
209
+ expiration: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now
210
+ };
211
+ expect(ValidationUtils.isExpired(permit)).toBe(false);
212
+ });
213
+ });
214
+
215
+ describe('isSigned', () => {
216
+ it('should return true for signed self permit', async () => {
217
+ const permit = {
218
+ ...(await createMockPermit()),
219
+ type: 'self' as const,
220
+ issuerSignature: '0x1234567890abcdef' as `0x${string}`,
221
+ };
222
+ expect(ValidationUtils.isSigned(permit)).toBe(true);
223
+ });
224
+
225
+ it('should return false for unsigned self permit', async () => {
226
+ const permit = {
227
+ ...(await createMockPermit()),
228
+ type: 'self' as const,
229
+ issuerSignature: '0x' as `0x${string}`,
230
+ };
231
+ expect(ValidationUtils.isSigned(permit)).toBe(false);
232
+ });
233
+
234
+ it('should return true for signed recipient permit', async () => {
235
+ const permit = {
236
+ ...(await createMockPermit()),
237
+ type: 'recipient' as const,
238
+ recipientSignature: '0x1234567890abcdef' as `0x${string}`,
239
+ };
240
+ expect(ValidationUtils.isSigned(permit)).toBe(true);
241
+ });
242
+
243
+ it('should return false for unsigned recipient permit', async () => {
244
+ const permit = {
245
+ ...(await createMockPermit()),
246
+ type: 'recipient' as const,
247
+ recipientSignature: '0x' as `0x${string}`,
248
+ };
249
+ expect(ValidationUtils.isSigned(permit)).toBe(false);
250
+ });
251
+ });
252
+
253
+ describe('isValid', () => {
254
+ it('should return valid for valid permit', async () => {
255
+ const permit = {
256
+ ...(await createMockPermit()),
257
+ expiration: Math.floor(Date.now() / 1000) + 3600,
258
+ issuerSignature: '0x1234567890abcdef' as `0x${string}`,
259
+ };
260
+ const result = ValidationUtils.isValid(permit);
261
+ expect(result.valid).toBe(true);
262
+ expect(result.error).toBeNull();
263
+ });
264
+
265
+ it('should return invalid for expired permit', async () => {
266
+ const permit = {
267
+ ...(await createMockPermit()),
268
+ expiration: Math.floor(Date.now() / 1000) - 3600,
269
+ issuerSignature: '0x1234567890abcdef' as `0x${string}`,
270
+ };
271
+ const result = ValidationUtils.isValid(permit);
272
+ expect(result.valid).toBe(false);
273
+ expect(result.error).toBe('expired');
274
+ });
275
+
276
+ it('should return invalid for unsigned permit', async () => {
277
+ const permit = {
278
+ ...(await createMockPermit()),
279
+ expiration: Math.floor(Date.now() / 1000) + 3600,
280
+ issuerSignature: '0x' as `0x${string}`,
281
+ };
282
+ const result = ValidationUtils.isValid(permit);
283
+ expect(result.valid).toBe(false);
284
+ expect(result.error).toBe('not-signed');
285
+ });
286
+ });
287
+ });
288
+ });
@@ -0,0 +1,349 @@
1
+ import { z } from 'zod';
2
+ import { isAddress, zeroAddress } from 'viem';
3
+ import { type Permit, type ValidationResult } from './types.js';
4
+ import { is0xPrefixed } from './utils.js';
5
+
6
+ const SerializedSealingPair = z.object({
7
+ privateKey: z.string(),
8
+ publicKey: z.string(),
9
+ });
10
+
11
+ const zPermitWithDefaults = z.object({
12
+ name: z.string().optional().default('Unnamed Permit'),
13
+ type: z.enum(['self', 'sharing', 'recipient']),
14
+ issuer: z
15
+ .string()
16
+ .refine((val) => isAddress(val), {
17
+ message: 'Permit issuer :: invalid address',
18
+ })
19
+ .refine((val) => val !== zeroAddress, {
20
+ message: 'Permit issuer :: must not be zeroAddress',
21
+ }),
22
+ expiration: z.number().optional().default(1000000000000),
23
+ recipient: z
24
+ .string()
25
+ .optional()
26
+ .default(zeroAddress)
27
+ .refine((val) => isAddress(val), {
28
+ message: 'Permit recipient :: invalid address',
29
+ }),
30
+ validatorId: z.number().optional().default(0),
31
+ validatorContract: z
32
+ .string()
33
+ .optional()
34
+ .default(zeroAddress)
35
+ .refine((val) => isAddress(val), {
36
+ message: 'Permit validatorContract :: invalid address',
37
+ }),
38
+ issuerSignature: z.string().optional().default('0x'),
39
+ recipientSignature: z.string().optional().default('0x'),
40
+ });
41
+
42
+ const zPermitWithSealingPair = zPermitWithDefaults.extend({
43
+ sealingPair: SerializedSealingPair.optional(),
44
+ });
45
+
46
+ type zPermitType = z.infer<typeof zPermitWithDefaults>;
47
+
48
+ /**
49
+ * Permits allow a hook into an optional external validator contract,
50
+ * this check ensures that IF an external validator is applied, that both `validatorId` and `validatorContract` are populated,
51
+ * ELSE ensures that both `validatorId` and `validatorContract` are empty
52
+ */
53
+ const ValidatorContractRefinement = [
54
+ (data: zPermitType) =>
55
+ (data.validatorId !== 0 && data.validatorContract !== zeroAddress) ||
56
+ (data.validatorId === 0 && data.validatorContract === zeroAddress),
57
+ {
58
+ message: 'Permit external validator :: validatorId and validatorContract must either both be set or both be unset.',
59
+ path: ['validatorId', 'validatorContract'] as string[],
60
+ },
61
+ ] as const;
62
+
63
+ // ============================================================================
64
+ // SELF PERMIT VALIDATORS
65
+ // ============================================================================
66
+
67
+ /**
68
+ * Validator for self permit creation options
69
+ */
70
+ export const SelfPermitOptionsValidator = z
71
+ .object({
72
+ type: z.literal('self').optional().default('self'),
73
+ issuer: z
74
+ .string()
75
+ .refine((val) => isAddress(val), {
76
+ message: 'Self permit issuer :: invalid address',
77
+ })
78
+ .refine((val) => val !== zeroAddress, {
79
+ message: 'Self permit issuer :: must not be zeroAddress',
80
+ }),
81
+ name: z.string().optional().default('Unnamed Permit'),
82
+ expiration: z.number().optional().default(1000000000000),
83
+ recipient: z
84
+ .string()
85
+ .optional()
86
+ .default(zeroAddress)
87
+ .refine((val) => isAddress(val), {
88
+ message: 'Self permit recipient :: invalid address',
89
+ })
90
+ .refine((val) => val === zeroAddress, {
91
+ message: 'Self permit recipient :: must be zeroAddress',
92
+ }),
93
+ validatorId: z.number().optional().default(0),
94
+ validatorContract: z
95
+ .string()
96
+ .optional()
97
+ .default(zeroAddress)
98
+ .refine((val) => isAddress(val), {
99
+ message: 'Self permit validatorContract :: invalid address',
100
+ }),
101
+ issuerSignature: z
102
+ .string()
103
+ .optional()
104
+ .default('0x')
105
+ .refine((val) => is0xPrefixed(val), {
106
+ message: 'Self permit issuerSignature :: must be 0x prefixed',
107
+ }),
108
+ recipientSignature: z
109
+ .string()
110
+ .optional()
111
+ .default('0x')
112
+ .refine((val) => is0xPrefixed(val), {
113
+ message: 'Self permit recipientSignature :: must be 0x prefixed',
114
+ }),
115
+ })
116
+ .refine(...ValidatorContractRefinement);
117
+
118
+ /**
119
+ * Validator for fully formed self permits
120
+ */
121
+ export const SelfPermitValidator = zPermitWithSealingPair
122
+ .refine((data) => data.type === 'self', {
123
+ message: "Self permit :: type must be 'self'",
124
+ })
125
+ .refine((data) => data.recipient === zeroAddress, {
126
+ message: 'Self permit :: recipient must be zeroAddress',
127
+ })
128
+ .refine((data) => data.issuerSignature !== '0x', {
129
+ message: 'Self permit :: issuerSignature must be populated',
130
+ })
131
+ .refine((data) => data.recipientSignature === '0x', {
132
+ message: 'Self permit :: recipientSignature must be empty',
133
+ })
134
+ .refine(...ValidatorContractRefinement);
135
+
136
+ // ============================================================================
137
+ // SHARING PERMIT VALIDATORS
138
+ // ============================================================================
139
+
140
+ /**
141
+ * Validator for sharing permit creation options
142
+ */
143
+ export const SharingPermitOptionsValidator = z
144
+ .object({
145
+ type: z.literal('sharing').optional().default('sharing'),
146
+ issuer: z
147
+ .string()
148
+ .refine((val) => isAddress(val), {
149
+ message: 'Sharing permit issuer :: invalid address',
150
+ })
151
+ .refine((val) => val !== zeroAddress, {
152
+ message: 'Sharing permit issuer :: must not be zeroAddress',
153
+ }),
154
+ recipient: z
155
+ .string()
156
+ .refine((val) => isAddress(val), {
157
+ message: 'Sharing permit recipient :: invalid address',
158
+ })
159
+ .refine((val) => val !== zeroAddress, {
160
+ message: 'Sharing permit recipient :: must not be zeroAddress',
161
+ }),
162
+ name: z.string().optional().default('Unnamed Permit'),
163
+ expiration: z.number().optional().default(1000000000000),
164
+ validatorId: z.number().optional().default(0),
165
+ validatorContract: z
166
+ .string()
167
+ .optional()
168
+ .default(zeroAddress)
169
+ .refine((val) => isAddress(val), {
170
+ message: 'Sharing permit validatorContract :: invalid address',
171
+ }),
172
+ issuerSignature: z
173
+ .string()
174
+ .optional()
175
+ .default('0x')
176
+ .refine((val) => is0xPrefixed(val), {
177
+ message: 'Sharing permit issuerSignature :: must be 0x prefixed',
178
+ }),
179
+ recipientSignature: z
180
+ .string()
181
+ .optional()
182
+ .default('0x')
183
+ .refine((val) => is0xPrefixed(val), {
184
+ message: 'Sharing permit recipientSignature :: must be 0x prefixed',
185
+ }),
186
+ })
187
+ .refine(...ValidatorContractRefinement);
188
+
189
+ /**
190
+ * Validator for fully formed sharing permits
191
+ */
192
+ export const SharingPermitValidator = zPermitWithSealingPair
193
+ .refine((data) => data.type === 'sharing', {
194
+ message: "Sharing permit :: type must be 'sharing'",
195
+ })
196
+ .refine((data) => data.recipient !== zeroAddress, {
197
+ message: 'Sharing permit :: recipient must not be zeroAddress',
198
+ })
199
+ .refine((data) => data.issuerSignature !== '0x', {
200
+ message: 'Sharing permit :: issuerSignature must be populated',
201
+ })
202
+ .refine((data) => data.recipientSignature === '0x', {
203
+ message: 'Sharing permit :: recipientSignature must be empty',
204
+ })
205
+ .refine(...ValidatorContractRefinement);
206
+
207
+ // ============================================================================
208
+ // IMPORT/RECIPIENT PERMIT VALIDATORS
209
+ // ============================================================================
210
+
211
+ /**
212
+ * Validator for import permit creation options (recipient receiving shared permit)
213
+ */
214
+ export const ImportPermitOptionsValidator = z
215
+ .object({
216
+ type: z.literal('recipient').optional().default('recipient'),
217
+ issuer: z
218
+ .string()
219
+ .refine((val) => isAddress(val), {
220
+ message: 'Import permit issuer :: invalid address',
221
+ })
222
+ .refine((val) => val !== zeroAddress, {
223
+ message: 'Import permit issuer :: must not be zeroAddress',
224
+ }),
225
+ recipient: z
226
+ .string()
227
+ .refine((val) => isAddress(val), {
228
+ message: 'Import permit recipient :: invalid address',
229
+ })
230
+ .refine((val) => val !== zeroAddress, {
231
+ message: 'Import permit recipient :: must not be zeroAddress',
232
+ }),
233
+ issuerSignature: z
234
+ .string()
235
+ .refine((val) => is0xPrefixed(val), {
236
+ message: 'Import permit issuerSignature :: must be 0x prefixed',
237
+ })
238
+ .refine((val) => val !== '0x', {
239
+ message: 'Import permit :: issuerSignature must be provided',
240
+ }),
241
+ name: z.string().optional().default('Unnamed Permit'),
242
+ expiration: z.number().optional().default(1000000000000),
243
+ validatorId: z.number().optional().default(0),
244
+ validatorContract: z
245
+ .string()
246
+ .optional()
247
+ .default(zeroAddress)
248
+ .refine((val) => isAddress(val), {
249
+ message: 'Import permit validatorContract :: invalid address',
250
+ }),
251
+ recipientSignature: z
252
+ .string()
253
+ .optional()
254
+ .default('0x')
255
+ .refine((val) => is0xPrefixed(val), {
256
+ message: 'Import permit recipientSignature :: must be 0x prefixed',
257
+ }),
258
+ })
259
+ .refine(...ValidatorContractRefinement);
260
+
261
+ /**
262
+ * Validator for fully formed import/recipient permits
263
+ */
264
+ export const ImportPermitValidator = zPermitWithSealingPair
265
+ .refine((data) => data.type === 'recipient', {
266
+ message: "Import permit :: type must be 'recipient'",
267
+ })
268
+ .refine((data) => data.recipient !== zeroAddress, {
269
+ message: 'Import permit :: recipient must not be zeroAddress',
270
+ })
271
+ .refine((data) => data.issuerSignature !== '0x', {
272
+ message: 'Import permit :: issuerSignature must be populated',
273
+ })
274
+ .refine((data) => data.recipientSignature !== '0x', {
275
+ message: 'Import permit :: recipientSignature must be populated',
276
+ })
277
+ .refine(...ValidatorContractRefinement);
278
+
279
+ // ============================================================================
280
+ // VALIDATION FUNCTIONS
281
+ // ============================================================================
282
+
283
+ /**
284
+ * Validates self permit creation options
285
+ */
286
+ export const validateSelfPermitOptions = (options: any) => SelfPermitOptionsValidator.safeParse(options);
287
+
288
+ /**
289
+ * Validates sharing permit creation options
290
+ */
291
+ export const validateSharingPermitOptions = (options: any) => SharingPermitOptionsValidator.safeParse(options);
292
+
293
+ /**
294
+ * Validates import permit creation options
295
+ */
296
+ export const validateImportPermitOptions = (options: any) => ImportPermitOptionsValidator.safeParse(options);
297
+
298
+ /**
299
+ * Validates a fully formed self permit
300
+ */
301
+ export const validateSelfPermit = (permit: any) => SelfPermitValidator.safeParse(permit);
302
+
303
+ /**
304
+ * Validates a fully formed sharing permit
305
+ */
306
+ export const validateSharingPermit = (permit: any) => SharingPermitValidator.safeParse(permit);
307
+
308
+ /**
309
+ * Validates a fully formed import/recipient permit
310
+ */
311
+ export const validateImportPermit = (permit: any) => ImportPermitValidator.safeParse(permit);
312
+
313
+ /**
314
+ * Simple validation functions for common checks
315
+ */
316
+ export const ValidationUtils = {
317
+ /**
318
+ * Check if permit is expired
319
+ */
320
+ isExpired: (permit: Permit): boolean => {
321
+ return permit.expiration < Math.floor(Date.now() / 1000);
322
+ },
323
+
324
+ /**
325
+ * Check if permit is signed by the active party
326
+ */
327
+ isSigned: (permit: Permit): boolean => {
328
+ if (permit.type === 'self' || permit.type === 'sharing') {
329
+ return permit.issuerSignature !== '0x';
330
+ }
331
+ if (permit.type === 'recipient') {
332
+ return permit.recipientSignature !== '0x';
333
+ }
334
+ return false;
335
+ },
336
+
337
+ /**
338
+ * Overall validity checker of a permit
339
+ */
340
+ isValid: (permit: Permit): ValidationResult => {
341
+ if (ValidationUtils.isExpired(permit)) {
342
+ return { valid: false, error: 'expired' };
343
+ }
344
+ if (!ValidationUtils.isSigned(permit)) {
345
+ return { valid: false, error: 'not-signed' };
346
+ }
347
+ return { valid: true, error: null };
348
+ },
349
+ };