@cofhe/sdk 0.1.1 → 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.
- package/CHANGELOG.md +22 -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/defineChain.ts +2 -2
- package/chains/index.ts +3 -1
- package/chains/types.ts +3 -3
- package/core/baseBuilder.ts +30 -49
- package/core/client.test.ts +200 -72
- package/core/client.ts +152 -148
- package/core/clientTypes.ts +114 -0
- package/core/config.test.ts +30 -11
- package/core/config.ts +26 -13
- package/core/consts.ts +18 -0
- package/core/decrypt/cofheMocksSealOutput.ts +2 -4
- 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 +15 -16
- 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 +37 -17
- package/core/keyStore.ts +65 -38
- package/core/permits.test.ts +255 -4
- package/core/permits.ts +83 -18
- 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 +18 -8
- package/dist/chains.d.cts +31 -9
- package/dist/chains.d.ts +31 -9
- package/dist/chains.js +1 -1
- package/dist/{chunk-KFGPTJ6X.js → chunk-I5WFEYXX.js} +1768 -1526
- package/dist/{chunk-LU7BMUUT.js → chunk-R3B5TMVX.js} +330 -197
- package/dist/{chunk-GZCQQYVI.js → chunk-TBLR7NNE.js} +18 -9
- package/dist/{types-PhwGgQvs.d.ts → clientTypes-RqkgkV2i.d.ts} +331 -429
- package/dist/{types-bB7wLj0q.d.cts → clientTypes-e4filDzK.d.cts} +331 -429
- package/dist/core.cjs +3000 -2625
- package/dist/core.d.cts +113 -7
- package/dist/core.d.ts +113 -7
- package/dist/core.js +3 -3
- package/dist/node.cjs +2851 -2526
- package/dist/node.d.cts +4 -4
- package/dist/node.d.ts +4 -4
- package/dist/node.js +4 -3
- package/dist/{permit-S9CnI6MF.d.cts → permit-MZ502UBl.d.cts} +54 -41
- package/dist/{permit-S9CnI6MF.d.ts → permit-MZ502UBl.d.ts} +54 -41
- package/dist/permits.cjs +328 -195
- package/dist/permits.d.cts +113 -825
- package/dist/permits.d.ts +113 -825
- package/dist/permits.js +1 -1
- package/dist/types-YiAC4gig.d.cts +33 -0
- package/dist/types-YiAC4gig.d.ts +33 -0
- package/dist/web.cjs +3067 -2527
- package/dist/web.d.cts +22 -6
- package/dist/web.d.ts +22 -6
- 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 +15 -15
- package/permits/index.ts +1 -0
- package/permits/localstorage.test.ts +9 -14
- package/permits/onchain-utils.ts +221 -0
- package/permits/permit.test.ts +76 -27
- package/permits/permit.ts +58 -95
- package/permits/sealing.test.ts +3 -3
- package/permits/sealing.ts +2 -2
- package/permits/store.test.ts +10 -50
- package/permits/store.ts +9 -21
- package/permits/test-utils.ts +11 -3
- package/permits/types.ts +39 -9
- package/permits/utils.ts +0 -5
- package/permits/validation.test.ts +29 -32
- package/permits/validation.ts +114 -176
- 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
- package/dist/types-KImPrEIe.d.cts +0 -48
- package/dist/types-KImPrEIe.d.ts +0 -48
package/core/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Client (base implementations)
|
|
2
|
-
export { createCofhesdkClientBase } from './client.js';
|
|
2
|
+
export { createCofhesdkClientBase, InitialConnectStore as CONNECT_STORE_DEFAULTS } from './client.js';
|
|
3
3
|
|
|
4
4
|
// Configuration (base implementations)
|
|
5
5
|
export { createCofhesdkConfigBase, getCofhesdkConfigItem } from './config.js';
|
|
@@ -12,6 +12,9 @@ export type {
|
|
|
12
12
|
CofhesdkClientParams,
|
|
13
13
|
CofhesdkClientConnectionState,
|
|
14
14
|
CofhesdkClientPermits,
|
|
15
|
+
} from './clientTypes.js';
|
|
16
|
+
|
|
17
|
+
export type {
|
|
15
18
|
IStorage,
|
|
16
19
|
// Primitive types
|
|
17
20
|
Primitive,
|
|
@@ -34,35 +37,33 @@ export type {
|
|
|
34
37
|
EncryptedUint32Input,
|
|
35
38
|
EncryptedUint64Input,
|
|
36
39
|
EncryptedUint128Input,
|
|
37
|
-
EncryptedUint256Input,
|
|
38
40
|
EncryptedAddressInput,
|
|
39
41
|
EncryptedItemInputs,
|
|
40
42
|
EncryptableToEncryptedItemInputMap,
|
|
43
|
+
FheTypeValue,
|
|
41
44
|
// Decryption types
|
|
42
45
|
UnsealedItem,
|
|
43
46
|
// Util types
|
|
44
47
|
EncryptStepCallbackFunction as EncryptSetStateFn,
|
|
48
|
+
EncryptStepCallbackContext,
|
|
49
|
+
} from './types.js';
|
|
50
|
+
export {
|
|
51
|
+
FheTypes,
|
|
52
|
+
FheUintUTypes,
|
|
53
|
+
FheAllUTypes,
|
|
54
|
+
Encryptable,
|
|
55
|
+
isEncryptableItem,
|
|
56
|
+
EncryptStep,
|
|
57
|
+
isLastEncryptionStep,
|
|
58
|
+
assertCorrectEncryptedItemInput,
|
|
45
59
|
} from './types.js';
|
|
46
|
-
export { FheTypes, FheUintUTypes, FheAllUTypes, Encryptable, isEncryptableItem, EncryptStep } from './types.js';
|
|
47
60
|
|
|
48
61
|
// Error handling
|
|
49
62
|
export { CofhesdkError, CofhesdkErrorCode, isCofhesdkError } from './error.js';
|
|
50
63
|
export type { CofhesdkErrorParams } from './error.js';
|
|
51
64
|
|
|
52
|
-
// Result types
|
|
53
|
-
export {
|
|
54
|
-
ResultErr,
|
|
55
|
-
ResultOk,
|
|
56
|
-
ResultErrOrInternal,
|
|
57
|
-
ResultHttpError,
|
|
58
|
-
ResultValidationError,
|
|
59
|
-
resultWrapper,
|
|
60
|
-
resultWrapperSync,
|
|
61
|
-
} from './result.js';
|
|
62
|
-
export type { Result } from './result.js';
|
|
63
|
-
|
|
64
65
|
// Key fetching
|
|
65
|
-
export { fetchKeys
|
|
66
|
+
export { fetchKeys } from './fetchKeys.js';
|
|
66
67
|
export type { FheKeyDeserializer } from './fetchKeys.js';
|
|
67
68
|
|
|
68
69
|
// Key storage
|
|
@@ -74,4 +75,23 @@ export { EncryptInputsBuilder } from './encrypt/encryptInputsBuilder.js';
|
|
|
74
75
|
export { DecryptHandlesBuilder } from './decrypt/decryptHandleBuilder.js';
|
|
75
76
|
|
|
76
77
|
// ZK utilities
|
|
77
|
-
export type {
|
|
78
|
+
export type {
|
|
79
|
+
ZkBuilderAndCrsGenerator,
|
|
80
|
+
ZkProveWorkerFunction,
|
|
81
|
+
ZkProveWorkerRequest,
|
|
82
|
+
ZkProveWorkerResponse,
|
|
83
|
+
} from './encrypt/zkPackProveVerify.js';
|
|
84
|
+
export { zkProveWithWorker } from './encrypt/zkPackProveVerify.js';
|
|
85
|
+
|
|
86
|
+
// Contract addresses
|
|
87
|
+
export {
|
|
88
|
+
TASK_MANAGER_ADDRESS,
|
|
89
|
+
MOCKS_ZK_VERIFIER_ADDRESS,
|
|
90
|
+
MOCKS_ZK_VERIFIER_SIGNER_ADDRESS,
|
|
91
|
+
MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY,
|
|
92
|
+
MOCKS_QUERY_DECRYPTER_ADDRESS,
|
|
93
|
+
TEST_BED_ADDRESS,
|
|
94
|
+
} from './consts.js';
|
|
95
|
+
|
|
96
|
+
// Utils
|
|
97
|
+
export { fheTypeToString } from './utils.js';
|
package/core/keyStore.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-unused-vars */
|
|
2
1
|
import { createStore, type StoreApi } from 'zustand/vanilla';
|
|
3
2
|
import { persist, createJSONStorage } from 'zustand/middleware';
|
|
4
3
|
import { produce } from 'immer';
|
|
@@ -24,6 +23,30 @@ export type KeysStorage = {
|
|
|
24
23
|
rehydrateKeysStore: () => Promise<void>;
|
|
25
24
|
};
|
|
26
25
|
|
|
26
|
+
function isValidPersistedState(state: unknown): state is KeysStore {
|
|
27
|
+
if (state && typeof state === 'object') {
|
|
28
|
+
if ('fhe' in state && 'crs' in state) {
|
|
29
|
+
return true;
|
|
30
|
+
} else {
|
|
31
|
+
throw new Error(
|
|
32
|
+
"Invalid persisted state structure for KeysStore. Is object but doesn't contain required fields 'fhe' and 'crs'."
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const DEFAULT_KEYS_STORE: KeysStore = {
|
|
41
|
+
fhe: {},
|
|
42
|
+
crs: {},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type StoreWithPersist = ReturnType<typeof createStoreWithPersit>;
|
|
46
|
+
|
|
47
|
+
function isStoreWithPersist(store: StoreApi<KeysStore> | StoreWithPersist): store is StoreWithPersist {
|
|
48
|
+
return 'persist' in store;
|
|
49
|
+
}
|
|
27
50
|
/**
|
|
28
51
|
* Creates a keys storage instance using the provided storage implementation
|
|
29
52
|
* @param storage - The storage implementation to use (IStorage interface), or null for non-persisted store
|
|
@@ -32,39 +55,7 @@ export type KeysStorage = {
|
|
|
32
55
|
export function createKeysStore(storage: IStorage | null): KeysStorage {
|
|
33
56
|
// Conditionally create store with or without persist wrapper
|
|
34
57
|
const keysStore = storage
|
|
35
|
-
?
|
|
36
|
-
persist(
|
|
37
|
-
() => ({
|
|
38
|
-
fhe: {},
|
|
39
|
-
crs: {},
|
|
40
|
-
}),
|
|
41
|
-
{
|
|
42
|
-
name: 'cofhesdk-keys',
|
|
43
|
-
storage: createJSONStorage(() => storage),
|
|
44
|
-
merge: (persistedState, currentState) => {
|
|
45
|
-
const persisted = persistedState as KeysStore;
|
|
46
|
-
const current = currentState as KeysStore;
|
|
47
|
-
|
|
48
|
-
// Deep merge for fhe
|
|
49
|
-
const mergedFhe: KeysStore['fhe'] = { ...persisted.fhe };
|
|
50
|
-
const allChainIds = new Set([...Object.keys(current.fhe), ...Object.keys(persisted.fhe)]);
|
|
51
|
-
for (const chainId of allChainIds) {
|
|
52
|
-
const persistedZones = persisted.fhe[chainId] || {};
|
|
53
|
-
const currentZones = current.fhe[chainId] || {};
|
|
54
|
-
mergedFhe[chainId] = { ...persistedZones, ...currentZones };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Deep merge for crs
|
|
58
|
-
const mergedCrs: KeysStore['crs'] = { ...persisted.crs, ...current.crs };
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
fhe: mergedFhe,
|
|
62
|
-
crs: mergedCrs,
|
|
63
|
-
};
|
|
64
|
-
},
|
|
65
|
-
}
|
|
66
|
-
)
|
|
67
|
-
)
|
|
58
|
+
? createStoreWithPersit(storage)
|
|
68
59
|
: createStore<KeysStore>()(() => ({
|
|
69
60
|
fhe: {},
|
|
70
61
|
crs: {},
|
|
@@ -109,10 +100,9 @@ export function createKeysStore(storage: IStorage | null): KeysStorage {
|
|
|
109
100
|
};
|
|
110
101
|
|
|
111
102
|
const rehydrateKeysStore = async () => {
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
103
|
+
if (!isStoreWithPersist(keysStore)) return;
|
|
104
|
+
if (keysStore.persist.hasHydrated()) return;
|
|
105
|
+
await keysStore.persist.rehydrate();
|
|
116
106
|
};
|
|
117
107
|
|
|
118
108
|
return {
|
|
@@ -125,3 +115,40 @@ export function createKeysStore(storage: IStorage | null): KeysStorage {
|
|
|
125
115
|
rehydrateKeysStore,
|
|
126
116
|
};
|
|
127
117
|
}
|
|
118
|
+
|
|
119
|
+
function createStoreWithPersit(storage: IStorage) {
|
|
120
|
+
const result = createStore<KeysStore>()(
|
|
121
|
+
persist(() => DEFAULT_KEYS_STORE, {
|
|
122
|
+
// because earleir tests were written with on-init hydration skipped (due to the error suppression in zustand), returning this flag to fix test (i.e. KeyStore > Storage Utilities > should rehydrate keys store)
|
|
123
|
+
skipHydration: true,
|
|
124
|
+
// if onRehydrateStorage is not passed here, the errors thrown by storage layer are swallowed by zustand here: https://github.com/pmndrs/zustand/blob/39a391b6c1ff9aa89b81694d9bdb21da37dd4ac6/src/middleware/persist.ts#L321
|
|
125
|
+
onRehydrateStorage: () => (_state?, _error?) => {
|
|
126
|
+
if (_error) throw new Error(`onRehydrateStorage: Error rehydrating keys store: ${_error}`);
|
|
127
|
+
},
|
|
128
|
+
name: 'cofhesdk-keys',
|
|
129
|
+
storage: createJSONStorage(() => storage),
|
|
130
|
+
merge: (persistedState, currentState) => {
|
|
131
|
+
const persisted = isValidPersistedState(persistedState) ? persistedState : DEFAULT_KEYS_STORE;
|
|
132
|
+
const current = currentState as KeysStore;
|
|
133
|
+
|
|
134
|
+
// Deep merge for fhe
|
|
135
|
+
const mergedFhe: KeysStore['fhe'] = { ...persisted.fhe };
|
|
136
|
+
const allChainIds = new Set([...Object.keys(current.fhe), ...Object.keys(persisted.fhe)]);
|
|
137
|
+
for (const chainId of allChainIds) {
|
|
138
|
+
const persistedZones = persisted.fhe[chainId] || {};
|
|
139
|
+
const currentZones = current.fhe[chainId] || {};
|
|
140
|
+
mergedFhe[chainId] = { ...persistedZones, ...currentZones };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Deep merge for crs
|
|
144
|
+
const mergedCrs: KeysStore['crs'] = { ...persisted.crs, ...current.crs };
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
fhe: mergedFhe,
|
|
148
|
+
crs: mergedCrs,
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
return result;
|
|
154
|
+
}
|
package/core/permits.test.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @vitest-environment happy-dom
|
|
3
3
|
*/
|
|
4
|
-
/* eslint-disable no-unused-vars */
|
|
5
4
|
import { permitStore } from '@/permits';
|
|
6
5
|
|
|
7
6
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
@@ -152,7 +151,7 @@ describe('Core Permits Tests', () => {
|
|
|
152
151
|
publicClient,
|
|
153
152
|
bobWalletClient
|
|
154
153
|
);
|
|
155
|
-
permitHash =
|
|
154
|
+
permitHash = createdPermit.hash;
|
|
156
155
|
});
|
|
157
156
|
|
|
158
157
|
it('should get permit by hash', async () => {
|
|
@@ -196,9 +195,8 @@ describe('Core Permits Tests', () => {
|
|
|
196
195
|
const permitKeys = Object.keys(parsedData.state.permits[chainId][bobAddress]);
|
|
197
196
|
expect(permitKeys.length).toBeGreaterThan(0);
|
|
198
197
|
|
|
199
|
-
const permitHash = permits.getHash(createdPermit);
|
|
200
198
|
const serializedPermit = permits.serialize(createdPermit);
|
|
201
|
-
expect(parsedData.state.permits[chainId][bobAddress][
|
|
199
|
+
expect(parsedData.state.permits[chainId][bobAddress][createdPermit.hash]).toEqual(serializedPermit);
|
|
202
200
|
});
|
|
203
201
|
});
|
|
204
202
|
|
|
@@ -239,4 +237,257 @@ describe('Core Permits Tests', () => {
|
|
|
239
237
|
expect(activePermit?.name).toBe('Permit 2');
|
|
240
238
|
});
|
|
241
239
|
});
|
|
240
|
+
|
|
241
|
+
describe('getOrCreateSelfPermit', () => {
|
|
242
|
+
it('should create a new self permit when none exists', async () => {
|
|
243
|
+
const permit = await permits.getOrCreateSelfPermit(publicClient, bobWalletClient, chainId, bobAddress, {
|
|
244
|
+
issuer: bobAddress,
|
|
245
|
+
name: 'New Self Permit',
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
expect(permit).toBeDefined();
|
|
249
|
+
expect(permit.name).toBe('New Self Permit');
|
|
250
|
+
expect(permit.type).toBe('self');
|
|
251
|
+
expect(permit.issuer).toBe(bobAddress);
|
|
252
|
+
expect(permit.issuerSignature).toBeDefined();
|
|
253
|
+
|
|
254
|
+
// Verify it was stored and set as active
|
|
255
|
+
const activePermit = await permits.getActivePermit(chainId, bobAddress);
|
|
256
|
+
expect(activePermit?.name).toBe('New Self Permit');
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should return existing self permit when one exists', async () => {
|
|
260
|
+
// Create an initial self permit
|
|
261
|
+
const firstPermit = await permits.createSelf(
|
|
262
|
+
{ name: 'First Self Permit', issuer: bobAddress },
|
|
263
|
+
publicClient,
|
|
264
|
+
bobWalletClient
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
// Call getOrCreateSelfPermit - should return existing
|
|
268
|
+
const permit = await permits.getOrCreateSelfPermit(publicClient, bobWalletClient, chainId, bobAddress, {
|
|
269
|
+
issuer: bobAddress,
|
|
270
|
+
name: 'Should Not Create This',
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
expect(permit.name).toBe('First Self Permit');
|
|
274
|
+
expect(permit.hash).toBe(firstPermit.hash);
|
|
275
|
+
|
|
276
|
+
// Verify no new permit was created
|
|
277
|
+
const allPermits = await permits.getPermits(chainId, bobAddress);
|
|
278
|
+
expect(Object.keys(allPermits).length).toBe(1);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should create new self permit when active permit is sharing type', async () => {
|
|
282
|
+
// Create a sharing permit first
|
|
283
|
+
await permits.createSharing(
|
|
284
|
+
{
|
|
285
|
+
name: 'Sharing Permit',
|
|
286
|
+
issuer: bobAddress,
|
|
287
|
+
recipient: aliceAddress,
|
|
288
|
+
},
|
|
289
|
+
publicClient,
|
|
290
|
+
bobWalletClient
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
// Call getOrCreateSelfPermit - should create new since active is sharing type
|
|
294
|
+
const permit = await permits.getOrCreateSelfPermit(publicClient, bobWalletClient, chainId, bobAddress, {
|
|
295
|
+
issuer: bobAddress,
|
|
296
|
+
name: 'New Self Permit',
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
expect(permit.name).toBe('New Self Permit');
|
|
300
|
+
expect(permit.type).toBe('self');
|
|
301
|
+
|
|
302
|
+
// Verify two permits exist now
|
|
303
|
+
const allPermits = await permits.getPermits(chainId, bobAddress);
|
|
304
|
+
expect(Object.keys(allPermits).length).toBe(2);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should use default options when none provided', async () => {
|
|
308
|
+
const permit = await permits.getOrCreateSelfPermit(publicClient, bobWalletClient, chainId, bobAddress);
|
|
309
|
+
|
|
310
|
+
expect(permit).toBeDefined();
|
|
311
|
+
expect(permit.type).toBe('self');
|
|
312
|
+
expect(permit.issuer).toBe(bobAddress);
|
|
313
|
+
expect(permit.name).toBe('Autogenerated Self Permit');
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should use default chainId and account when not provided', async () => {
|
|
317
|
+
const permit = await permits.getOrCreateSelfPermit(publicClient, bobWalletClient, undefined, undefined, {
|
|
318
|
+
issuer: bobAddress,
|
|
319
|
+
name: 'Test Permit',
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
expect(permit).toBeDefined();
|
|
323
|
+
expect(permit.issuer).toBe(bobAddress);
|
|
324
|
+
|
|
325
|
+
// Verify it was stored with the chain's actual chainId
|
|
326
|
+
const activePermit = await permits.getActivePermit(chainId, bobAddress);
|
|
327
|
+
expect(activePermit?.name).toBe('Test Permit');
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
describe('getOrCreateSharingPermit', () => {
|
|
332
|
+
it('should create a new sharing permit when none exists', async () => {
|
|
333
|
+
const permit = await permits.getOrCreateSharingPermit(
|
|
334
|
+
publicClient,
|
|
335
|
+
bobWalletClient,
|
|
336
|
+
{
|
|
337
|
+
issuer: bobAddress,
|
|
338
|
+
recipient: aliceAddress,
|
|
339
|
+
name: 'New Sharing Permit',
|
|
340
|
+
},
|
|
341
|
+
chainId,
|
|
342
|
+
bobAddress
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
expect(permit).toBeDefined();
|
|
346
|
+
expect(permit.name).toBe('New Sharing Permit');
|
|
347
|
+
expect(permit.type).toBe('sharing');
|
|
348
|
+
expect(permit.issuer).toBe(bobAddress);
|
|
349
|
+
expect(permit.recipient).toBe(aliceAddress);
|
|
350
|
+
expect(permit.issuerSignature).toBeDefined();
|
|
351
|
+
|
|
352
|
+
// Verify it was stored and set as active
|
|
353
|
+
const activePermit = await permits.getActivePermit(chainId, bobAddress);
|
|
354
|
+
expect(activePermit?.name).toBe('New Sharing Permit');
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('should return existing sharing permit when one exists', async () => {
|
|
358
|
+
// Create an initial sharing permit
|
|
359
|
+
const firstPermit = await permits.createSharing(
|
|
360
|
+
{
|
|
361
|
+
name: 'First Sharing Permit',
|
|
362
|
+
issuer: bobAddress,
|
|
363
|
+
recipient: aliceAddress,
|
|
364
|
+
},
|
|
365
|
+
publicClient,
|
|
366
|
+
bobWalletClient
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
// Call getOrCreateSharingPermit - should return existing
|
|
370
|
+
const permit = await permits.getOrCreateSharingPermit(
|
|
371
|
+
publicClient,
|
|
372
|
+
bobWalletClient,
|
|
373
|
+
{
|
|
374
|
+
issuer: bobAddress,
|
|
375
|
+
recipient: aliceAddress,
|
|
376
|
+
name: 'Should Not Create This',
|
|
377
|
+
},
|
|
378
|
+
chainId,
|
|
379
|
+
bobAddress
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
expect(permit.name).toBe('First Sharing Permit');
|
|
383
|
+
expect(permit.hash).toBe(firstPermit.hash);
|
|
384
|
+
|
|
385
|
+
// Verify no new permit was created
|
|
386
|
+
const allPermits = await permits.getPermits(chainId, bobAddress);
|
|
387
|
+
expect(Object.keys(allPermits).length).toBe(1);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('should create new sharing permit when active permit is self type', async () => {
|
|
391
|
+
// Create a self permit first
|
|
392
|
+
await permits.createSelf({ name: 'Self Permit', issuer: bobAddress }, publicClient, bobWalletClient);
|
|
393
|
+
|
|
394
|
+
// Call getOrCreateSharingPermit - should create new since active is self type
|
|
395
|
+
const permit = await permits.getOrCreateSharingPermit(
|
|
396
|
+
publicClient,
|
|
397
|
+
bobWalletClient,
|
|
398
|
+
{
|
|
399
|
+
issuer: bobAddress,
|
|
400
|
+
recipient: aliceAddress,
|
|
401
|
+
name: 'New Sharing Permit',
|
|
402
|
+
},
|
|
403
|
+
chainId,
|
|
404
|
+
bobAddress
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
expect(permit.name).toBe('New Sharing Permit');
|
|
408
|
+
expect(permit.type).toBe('sharing');
|
|
409
|
+
|
|
410
|
+
// Verify two permits exist now
|
|
411
|
+
const allPermits = await permits.getPermits(chainId, bobAddress);
|
|
412
|
+
expect(Object.keys(allPermits).length).toBe(2);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('should use default chainId and account when not provided', async () => {
|
|
416
|
+
const permit = await permits.getOrCreateSharingPermit(
|
|
417
|
+
publicClient,
|
|
418
|
+
bobWalletClient,
|
|
419
|
+
{
|
|
420
|
+
issuer: bobAddress,
|
|
421
|
+
recipient: aliceAddress,
|
|
422
|
+
name: 'Test Sharing Permit',
|
|
423
|
+
},
|
|
424
|
+
undefined,
|
|
425
|
+
undefined
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
expect(permit).toBeDefined();
|
|
429
|
+
expect(permit.issuer).toBe(bobAddress);
|
|
430
|
+
expect(permit.recipient).toBe(aliceAddress);
|
|
431
|
+
|
|
432
|
+
// Verify it was stored with the chain's actual chainId
|
|
433
|
+
const activePermit = await permits.getActivePermit(chainId, bobAddress);
|
|
434
|
+
expect(activePermit?.name).toBe('Test Sharing Permit');
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
describe('getOrCreate - Multiple Types Scenarios', () => {
|
|
439
|
+
it('should handle switching between self and sharing permits', async () => {
|
|
440
|
+
// Create self permit
|
|
441
|
+
const selfPermit = await permits.getOrCreateSelfPermit(publicClient, bobWalletClient, chainId, bobAddress, {
|
|
442
|
+
issuer: bobAddress,
|
|
443
|
+
name: 'Self Permit',
|
|
444
|
+
});
|
|
445
|
+
expect(selfPermit.type).toBe('self');
|
|
446
|
+
|
|
447
|
+
// Create sharing permit (should create new one)
|
|
448
|
+
const sharingPermit = await permits.getOrCreateSharingPermit(
|
|
449
|
+
publicClient,
|
|
450
|
+
bobWalletClient,
|
|
451
|
+
{
|
|
452
|
+
issuer: bobAddress,
|
|
453
|
+
recipient: aliceAddress,
|
|
454
|
+
name: 'Sharing Permit',
|
|
455
|
+
},
|
|
456
|
+
chainId,
|
|
457
|
+
bobAddress
|
|
458
|
+
);
|
|
459
|
+
expect(sharingPermit.type).toBe('sharing');
|
|
460
|
+
|
|
461
|
+
// Both should exist
|
|
462
|
+
const allPermits = await permits.getPermits(chainId, bobAddress);
|
|
463
|
+
expect(Object.keys(allPermits).length).toBe(2);
|
|
464
|
+
|
|
465
|
+
// Active permit should be the sharing one
|
|
466
|
+
const activePermit = await permits.getActivePermit(chainId, bobAddress);
|
|
467
|
+
expect(activePermit?.type).toBe('sharing');
|
|
468
|
+
expect(activePermit?.name).toBe('Sharing Permit');
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it('should correctly handle sequential getOrCreate calls', async () => {
|
|
472
|
+
// First call - creates new
|
|
473
|
+
const permit1 = await permits.getOrCreateSelfPermit(publicClient, bobWalletClient, chainId, bobAddress, {
|
|
474
|
+
issuer: bobAddress,
|
|
475
|
+
name: 'Permit 1',
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// Second call - returns existing
|
|
479
|
+
const permit2 = await permits.getOrCreateSelfPermit(publicClient, bobWalletClient, chainId, bobAddress, {
|
|
480
|
+
issuer: bobAddress,
|
|
481
|
+
name: 'Permit 2',
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// Should be the same permit
|
|
485
|
+
expect(permit1.hash).toBe(permit2.hash);
|
|
486
|
+
expect(permit2.name).toBe('Permit 1'); // Original name
|
|
487
|
+
|
|
488
|
+
// Only one permit should exist
|
|
489
|
+
const allPermits = await permits.getPermits(chainId, bobAddress);
|
|
490
|
+
expect(Object.keys(allPermits).length).toBe(1);
|
|
491
|
+
});
|
|
492
|
+
});
|
|
242
493
|
});
|
package/core/permits.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-unused-vars */
|
|
2
1
|
import {
|
|
3
2
|
type ImportSharedPermitOptions,
|
|
4
3
|
PermitUtils,
|
|
@@ -7,6 +6,10 @@ import {
|
|
|
7
6
|
type Permit,
|
|
8
7
|
permitStore,
|
|
9
8
|
type SerializedPermit,
|
|
9
|
+
type SelfPermit,
|
|
10
|
+
type RecipientPermit,
|
|
11
|
+
type SharingPermit,
|
|
12
|
+
type PermitHashFields,
|
|
10
13
|
} from '@/permits';
|
|
11
14
|
|
|
12
15
|
import { type PublicClient, type WalletClient } from 'viem';
|
|
@@ -19,16 +22,16 @@ const storeActivePermit = async (permit: Permit, publicClient: any, walletClient
|
|
|
19
22
|
const account = walletClient.account!.address;
|
|
20
23
|
|
|
21
24
|
permitStore.setPermit(chainId, account, permit);
|
|
22
|
-
permitStore.setActivePermitHash(chainId, account,
|
|
25
|
+
permitStore.setActivePermitHash(chainId, account, permit.hash);
|
|
23
26
|
};
|
|
24
27
|
|
|
25
28
|
// Generic function to handle permit creation with error handling
|
|
26
|
-
const createPermitWithSign = async <T>(
|
|
29
|
+
const createPermitWithSign = async <T, TPermit extends Permit>(
|
|
27
30
|
options: T,
|
|
28
31
|
publicClient: PublicClient,
|
|
29
32
|
walletClient: WalletClient,
|
|
30
|
-
permitMethod: (options: T, publicClient: PublicClient, walletClient: WalletClient) => Promise<
|
|
31
|
-
): Promise<
|
|
33
|
+
permitMethod: (options: T, publicClient: PublicClient, walletClient: WalletClient) => Promise<TPermit>
|
|
34
|
+
): Promise<TPermit> => {
|
|
32
35
|
const permit = await permitMethod(options, publicClient, walletClient);
|
|
33
36
|
await storeActivePermit(permit, publicClient, walletClient);
|
|
34
37
|
return permit;
|
|
@@ -46,7 +49,7 @@ const createSelf = async (
|
|
|
46
49
|
options: CreateSelfPermitOptions,
|
|
47
50
|
publicClient: PublicClient,
|
|
48
51
|
walletClient: WalletClient
|
|
49
|
-
): Promise<
|
|
52
|
+
): Promise<SelfPermit> => {
|
|
50
53
|
return createPermitWithSign(options, publicClient, walletClient, PermitUtils.createSelfAndSign);
|
|
51
54
|
};
|
|
52
55
|
|
|
@@ -54,21 +57,21 @@ const createSharing = async (
|
|
|
54
57
|
options: CreateSharingPermitOptions,
|
|
55
58
|
publicClient: PublicClient,
|
|
56
59
|
walletClient: WalletClient
|
|
57
|
-
): Promise<
|
|
60
|
+
): Promise<SharingPermit> => {
|
|
58
61
|
return createPermitWithSign(options, publicClient, walletClient, PermitUtils.createSharingAndSign);
|
|
59
62
|
};
|
|
60
63
|
|
|
61
64
|
const importShared = async (
|
|
62
|
-
options: ImportSharedPermitOptions |
|
|
65
|
+
options: ImportSharedPermitOptions | string,
|
|
63
66
|
publicClient: PublicClient,
|
|
64
67
|
walletClient: WalletClient
|
|
65
|
-
): Promise<
|
|
68
|
+
): Promise<RecipientPermit> => {
|
|
66
69
|
return createPermitWithSign(options, publicClient, walletClient, PermitUtils.importSharedAndSign);
|
|
67
70
|
};
|
|
68
71
|
|
|
69
72
|
// PERMIT UTILS
|
|
70
73
|
|
|
71
|
-
const getHash = (permit:
|
|
74
|
+
const getHash = (permit: PermitHashFields) => {
|
|
72
75
|
return PermitUtils.getHash(permit);
|
|
73
76
|
};
|
|
74
77
|
|
|
@@ -94,24 +97,83 @@ const getActivePermit = async (chainId: number, account: string): Promise<Permit
|
|
|
94
97
|
return permitStore.getActivePermit(chainId, account);
|
|
95
98
|
};
|
|
96
99
|
|
|
97
|
-
const getActivePermitHash =
|
|
100
|
+
const getActivePermitHash = (chainId: number, account: string): string | undefined => {
|
|
98
101
|
return permitStore.getActivePermitHash(chainId, account);
|
|
99
102
|
};
|
|
100
103
|
|
|
101
|
-
const selectActivePermit =
|
|
102
|
-
|
|
104
|
+
const selectActivePermit = (chainId: number, account: string, hash: string): void => {
|
|
105
|
+
permitStore.setActivePermitHash(chainId, account, hash);
|
|
103
106
|
};
|
|
104
107
|
|
|
105
|
-
//
|
|
108
|
+
// GET OR CREATE
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get the active self permit or create a new one if it doesn't exist
|
|
112
|
+
* @param publicClient - The public client
|
|
113
|
+
* @param walletClient - The wallet client
|
|
114
|
+
* @param chainId - Optional chain ID (will use publicClient if not provided)
|
|
115
|
+
* @param account - Optional account (will use walletClient if not provided)
|
|
116
|
+
* @param options - The options for creating a self permit
|
|
117
|
+
* @returns The existing or newly created permit
|
|
118
|
+
*/
|
|
119
|
+
const getOrCreateSelfPermit = async (
|
|
120
|
+
publicClient: PublicClient,
|
|
121
|
+
walletClient: WalletClient,
|
|
122
|
+
chainId?: number,
|
|
123
|
+
account?: string,
|
|
124
|
+
options?: CreateSelfPermitOptions
|
|
125
|
+
): Promise<Permit> => {
|
|
126
|
+
const _chainId = chainId ?? (await publicClient.getChainId());
|
|
127
|
+
const _account = account ?? walletClient.account!.address;
|
|
128
|
+
|
|
129
|
+
// Try to get active permit first
|
|
130
|
+
const activePermit = await getActivePermit(_chainId, _account);
|
|
131
|
+
|
|
132
|
+
if (activePermit && activePermit.type === 'self') {
|
|
133
|
+
return activePermit;
|
|
134
|
+
}
|
|
106
135
|
|
|
107
|
-
|
|
108
|
-
|
|
136
|
+
// No active permit or wrong type, create new one
|
|
137
|
+
return createSelf(options ?? { issuer: _account, name: 'Autogenerated Self Permit' }, publicClient, walletClient);
|
|
109
138
|
};
|
|
110
139
|
|
|
111
|
-
|
|
112
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Get the active sharing permit or create a new one if it doesn't exist
|
|
142
|
+
* @param publicClient - The public client
|
|
143
|
+
* @param walletClient - The wallet client
|
|
144
|
+
* @param options - The options for creating a sharing permit (required)
|
|
145
|
+
* @param chainId - Optional chain ID (will use publicClient if not provided)
|
|
146
|
+
* @param account - Optional account (will use walletClient if not provided)
|
|
147
|
+
* @returns The existing or newly created permit
|
|
148
|
+
*/
|
|
149
|
+
const getOrCreateSharingPermit = async (
|
|
150
|
+
publicClient: PublicClient,
|
|
151
|
+
walletClient: WalletClient,
|
|
152
|
+
options: CreateSharingPermitOptions,
|
|
153
|
+
chainId?: number,
|
|
154
|
+
account?: string
|
|
155
|
+
): Promise<Permit> => {
|
|
156
|
+
const _chainId = chainId ?? (await publicClient.getChainId());
|
|
157
|
+
const _account = account ?? walletClient.account!.address;
|
|
158
|
+
|
|
159
|
+
// Try to get active permit first
|
|
160
|
+
const activePermit = await getActivePermit(_chainId, _account);
|
|
161
|
+
|
|
162
|
+
if (activePermit && activePermit.type === 'sharing') {
|
|
163
|
+
return activePermit;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return createSharing(options, publicClient, walletClient);
|
|
113
167
|
};
|
|
114
168
|
|
|
169
|
+
// REMOVE
|
|
170
|
+
|
|
171
|
+
const removePermit = async (chainId: number, account: string, hash: string): Promise<void> =>
|
|
172
|
+
permitStore.removePermit(chainId, account, hash);
|
|
173
|
+
|
|
174
|
+
const removeActivePermit = async (chainId: number, account: string): Promise<void> =>
|
|
175
|
+
permitStore.removeActivePermitHash(chainId, account);
|
|
176
|
+
|
|
115
177
|
// EXPORT
|
|
116
178
|
|
|
117
179
|
export const permits = {
|
|
@@ -122,6 +184,9 @@ export const permits = {
|
|
|
122
184
|
createSharing,
|
|
123
185
|
importShared,
|
|
124
186
|
|
|
187
|
+
getOrCreateSelfPermit,
|
|
188
|
+
getOrCreateSharingPermit,
|
|
189
|
+
|
|
125
190
|
getHash,
|
|
126
191
|
serialize,
|
|
127
192
|
deserialize,
|