@cofhe/sdk 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/adapters/ethers6.ts +28 -28
- package/adapters/hardhat.ts +0 -1
- package/adapters/index.test.ts +14 -19
- package/adapters/smartWallet.ts +81 -73
- package/adapters/test-utils.ts +45 -45
- package/adapters/types.ts +3 -3
- package/chains/chains/localcofhe.ts +14 -0
- package/chains/chains.test.ts +2 -1
- package/chains/index.ts +3 -1
- package/core/baseBuilder.ts +30 -49
- package/core/client.test.ts +94 -77
- package/core/client.ts +133 -149
- package/core/clientTypes.ts +108 -0
- package/core/config.test.ts +22 -11
- package/core/config.ts +16 -9
- package/core/decrypt/decryptHandleBuilder.ts +51 -45
- package/core/decrypt/{tnSealOutput.ts → tnSealOutputV1.ts} +1 -1
- package/core/decrypt/tnSealOutputV2.ts +298 -0
- package/core/encrypt/cofheMocksZkVerifySign.ts +16 -10
- package/core/encrypt/encryptInputsBuilder.test.ts +132 -116
- package/core/encrypt/encryptInputsBuilder.ts +159 -111
- package/core/encrypt/encryptUtils.ts +6 -3
- package/core/encrypt/zkPackProveVerify.ts +70 -8
- package/core/error.ts +0 -2
- package/core/fetchKeys.test.ts +1 -18
- package/core/fetchKeys.ts +0 -26
- package/core/index.ts +29 -17
- package/core/keyStore.ts +65 -38
- package/core/permits.test.ts +253 -1
- package/core/permits.ts +80 -16
- package/core/types.ts +198 -152
- package/core/utils.ts +43 -1
- package/dist/adapters.d.cts +38 -20
- package/dist/adapters.d.ts +38 -20
- package/dist/chains.cjs +14 -1
- package/dist/chains.d.cts +23 -1
- package/dist/chains.d.ts +23 -1
- package/dist/chains.js +1 -1
- package/dist/{chunk-LU7BMUUT.js → chunk-UGBVZNRT.js} +39 -25
- package/dist/{chunk-GZCQQYVI.js → chunk-WEAZ25JO.js} +14 -2
- package/dist/{chunk-KFGPTJ6X.js → chunk-WGCRJCBR.js} +1920 -1692
- package/dist/{types-bB7wLj0q.d.cts → clientTypes-5_1nwtUe.d.cts} +308 -347
- package/dist/{types-PhwGgQvs.d.ts → clientTypes-Es7fyi65.d.ts} +308 -347
- package/dist/core.cjs +2872 -2632
- package/dist/core.d.cts +101 -6
- package/dist/core.d.ts +101 -6
- package/dist/core.js +3 -3
- package/dist/node.cjs +2716 -2520
- package/dist/node.d.cts +3 -3
- package/dist/node.d.ts +3 -3
- package/dist/node.js +4 -3
- package/dist/{permit-S9CnI6MF.d.cts → permit-fUSe6KKq.d.cts} +31 -15
- package/dist/{permit-S9CnI6MF.d.ts → permit-fUSe6KKq.d.ts} +31 -15
- package/dist/permits.cjs +39 -24
- package/dist/permits.d.cts +137 -148
- package/dist/permits.d.ts +137 -148
- package/dist/permits.js +1 -1
- package/dist/web.cjs +2929 -2518
- package/dist/web.d.cts +21 -5
- package/dist/web.d.ts +21 -5
- package/dist/web.js +185 -9
- package/dist/zkProve.worker.cjs +93 -0
- package/dist/zkProve.worker.d.cts +2 -0
- package/dist/zkProve.worker.d.ts +2 -0
- package/dist/zkProve.worker.js +91 -0
- package/node/client.test.ts +20 -25
- package/node/encryptInputs.test.ts +18 -38
- package/node/index.ts +1 -0
- package/package.json +14 -14
- package/permits/index.ts +1 -0
- package/permits/localstorage.test.ts +0 -1
- package/permits/permit.test.ts +25 -22
- package/permits/permit.ts +30 -21
- package/permits/sealing.test.ts +3 -3
- package/permits/sealing.ts +2 -2
- package/permits/store.ts +5 -7
- package/permits/test-utils.ts +1 -1
- package/permits/types.ts +17 -0
- package/permits/utils.ts +0 -1
- package/permits/validation.ts +24 -4
- package/web/client.web.test.ts +20 -25
- package/web/config.web.test.ts +0 -2
- package/web/encryptInputs.web.test.ts +31 -54
- package/web/index.ts +65 -1
- package/web/storage.ts +19 -5
- package/web/worker.builder.web.test.ts +148 -0
- package/web/worker.config.web.test.ts +329 -0
- package/web/worker.output.web.test.ts +84 -0
- package/web/workerManager.test.ts +80 -0
- package/web/workerManager.ts +214 -0
- package/web/workerManager.web.test.ts +114 -0
- package/web/zkProve.worker.ts +133 -0
- package/core/result.test.ts +0 -180
- package/core/result.ts +0 -67
- package/core/test-utils.ts +0 -45
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Encryptable, FheTypes, type CofhesdkClient,
|
|
1
|
+
import { Encryptable, FheTypes, type CofhesdkClient, CofhesdkErrorCode, CofhesdkError } from '@/core';
|
|
2
2
|
import { arbSepolia as cofhesdkArbSepolia } from '@/chains';
|
|
3
3
|
|
|
4
4
|
import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
|
|
@@ -11,22 +11,6 @@ import { createCofhesdkClient, createCofhesdkConfig } from './index.js';
|
|
|
11
11
|
// Real test setup - using actual node-tfhe
|
|
12
12
|
const TEST_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
|
|
13
13
|
|
|
14
|
-
const expectResultSuccess = <T>(result: Result<T>): T => {
|
|
15
|
-
expect(result.success, `Result error: ${result.error?.toString()}`).toBe(true);
|
|
16
|
-
return result.data!;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const expectResultError = <T>(result: Result<T>, errorCode?: CofhesdkErrorCode): void => {
|
|
20
|
-
expect(result.success).toBe(false);
|
|
21
|
-
expect(result.data).toBe(null);
|
|
22
|
-
expect(result.error).not.toBe(null);
|
|
23
|
-
const error = result.error as CofhesdkError;
|
|
24
|
-
expect(error).toBeInstanceOf(CofhesdkError);
|
|
25
|
-
if (errorCode) {
|
|
26
|
-
expect(error.code, `Result error: ${result.error?.toString()}`).toBe(errorCode);
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
14
|
describe('@cofhe/node - Encrypt Inputs', () => {
|
|
31
15
|
let cofhesdkClient: CofhesdkClient;
|
|
32
16
|
let publicClient: PublicClient;
|
|
@@ -59,8 +43,7 @@ describe('@cofhe/node - Encrypt Inputs', () => {
|
|
|
59
43
|
await cofhesdkClient.connect(publicClient, walletClient);
|
|
60
44
|
|
|
61
45
|
// This will trigger real TFHE initialization
|
|
62
|
-
const
|
|
63
|
-
const encrypted = expectResultSuccess(result);
|
|
46
|
+
const encrypted = await cofhesdkClient.encryptInputs([Encryptable.uint128(100n)]).encrypt();
|
|
64
47
|
|
|
65
48
|
// If we get here, TFHE was initialized successfully
|
|
66
49
|
expect(encrypted).toBeDefined();
|
|
@@ -70,12 +53,10 @@ describe('@cofhe/node - Encrypt Inputs', () => {
|
|
|
70
53
|
await cofhesdkClient.connect(publicClient, walletClient);
|
|
71
54
|
|
|
72
55
|
// First encryption
|
|
73
|
-
|
|
74
|
-
expectResultSuccess(result1);
|
|
56
|
+
await cofhesdkClient.encryptInputs([Encryptable.uint128(100n)]).encrypt();
|
|
75
57
|
|
|
76
58
|
// Second encryption should reuse initialization
|
|
77
|
-
|
|
78
|
-
expectResultSuccess(result2);
|
|
59
|
+
await cofhesdkClient.encryptInputs([Encryptable.uint64(50n)]).encrypt();
|
|
79
60
|
}, 120000);
|
|
80
61
|
});
|
|
81
62
|
|
|
@@ -83,8 +64,7 @@ describe('@cofhe/node - Encrypt Inputs', () => {
|
|
|
83
64
|
it('should encrypt a bool with real TFHE', async () => {
|
|
84
65
|
await cofhesdkClient.connect(publicClient, walletClient);
|
|
85
66
|
|
|
86
|
-
const
|
|
87
|
-
const encrypted = expectResultSuccess(result);
|
|
67
|
+
const encrypted = await cofhesdkClient.encryptInputs([Encryptable.bool(true)]).encrypt();
|
|
88
68
|
|
|
89
69
|
expect(encrypted.length).toBe(1);
|
|
90
70
|
expect(encrypted[0].utype).toBe(FheTypes.Bool);
|
|
@@ -108,8 +88,7 @@ describe('@cofhe/node - Encrypt Inputs', () => {
|
|
|
108
88
|
Encryptable.address('0x742d35Cc6634C0532925a3b844D16faC4c175E99'),
|
|
109
89
|
];
|
|
110
90
|
|
|
111
|
-
const
|
|
112
|
-
const encrypted = expectResultSuccess(result);
|
|
91
|
+
const encrypted = await cofhesdkClient.encryptInputs(inputs).encrypt();
|
|
113
92
|
|
|
114
93
|
expect(encrypted.length).toBe(7);
|
|
115
94
|
// Verify each type
|
|
@@ -128,14 +107,13 @@ describe('@cofhe/node - Encrypt Inputs', () => {
|
|
|
128
107
|
await cofhesdkClient.connect(publicClient, walletClient);
|
|
129
108
|
|
|
130
109
|
const snapshot = cofhesdkClient.getSnapshot();
|
|
131
|
-
const
|
|
110
|
+
const encrypted = await cofhesdkClient
|
|
132
111
|
.encryptInputs([Encryptable.uint128(100n)])
|
|
133
112
|
.setChainId(snapshot.chainId!)
|
|
134
113
|
.setAccount(snapshot.account!)
|
|
135
114
|
.setSecurityZone(0)
|
|
136
115
|
.encrypt();
|
|
137
116
|
|
|
138
|
-
const encrypted = expectResultSuccess(result);
|
|
139
117
|
expect(encrypted.length).toBe(1);
|
|
140
118
|
expect(encrypted[0].utype).toBe(FheTypes.Uint128);
|
|
141
119
|
}, 60000);
|
|
@@ -144,9 +122,12 @@ describe('@cofhe/node - Encrypt Inputs', () => {
|
|
|
144
122
|
describe('Real Error Handling', () => {
|
|
145
123
|
it('should fail gracefully when not connected', async () => {
|
|
146
124
|
// Don't connect the client
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
125
|
+
try {
|
|
126
|
+
await cofhesdkClient.encryptInputs([Encryptable.uint128(100n)]).encrypt();
|
|
127
|
+
} catch (error) {
|
|
128
|
+
expect(error).toBeInstanceOf(CofhesdkError);
|
|
129
|
+
expect((error as CofhesdkError).code).toBe(CofhesdkErrorCode.NotConnected);
|
|
130
|
+
}
|
|
150
131
|
}, 30000);
|
|
151
132
|
|
|
152
133
|
it('should handle invalid CoFHE URL', async () => {
|
|
@@ -158,17 +139,16 @@ describe('@cofhe/node - Encrypt Inputs', () => {
|
|
|
158
139
|
verifierUrl: 'http://invalid-verifier-url.local',
|
|
159
140
|
},
|
|
160
141
|
],
|
|
161
|
-
fheKeysPrefetching: 'OFF',
|
|
162
142
|
});
|
|
163
143
|
|
|
164
144
|
const badClient = createCofhesdkClient(badConfig);
|
|
165
145
|
await badClient.connect(publicClient, walletClient);
|
|
166
146
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
expect(
|
|
147
|
+
try {
|
|
148
|
+
await badClient.encryptInputs([Encryptable.uint128(100n)]).encrypt();
|
|
149
|
+
} catch (error) {
|
|
150
|
+
expect(error).toBeInstanceOf(CofhesdkError);
|
|
151
|
+
expect((error as CofhesdkError).code).toBe(CofhesdkErrorCode.ZkVerifyFailed);
|
|
172
152
|
}
|
|
173
153
|
}, 60000);
|
|
174
154
|
});
|
package/node/index.ts
CHANGED
|
@@ -74,6 +74,7 @@ const zkBuilderAndCrsGenerator: ZkBuilderAndCrsGenerator = (fhe: string, crs: st
|
|
|
74
74
|
*/
|
|
75
75
|
export function createCofhesdkConfig(config: CofhesdkInputConfig): CofhesdkConfig {
|
|
76
76
|
return createCofhesdkConfigBase({
|
|
77
|
+
environment: 'node',
|
|
77
78
|
...config,
|
|
78
79
|
fheKeyStorage: config.fheKeyStorage === null ? null : config.fheKeyStorage ?? createNodeStorage(),
|
|
79
80
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cofhe/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SDK for Fhenix COFHE coprocessor interaction",
|
|
6
6
|
"main": "./dist/core.cjs",
|
|
@@ -57,21 +57,21 @@
|
|
|
57
57
|
"CHANGELOG.md"
|
|
58
58
|
],
|
|
59
59
|
"dependencies": {
|
|
60
|
+
"iframe-shared-storage": "^1.0.34",
|
|
60
61
|
"immer": "^10.1.1",
|
|
61
|
-
"zustand": "^5.0.1",
|
|
62
|
-
"zod": "^3.22.0",
|
|
63
|
-
"viem": "^2.0.0",
|
|
64
62
|
"node-tfhe": "0.11.1",
|
|
65
|
-
"idb-keyval": "^6.2.1",
|
|
66
63
|
"tfhe": "0.11.1",
|
|
67
|
-
"tweetnacl": "^1.0.3"
|
|
64
|
+
"tweetnacl": "^1.0.3",
|
|
65
|
+
"viem": "^2.38.6",
|
|
66
|
+
"zod": "^3.22.0",
|
|
67
|
+
"zustand": "^5.0.1"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
|
-
"
|
|
70
|
+
"@nomicfoundation/hardhat-ethers": "^3.0.0",
|
|
71
71
|
"@wagmi/core": "^2.0.0",
|
|
72
72
|
"ethers": "^5.0.0 || ^6.0.0",
|
|
73
73
|
"hardhat": "^2.0.0",
|
|
74
|
-
"
|
|
74
|
+
"viem": "^2.38.6"
|
|
75
75
|
},
|
|
76
76
|
"peerDependenciesMeta": {
|
|
77
77
|
"@wagmi/core": {
|
|
@@ -88,21 +88,21 @@
|
|
|
88
88
|
}
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
91
|
+
"@nomicfoundation/hardhat-ethers": "^3.0.0",
|
|
91
92
|
"@types/node": "^20.0.0",
|
|
92
93
|
"@vitest/browser": "^3.0.0",
|
|
93
94
|
"@vitest/coverage-v8": "^3.0.0",
|
|
94
95
|
"eslint": "^8.57.0",
|
|
96
|
+
"ethers5": "npm:ethers@^5.7.2",
|
|
97
|
+
"ethers6": "npm:ethers@^6.13.0",
|
|
95
98
|
"happy-dom": "^15.0.0",
|
|
99
|
+
"hardhat": "^2.19.0",
|
|
96
100
|
"playwright": "^1.55.0",
|
|
97
101
|
"tsup": "^8.0.2",
|
|
98
102
|
"typescript": "5.5.4",
|
|
99
103
|
"vitest": "^3.0.0",
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"hardhat": "^2.19.0",
|
|
103
|
-
"@nomicfoundation/hardhat-ethers": "^3.0.0",
|
|
104
|
-
"@cofhe/eslint-config": "0.1.1",
|
|
105
|
-
"@cofhe/tsconfig": "0.1.0"
|
|
104
|
+
"@cofhe/eslint-config": "0.2.0",
|
|
105
|
+
"@cofhe/tsconfig": "0.1.1"
|
|
106
106
|
},
|
|
107
107
|
"publishConfig": {
|
|
108
108
|
"access": "public"
|
package/permits/index.ts
CHANGED
package/permits/permit.test.ts
CHANGED
|
@@ -44,7 +44,7 @@ describe('PermitUtils Tests', () => {
|
|
|
44
44
|
name: 'Test Permit',
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
const permit =
|
|
47
|
+
const permit = PermitUtils.createSelf(options);
|
|
48
48
|
|
|
49
49
|
expect(permit.type).toBe('self');
|
|
50
50
|
expect(permit.name).toBe('Test Permit');
|
|
@@ -66,7 +66,7 @@ describe('PermitUtils Tests', () => {
|
|
|
66
66
|
name: 'Test Permit',
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
expect(() => PermitUtils.createSelf(options)).toThrowError();
|
|
70
70
|
});
|
|
71
71
|
});
|
|
72
72
|
|
|
@@ -79,7 +79,7 @@ describe('PermitUtils Tests', () => {
|
|
|
79
79
|
name: 'Test Sharing Permit',
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
-
const permit =
|
|
82
|
+
const permit = PermitUtils.createSharing(options);
|
|
83
83
|
|
|
84
84
|
expect(permit.type).toBe('sharing');
|
|
85
85
|
expect(permit.name).toBe('Test Sharing Permit');
|
|
@@ -103,7 +103,7 @@ describe('PermitUtils Tests', () => {
|
|
|
103
103
|
name: 'Test Sharing Permit',
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
expect(() => PermitUtils.createSharing(options)).toThrow();
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
109
|
|
|
@@ -116,7 +116,7 @@ describe('PermitUtils Tests', () => {
|
|
|
116
116
|
name: 'Test Import Permit',
|
|
117
117
|
};
|
|
118
118
|
|
|
119
|
-
const permit =
|
|
119
|
+
const permit = PermitUtils.importShared(options);
|
|
120
120
|
|
|
121
121
|
expect(permit.type).toBe('recipient');
|
|
122
122
|
expect(permit.name).toBe('Test Import Permit');
|
|
@@ -140,7 +140,7 @@ describe('PermitUtils Tests', () => {
|
|
|
140
140
|
|
|
141
141
|
const stringOptions = JSON.stringify(options);
|
|
142
142
|
|
|
143
|
-
const permit =
|
|
143
|
+
const permit = PermitUtils.importShared(stringOptions);
|
|
144
144
|
|
|
145
145
|
expect(permit.type).toBe('recipient');
|
|
146
146
|
});
|
|
@@ -153,7 +153,7 @@ describe('PermitUtils Tests', () => {
|
|
|
153
153
|
issuerSignature: '0x1234567890abcdef',
|
|
154
154
|
} as unknown as ImportSharedPermitOptions;
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
expect(() => PermitUtils.importShared(options)).toThrow();
|
|
157
157
|
|
|
158
158
|
const options2 = {
|
|
159
159
|
type: 'recipient',
|
|
@@ -162,7 +162,7 @@ describe('PermitUtils Tests', () => {
|
|
|
162
162
|
issuerSignature: '0x1234567890abcdef',
|
|
163
163
|
} as unknown as ImportSharedPermitOptions;
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
expect(() => PermitUtils.importShared(options2)).toThrow();
|
|
166
166
|
});
|
|
167
167
|
|
|
168
168
|
it('should throw error for missing issuerSignature', async () => {
|
|
@@ -173,7 +173,7 @@ describe('PermitUtils Tests', () => {
|
|
|
173
173
|
name: 'Test Import Permit',
|
|
174
174
|
};
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
expect(() => PermitUtils.importShared(options)).toThrow();
|
|
177
177
|
});
|
|
178
178
|
});
|
|
179
179
|
|
|
@@ -266,7 +266,7 @@ describe('PermitUtils Tests', () => {
|
|
|
266
266
|
|
|
267
267
|
describe('sign', () => {
|
|
268
268
|
it('should sign a self permit', async () => {
|
|
269
|
-
const permit =
|
|
269
|
+
const permit = PermitUtils.createSelf({
|
|
270
270
|
issuer: bobAddress,
|
|
271
271
|
name: 'Test Permit',
|
|
272
272
|
});
|
|
@@ -280,7 +280,7 @@ describe('PermitUtils Tests', () => {
|
|
|
280
280
|
});
|
|
281
281
|
|
|
282
282
|
it('should sign a recipient permit', async () => {
|
|
283
|
-
const permit =
|
|
283
|
+
const permit = PermitUtils.importShared({
|
|
284
284
|
issuer: bobAddress,
|
|
285
285
|
recipient: aliceAddress,
|
|
286
286
|
issuerSignature: '0xexisting-signature',
|
|
@@ -295,7 +295,7 @@ describe('PermitUtils Tests', () => {
|
|
|
295
295
|
});
|
|
296
296
|
|
|
297
297
|
it('should throw error for undefined signer', async () => {
|
|
298
|
-
const permit =
|
|
298
|
+
const permit = PermitUtils.createSelf({
|
|
299
299
|
issuer: bobAddress,
|
|
300
300
|
name: 'Test Permit',
|
|
301
301
|
});
|
|
@@ -309,7 +309,7 @@ describe('PermitUtils Tests', () => {
|
|
|
309
309
|
|
|
310
310
|
describe('serialize/deserialize', () => {
|
|
311
311
|
it('should serialize and deserialize a permit', async () => {
|
|
312
|
-
const originalPermit =
|
|
312
|
+
const originalPermit = PermitUtils.createSelf({
|
|
313
313
|
issuer: bobAddress,
|
|
314
314
|
name: 'Test Permit',
|
|
315
315
|
});
|
|
@@ -348,12 +348,15 @@ describe('PermitUtils Tests', () => {
|
|
|
348
348
|
|
|
349
349
|
describe('getHash', () => {
|
|
350
350
|
it('should generate consistent hash for same permit data', async () => {
|
|
351
|
-
const
|
|
351
|
+
const expiration = Math.floor(Date.now() / 1000) + 3600; // 1 hour from now
|
|
352
|
+
const permit1 = PermitUtils.createSelf({
|
|
353
|
+
expiration,
|
|
352
354
|
issuer: bobAddress,
|
|
353
355
|
name: 'Test Permit',
|
|
354
356
|
});
|
|
355
357
|
|
|
356
|
-
const permit2 =
|
|
358
|
+
const permit2 = PermitUtils.createSelf({
|
|
359
|
+
expiration,
|
|
357
360
|
issuer: bobAddress,
|
|
358
361
|
name: 'Test Permit',
|
|
359
362
|
});
|
|
@@ -367,7 +370,7 @@ describe('PermitUtils Tests', () => {
|
|
|
367
370
|
|
|
368
371
|
describe('export', () => {
|
|
369
372
|
it('should export permit data without sensitive fields', async () => {
|
|
370
|
-
const permit =
|
|
373
|
+
const permit = PermitUtils.createSelf({
|
|
371
374
|
issuer: bobAddress,
|
|
372
375
|
name: 'Test Permit',
|
|
373
376
|
});
|
|
@@ -384,7 +387,7 @@ describe('PermitUtils Tests', () => {
|
|
|
384
387
|
|
|
385
388
|
describe('updateName', () => {
|
|
386
389
|
it('should update permit name immutably', async () => {
|
|
387
|
-
const permit =
|
|
390
|
+
const permit = PermitUtils.createSelf({
|
|
388
391
|
issuer: bobAddress,
|
|
389
392
|
name: 'Original Name',
|
|
390
393
|
});
|
|
@@ -399,13 +402,13 @@ describe('PermitUtils Tests', () => {
|
|
|
399
402
|
|
|
400
403
|
describe('validation helpers', () => {
|
|
401
404
|
it('should check if permit is expired', async () => {
|
|
402
|
-
const expiredPermit =
|
|
405
|
+
const expiredPermit = PermitUtils.createSelf({
|
|
403
406
|
issuer: bobAddress,
|
|
404
407
|
name: 'Test Permit',
|
|
405
408
|
expiration: Math.floor(Date.now() / 1000) - 3600, // 1 hour ago
|
|
406
409
|
});
|
|
407
410
|
|
|
408
|
-
const validPermit =
|
|
411
|
+
const validPermit = PermitUtils.createSelf({
|
|
409
412
|
issuer: bobAddress,
|
|
410
413
|
name: 'Test Permit',
|
|
411
414
|
expiration: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now
|
|
@@ -416,7 +419,7 @@ describe('PermitUtils Tests', () => {
|
|
|
416
419
|
});
|
|
417
420
|
|
|
418
421
|
it('should check if permit is signed', async () => {
|
|
419
|
-
const unsignedPermit =
|
|
422
|
+
const unsignedPermit = PermitUtils.createSelf({
|
|
420
423
|
issuer: bobAddress,
|
|
421
424
|
name: 'Test Permit',
|
|
422
425
|
});
|
|
@@ -428,7 +431,7 @@ describe('PermitUtils Tests', () => {
|
|
|
428
431
|
});
|
|
429
432
|
|
|
430
433
|
it('should check overall validity', async () => {
|
|
431
|
-
const validPermit =
|
|
434
|
+
const validPermit = PermitUtils.createSelf({
|
|
432
435
|
issuer: bobAddress,
|
|
433
436
|
name: 'Test Permit',
|
|
434
437
|
expiration: Math.floor(Date.now() / 1000) + 3600,
|
|
@@ -456,7 +459,7 @@ describe('PermitUtils Tests', () => {
|
|
|
456
459
|
}, 10000); // 10 second timeout for network call
|
|
457
460
|
|
|
458
461
|
it('should check signed domain validity with real contract data', async () => {
|
|
459
|
-
const permit =
|
|
462
|
+
const permit = PermitUtils.createSelf({
|
|
460
463
|
type: 'self',
|
|
461
464
|
issuer: bobAddress,
|
|
462
465
|
name: 'Test Permit',
|
package/permits/permit.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { keccak256, toHex, zeroAddress, parseAbi, type PublicClient, type WalletClient } from 'viem';
|
|
2
2
|
import {
|
|
3
3
|
type Permit,
|
|
4
|
+
type SelfPermit,
|
|
5
|
+
type SharingPermit,
|
|
6
|
+
type RecipientPermit,
|
|
4
7
|
type CreateSelfPermitOptions,
|
|
5
8
|
type CreateSharingPermitOptions,
|
|
6
9
|
type ImportSharedPermitOptions,
|
|
@@ -28,7 +31,7 @@ export const PermitUtils = {
|
|
|
28
31
|
/**
|
|
29
32
|
* Create a self permit for personal use
|
|
30
33
|
*/
|
|
31
|
-
createSelf:
|
|
34
|
+
createSelf: (options: CreateSelfPermitOptions): SelfPermit => {
|
|
32
35
|
const validation = validateSelfPermitOptions(options);
|
|
33
36
|
|
|
34
37
|
if (!validation.success) {
|
|
@@ -38,19 +41,21 @@ export const PermitUtils = {
|
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
// Always generate a new sealing key - users cannot provide their own
|
|
41
|
-
const sealingPair =
|
|
44
|
+
const sealingPair = GenerateSealingKey();
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
const permit = {
|
|
44
47
|
...validation.data,
|
|
45
48
|
sealingPair,
|
|
46
49
|
_signedDomain: undefined,
|
|
47
|
-
};
|
|
50
|
+
} satisfies SelfPermit;
|
|
51
|
+
|
|
52
|
+
return permit;
|
|
48
53
|
},
|
|
49
54
|
|
|
50
55
|
/**
|
|
51
56
|
* Create a sharing permit to be shared with another user
|
|
52
57
|
*/
|
|
53
|
-
createSharing:
|
|
58
|
+
createSharing: (options: CreateSharingPermitOptions): SharingPermit => {
|
|
54
59
|
const validation = validateSharingPermitOptions(options);
|
|
55
60
|
|
|
56
61
|
if (!validation.success) {
|
|
@@ -61,19 +66,21 @@ export const PermitUtils = {
|
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
// Always generate a new sealing key - users cannot provide their own
|
|
64
|
-
const sealingPair =
|
|
69
|
+
const sealingPair = GenerateSealingKey();
|
|
65
70
|
|
|
66
|
-
|
|
71
|
+
const permit = {
|
|
67
72
|
...validation.data,
|
|
68
73
|
sealingPair,
|
|
69
74
|
_signedDomain: undefined,
|
|
70
|
-
};
|
|
75
|
+
} satisfies SharingPermit;
|
|
76
|
+
|
|
77
|
+
return permit;
|
|
71
78
|
},
|
|
72
79
|
|
|
73
80
|
/**
|
|
74
81
|
* Import a shared permit from various input formats
|
|
75
82
|
*/
|
|
76
|
-
importShared:
|
|
83
|
+
importShared: (options: ImportSharedPermitOptions | string): RecipientPermit => {
|
|
77
84
|
let parsedOptions: ImportSharedPermitOptions;
|
|
78
85
|
|
|
79
86
|
// Handle different input types
|
|
@@ -107,19 +114,21 @@ export const PermitUtils = {
|
|
|
107
114
|
}
|
|
108
115
|
|
|
109
116
|
// Always generate a new sealing key - users cannot provide their own
|
|
110
|
-
const sealingPair =
|
|
117
|
+
const sealingPair = GenerateSealingKey();
|
|
111
118
|
|
|
112
|
-
|
|
119
|
+
const permit = {
|
|
113
120
|
...validation.data,
|
|
114
121
|
sealingPair,
|
|
115
122
|
_signedDomain: undefined,
|
|
116
|
-
};
|
|
123
|
+
} satisfies RecipientPermit;
|
|
124
|
+
|
|
125
|
+
return permit;
|
|
117
126
|
},
|
|
118
127
|
|
|
119
128
|
/**
|
|
120
129
|
* Sign a permit with the provided wallet client
|
|
121
130
|
*/
|
|
122
|
-
sign: async (permit:
|
|
131
|
+
sign: async <T extends Permit>(permit: T, publicClient: PublicClient, walletClient: WalletClient): Promise<T> => {
|
|
123
132
|
if (walletClient == null || walletClient.account == null) {
|
|
124
133
|
throw new Error(
|
|
125
134
|
'PermitUtils :: sign - walletClient undefined, you must pass in a `walletClient` for the connected user to create a permit signature'
|
|
@@ -153,7 +162,7 @@ export const PermitUtils = {
|
|
|
153
162
|
};
|
|
154
163
|
}
|
|
155
164
|
|
|
156
|
-
return updatedPermit;
|
|
165
|
+
return updatedPermit as T;
|
|
157
166
|
},
|
|
158
167
|
|
|
159
168
|
/**
|
|
@@ -163,8 +172,8 @@ export const PermitUtils = {
|
|
|
163
172
|
options: CreateSelfPermitOptions,
|
|
164
173
|
publicClient: PublicClient,
|
|
165
174
|
walletClient: WalletClient
|
|
166
|
-
): Promise<
|
|
167
|
-
const permit =
|
|
175
|
+
): Promise<SelfPermit> => {
|
|
176
|
+
const permit = PermitUtils.createSelf(options);
|
|
168
177
|
return PermitUtils.sign(permit, publicClient, walletClient);
|
|
169
178
|
},
|
|
170
179
|
|
|
@@ -175,8 +184,8 @@ export const PermitUtils = {
|
|
|
175
184
|
options: CreateSharingPermitOptions,
|
|
176
185
|
publicClient: PublicClient,
|
|
177
186
|
walletClient: WalletClient
|
|
178
|
-
): Promise<
|
|
179
|
-
const permit =
|
|
187
|
+
): Promise<SharingPermit> => {
|
|
188
|
+
const permit = PermitUtils.createSharing(options);
|
|
180
189
|
return PermitUtils.sign(permit, publicClient, walletClient);
|
|
181
190
|
},
|
|
182
191
|
|
|
@@ -184,11 +193,11 @@ export const PermitUtils = {
|
|
|
184
193
|
* Import and sign a shared permit in one operation from various input formats
|
|
185
194
|
*/
|
|
186
195
|
importSharedAndSign: async (
|
|
187
|
-
options: ImportSharedPermitOptions |
|
|
196
|
+
options: ImportSharedPermitOptions | string,
|
|
188
197
|
publicClient: PublicClient,
|
|
189
198
|
walletClient: WalletClient
|
|
190
|
-
): Promise<
|
|
191
|
-
const permit =
|
|
199
|
+
): Promise<RecipientPermit> => {
|
|
200
|
+
const permit = PermitUtils.importShared(options);
|
|
192
201
|
return PermitUtils.sign(permit, publicClient, walletClient);
|
|
193
202
|
},
|
|
194
203
|
|
package/permits/sealing.test.ts
CHANGED
|
@@ -67,7 +67,7 @@ describe('SealingKey', () => {
|
|
|
67
67
|
|
|
68
68
|
describe('GenerateSealingKey', () => {
|
|
69
69
|
it('should generate a valid SealingKey', async () => {
|
|
70
|
-
const sealingKey =
|
|
70
|
+
const sealingKey = GenerateSealingKey();
|
|
71
71
|
|
|
72
72
|
expect(sealingKey).toBeInstanceOf(SealingKey);
|
|
73
73
|
expect(sealingKey.privateKey).toHaveLength(64);
|
|
@@ -75,8 +75,8 @@ describe('GenerateSealingKey', () => {
|
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
it('should generate different keys on each call', async () => {
|
|
78
|
-
const key1 =
|
|
79
|
-
const key2 =
|
|
78
|
+
const key1 = GenerateSealingKey();
|
|
79
|
+
const key2 = GenerateSealingKey();
|
|
80
80
|
|
|
81
81
|
expect(key1.privateKey).not.toBe(key2.privateKey);
|
|
82
82
|
expect(key1.publicKey).not.toBe(key2.publicKey);
|
package/permits/sealing.ts
CHANGED
|
@@ -122,9 +122,9 @@ export class SealingKey {
|
|
|
122
122
|
* Asynchronously generates a new SealingKey.
|
|
123
123
|
* This function uses the 'nacl' library to create a new public/private key pair for sealing purposes.
|
|
124
124
|
* A sealing key is used to encrypt data such that it can only be unsealed (decrypted) by the owner of the corresponding private key.
|
|
125
|
-
* @returns {
|
|
125
|
+
* @returns {SealingKey} - A new SealingKey object containing the hexadecimal strings of the public and private keys.
|
|
126
126
|
*/
|
|
127
|
-
export const GenerateSealingKey =
|
|
127
|
+
export const GenerateSealingKey = (): SealingKey => {
|
|
128
128
|
const sodiumKeypair = nacl.box.keyPair();
|
|
129
129
|
|
|
130
130
|
return new SealingKey(toHexString(sodiumKeypair.secretKey), toHexString(sodiumKeypair.publicKey));
|
package/permits/store.ts
CHANGED
|
@@ -15,14 +15,12 @@ type PermitsStore = {
|
|
|
15
15
|
|
|
16
16
|
// Stores generated permits for each user, a hash indicating the active permit for each user
|
|
17
17
|
// Can be used to create reactive hooks
|
|
18
|
+
export const PERMIT_STORE_DEFAULTS: PermitsStore = {
|
|
19
|
+
permits: {},
|
|
20
|
+
activePermitHash: {},
|
|
21
|
+
};
|
|
18
22
|
export const _permitStore = createStore<PermitsStore>()(
|
|
19
|
-
persist(
|
|
20
|
-
() => ({
|
|
21
|
-
permits: {},
|
|
22
|
-
activePermitHash: {},
|
|
23
|
-
}),
|
|
24
|
-
{ name: 'cofhesdk-permits' }
|
|
25
|
-
)
|
|
23
|
+
persist(() => PERMIT_STORE_DEFAULTS, { name: 'cofhesdk-permits' })
|
|
26
24
|
);
|
|
27
25
|
|
|
28
26
|
export const clearStaleStore = () => {
|
package/permits/test-utils.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type Permit, type SerializedPermit, GenerateSealingKey, PermitUtils } f
|
|
|
2
2
|
|
|
3
3
|
// Mock permit for testing - using Bob's address as issuer
|
|
4
4
|
export const createMockPermit = async (): Promise<Permit> => {
|
|
5
|
-
const sealingPair =
|
|
5
|
+
const sealingPair = GenerateSealingKey();
|
|
6
6
|
const serializedPermit: SerializedPermit = {
|
|
7
7
|
name: 'Test Permit',
|
|
8
8
|
type: 'self',
|
package/permits/types.ts
CHANGED
|
@@ -89,6 +89,23 @@ export interface Permit {
|
|
|
89
89
|
_signedDomain?: EIP712Domain;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Permit discriminant helpers
|
|
94
|
+
*/
|
|
95
|
+
export type PermitType = Permit['type'];
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Utility type to narrow a permit to a specific discriminant.
|
|
99
|
+
*
|
|
100
|
+
* Note: this only narrows the `type` field. Runtime/validation constraints
|
|
101
|
+
* (e.g. recipient == zeroAddress for self permits) are enforced elsewhere.
|
|
102
|
+
*/
|
|
103
|
+
export type PermitOf<T extends PermitType> = Expand<Omit<Permit, 'type'> & { type: T }>;
|
|
104
|
+
|
|
105
|
+
export type SelfPermit = PermitOf<'self'>;
|
|
106
|
+
export type SharingPermit = PermitOf<'sharing'>;
|
|
107
|
+
export type RecipientPermit = PermitOf<'recipient'>;
|
|
108
|
+
|
|
92
109
|
/**
|
|
93
110
|
* Optional additional metadata of a Permit
|
|
94
111
|
* Can be passed into the constructor, but not necessary
|