@cofhe/sdk 0.1.0 → 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 +62 -0
- package/adapters/ethers5.test.ts +174 -0
- package/adapters/ethers5.ts +36 -0
- package/adapters/ethers6.test.ts +169 -0
- package/adapters/ethers6.ts +36 -0
- package/adapters/hardhat-node.ts +167 -0
- package/adapters/hardhat.hh2.test.ts +159 -0
- package/adapters/hardhat.ts +36 -0
- package/adapters/index.test.ts +20 -0
- package/adapters/index.ts +5 -0
- package/adapters/smartWallet.ts +99 -0
- package/adapters/test-utils.ts +53 -0
- package/adapters/types.ts +6 -0
- package/adapters/wagmi.test.ts +156 -0
- package/adapters/wagmi.ts +17 -0
- package/chains/chains/arbSepolia.ts +14 -0
- package/chains/chains/baseSepolia.ts +14 -0
- package/chains/chains/hardhat.ts +15 -0
- package/chains/chains/localcofhe.ts +14 -0
- package/chains/chains/sepolia.ts +14 -0
- package/chains/chains.test.ts +50 -0
- package/chains/defineChain.ts +18 -0
- package/chains/index.ts +35 -0
- package/chains/types.ts +32 -0
- package/core/baseBuilder.ts +119 -0
- package/core/client.test.ts +315 -0
- package/core/client.ts +292 -0
- package/core/clientTypes.ts +108 -0
- package/core/config.test.ts +235 -0
- package/core/config.ts +220 -0
- package/core/decrypt/MockQueryDecrypterAbi.ts +129 -0
- package/core/decrypt/cofheMocksSealOutput.ts +57 -0
- package/core/decrypt/decryptHandleBuilder.ts +287 -0
- package/core/decrypt/decryptUtils.ts +28 -0
- package/core/decrypt/tnSealOutputV1.ts +59 -0
- package/core/decrypt/tnSealOutputV2.ts +298 -0
- package/core/encrypt/MockZkVerifierAbi.ts +106 -0
- package/core/encrypt/cofheMocksZkVerifySign.ts +284 -0
- package/core/encrypt/encryptInputsBuilder.test.ts +751 -0
- package/core/encrypt/encryptInputsBuilder.ts +560 -0
- package/core/encrypt/encryptUtils.ts +67 -0
- package/core/encrypt/zkPackProveVerify.ts +335 -0
- package/core/error.ts +168 -0
- package/core/fetchKeys.test.ts +195 -0
- package/core/fetchKeys.ts +144 -0
- package/core/index.ts +89 -0
- package/core/keyStore.test.ts +226 -0
- package/core/keyStore.ts +154 -0
- package/core/permits.test.ts +494 -0
- package/core/permits.ts +200 -0
- package/core/types.ts +398 -0
- package/core/utils.ts +130 -0
- package/dist/adapters.cjs +88 -0
- package/dist/adapters.d.cts +14576 -0
- package/dist/adapters.d.ts +14576 -0
- package/dist/adapters.js +83 -0
- package/dist/chains.cjs +114 -0
- package/dist/chains.d.cts +121 -0
- package/dist/chains.d.ts +121 -0
- package/dist/chains.js +1 -0
- package/dist/chunk-UGBVZNRT.js +818 -0
- package/dist/chunk-WEAZ25JO.js +105 -0
- package/dist/chunk-WGCRJCBR.js +2523 -0
- package/dist/clientTypes-5_1nwtUe.d.cts +914 -0
- package/dist/clientTypes-Es7fyi65.d.ts +914 -0
- package/dist/core.cjs +3414 -0
- package/dist/core.d.cts +111 -0
- package/dist/core.d.ts +111 -0
- package/dist/core.js +3 -0
- package/dist/node.cjs +3286 -0
- package/dist/node.d.cts +22 -0
- package/dist/node.d.ts +22 -0
- package/dist/node.js +91 -0
- package/dist/permit-fUSe6KKq.d.cts +349 -0
- package/dist/permit-fUSe6KKq.d.ts +349 -0
- package/dist/permits.cjs +871 -0
- package/dist/permits.d.cts +1045 -0
- package/dist/permits.d.ts +1045 -0
- package/dist/permits.js +1 -0
- package/dist/types-KImPrEIe.d.cts +48 -0
- package/dist/types-KImPrEIe.d.ts +48 -0
- package/dist/web.cjs +3478 -0
- package/dist/web.d.cts +38 -0
- package/dist/web.d.ts +38 -0
- package/dist/web.js +240 -0
- 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 +147 -0
- package/node/config.test.ts +68 -0
- package/node/encryptInputs.test.ts +155 -0
- package/node/index.ts +97 -0
- package/node/storage.ts +51 -0
- package/package.json +27 -15
- package/permits/index.ts +68 -0
- package/permits/localstorage.test.ts +117 -0
- package/permits/permit.test.ts +477 -0
- package/permits/permit.ts +405 -0
- package/permits/sealing.test.ts +84 -0
- package/permits/sealing.ts +131 -0
- package/permits/signature.ts +79 -0
- package/permits/store.test.ts +128 -0
- package/permits/store.ts +166 -0
- package/permits/test-utils.ts +20 -0
- package/permits/types.ts +191 -0
- package/permits/utils.ts +62 -0
- package/permits/validation.test.ts +288 -0
- package/permits/validation.ts +369 -0
- package/web/client.web.test.ts +147 -0
- package/web/config.web.test.ts +69 -0
- package/web/encryptInputs.web.test.ts +172 -0
- package/web/index.ts +161 -0
- package/web/storage.ts +34 -0
- 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
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vitest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
permitStore,
|
|
8
|
+
getPermit,
|
|
9
|
+
getActivePermit,
|
|
10
|
+
getPermits,
|
|
11
|
+
setPermit,
|
|
12
|
+
removePermit,
|
|
13
|
+
getActivePermitHash,
|
|
14
|
+
setActivePermitHash,
|
|
15
|
+
PermitUtils,
|
|
16
|
+
} from './index.js';
|
|
17
|
+
|
|
18
|
+
import { createMockPermit } from './test-utils.js';
|
|
19
|
+
|
|
20
|
+
describe('Storage Tests', () => {
|
|
21
|
+
const chainId = 1;
|
|
22
|
+
const account = '0x1234567890123456789012345678901234567890';
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
permitStore.resetStore();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
permitStore.resetStore();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('Permit Storage', () => {
|
|
33
|
+
it('should store and retrieve permits', async () => {
|
|
34
|
+
const permit = await createMockPermit();
|
|
35
|
+
const hash = PermitUtils.getHash(permit);
|
|
36
|
+
|
|
37
|
+
setPermit(chainId, account, permit);
|
|
38
|
+
const retrieved = getPermit(chainId, account, hash);
|
|
39
|
+
|
|
40
|
+
expect(retrieved).toBeDefined();
|
|
41
|
+
expect(PermitUtils.serialize(retrieved!)).toEqual(PermitUtils.serialize(permit));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle multiple permits per account', async () => {
|
|
45
|
+
const permit1 = await createMockPermit();
|
|
46
|
+
const permit2 = {
|
|
47
|
+
...(await createMockPermit()),
|
|
48
|
+
issuer: '0x0987654321098765432109876543210987654321' as `0x${string}`,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const hash1 = PermitUtils.getHash(permit1);
|
|
52
|
+
const hash2 = PermitUtils.getHash(permit2);
|
|
53
|
+
|
|
54
|
+
setPermit(chainId, account, permit1);
|
|
55
|
+
setPermit(chainId, account, permit2);
|
|
56
|
+
|
|
57
|
+
const permits = getPermits(chainId, account);
|
|
58
|
+
expect(Object.keys(permits)).toHaveLength(2);
|
|
59
|
+
|
|
60
|
+
expect(PermitUtils.serialize(permits[hash1])).toEqual(PermitUtils.serialize(permit1));
|
|
61
|
+
expect(PermitUtils.serialize(permits[hash2])).toEqual(PermitUtils.serialize(permit2));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should handle active permit hash', async () => {
|
|
65
|
+
const permit = await createMockPermit();
|
|
66
|
+
const hash = PermitUtils.getHash(permit);
|
|
67
|
+
|
|
68
|
+
setPermit(chainId, account, permit);
|
|
69
|
+
setActivePermitHash(chainId, account, hash);
|
|
70
|
+
|
|
71
|
+
const activeHash = getActivePermitHash(chainId, account);
|
|
72
|
+
expect(activeHash).toBe(hash);
|
|
73
|
+
|
|
74
|
+
const activePermit = getActivePermit(chainId, account);
|
|
75
|
+
expect(activePermit).toBeDefined();
|
|
76
|
+
expect(PermitUtils.serialize(activePermit!)).toEqual(PermitUtils.serialize(permit));
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should remove permits', async () => {
|
|
80
|
+
const permit = await createMockPermit();
|
|
81
|
+
const hash = PermitUtils.getHash(permit);
|
|
82
|
+
|
|
83
|
+
setPermit(chainId, account, permit);
|
|
84
|
+
setActivePermitHash(chainId, account, hash);
|
|
85
|
+
|
|
86
|
+
removePermit(chainId, account, hash, true);
|
|
87
|
+
|
|
88
|
+
const retrieved = getPermit(chainId, account, hash);
|
|
89
|
+
expect(retrieved).toBeUndefined();
|
|
90
|
+
|
|
91
|
+
const activeHash = getActivePermitHash(chainId, account);
|
|
92
|
+
expect(activeHash).toBeUndefined();
|
|
93
|
+
});
|
|
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
|
+
});
|
|
128
|
+
});
|
package/permits/store.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { createStore } from 'zustand/vanilla';
|
|
2
|
+
import { persist } from 'zustand/middleware';
|
|
3
|
+
import { produce } from 'immer';
|
|
4
|
+
import { type Permit, type SerializedPermit } from './types.js';
|
|
5
|
+
import { PermitUtils } from './permit.js';
|
|
6
|
+
|
|
7
|
+
type ChainRecord<T> = Record<number, T>;
|
|
8
|
+
type AccountRecord<T> = Record<string, T>;
|
|
9
|
+
type HashRecord<T> = Record<string, T>;
|
|
10
|
+
|
|
11
|
+
type PermitsStore = {
|
|
12
|
+
permits: ChainRecord<AccountRecord<HashRecord<SerializedPermit | undefined>>>;
|
|
13
|
+
activePermitHash: ChainRecord<AccountRecord<string | undefined>>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Stores generated permits for each user, a hash indicating the active permit for each user
|
|
17
|
+
// Can be used to create reactive hooks
|
|
18
|
+
export const PERMIT_STORE_DEFAULTS: PermitsStore = {
|
|
19
|
+
permits: {},
|
|
20
|
+
activePermitHash: {},
|
|
21
|
+
};
|
|
22
|
+
export const _permitStore = createStore<PermitsStore>()(
|
|
23
|
+
persist(() => PERMIT_STORE_DEFAULTS, { name: 'cofhesdk-permits' })
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export const clearStaleStore = () => {
|
|
27
|
+
// Any is used here because we do not have types of the previous store
|
|
28
|
+
const state = _permitStore.getState() as any;
|
|
29
|
+
|
|
30
|
+
// Check if the store has the expected structure
|
|
31
|
+
const hasExpectedStructure =
|
|
32
|
+
state &&
|
|
33
|
+
typeof state === 'object' &&
|
|
34
|
+
'permits' in state &&
|
|
35
|
+
'activePermitHash' in state &&
|
|
36
|
+
typeof state.permits === 'object' &&
|
|
37
|
+
typeof state.activePermitHash === 'object';
|
|
38
|
+
|
|
39
|
+
if (hasExpectedStructure) return;
|
|
40
|
+
// Invalid structure detected - clear the store
|
|
41
|
+
_permitStore.setState({ permits: {}, activePermitHash: {} });
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const getPermit = (
|
|
45
|
+
chainId: number | undefined,
|
|
46
|
+
account: string | undefined,
|
|
47
|
+
hash: string | undefined
|
|
48
|
+
): Permit | undefined => {
|
|
49
|
+
clearStaleStore();
|
|
50
|
+
if (chainId == null || account == null || hash == null) return;
|
|
51
|
+
|
|
52
|
+
const savedPermit = _permitStore.getState().permits[chainId]?.[account]?.[hash];
|
|
53
|
+
if (savedPermit == null) return;
|
|
54
|
+
|
|
55
|
+
return PermitUtils.deserialize(savedPermit);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const getActivePermit = (chainId: number | undefined, account: string | undefined): Permit | undefined => {
|
|
59
|
+
clearStaleStore();
|
|
60
|
+
if (chainId == null || account == null) return;
|
|
61
|
+
|
|
62
|
+
const activePermitHash = _permitStore.getState().activePermitHash[chainId]?.[account];
|
|
63
|
+
return getPermit(chainId, account, activePermitHash);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const getPermits = (chainId: number | undefined, account: string | undefined): Record<string, Permit> => {
|
|
67
|
+
clearStaleStore();
|
|
68
|
+
if (chainId == null || account == null) return {};
|
|
69
|
+
|
|
70
|
+
return Object.entries(_permitStore.getState().permits[chainId]?.[account] ?? {}).reduce(
|
|
71
|
+
(acc, [hash, permit]) => {
|
|
72
|
+
if (permit == undefined) return acc;
|
|
73
|
+
return { ...acc, [hash]: PermitUtils.deserialize(permit) };
|
|
74
|
+
},
|
|
75
|
+
{} as Record<string, Permit>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const setPermit = (chainId: number, account: string, permit: Permit) => {
|
|
80
|
+
clearStaleStore();
|
|
81
|
+
_permitStore.setState(
|
|
82
|
+
produce<PermitsStore>((state) => {
|
|
83
|
+
if (state.permits[chainId] == null) state.permits[chainId] = {};
|
|
84
|
+
if (state.permits[chainId][account] == null) state.permits[chainId][account] = {};
|
|
85
|
+
state.permits[chainId][account][PermitUtils.getHash(permit)] = PermitUtils.serialize(permit);
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const removePermit = (chainId: number, account: string, hash: string, force?: boolean) => {
|
|
91
|
+
clearStaleStore();
|
|
92
|
+
_permitStore.setState(
|
|
93
|
+
produce<PermitsStore>((state) => {
|
|
94
|
+
if (state.permits[chainId] == null) state.permits[chainId] = {};
|
|
95
|
+
if (state.activePermitHash[chainId] == null) state.activePermitHash[chainId] = {};
|
|
96
|
+
|
|
97
|
+
const accountPermits = state.permits[chainId][account];
|
|
98
|
+
if (accountPermits == null) return;
|
|
99
|
+
|
|
100
|
+
if (accountPermits[hash] == null) return;
|
|
101
|
+
|
|
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
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Remove the permit
|
|
117
|
+
accountPermits[hash] = undefined;
|
|
118
|
+
})
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const getActivePermitHash = (chainId: number | undefined, account: string | undefined): string | undefined => {
|
|
123
|
+
clearStaleStore();
|
|
124
|
+
if (chainId == null || account == null) return undefined;
|
|
125
|
+
return _permitStore.getState().activePermitHash[chainId]?.[account];
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export const setActivePermitHash = (chainId: number, account: string, hash: string) => {
|
|
129
|
+
clearStaleStore();
|
|
130
|
+
_permitStore.setState(
|
|
131
|
+
produce<PermitsStore>((state) => {
|
|
132
|
+
if (state.activePermitHash[chainId] == null) state.activePermitHash[chainId] = {};
|
|
133
|
+
state.activePermitHash[chainId][account] = hash;
|
|
134
|
+
})
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const removeActivePermitHash = (chainId: number, account: string) => {
|
|
139
|
+
clearStaleStore();
|
|
140
|
+
_permitStore.setState(
|
|
141
|
+
produce<PermitsStore>((state) => {
|
|
142
|
+
if (state.activePermitHash[chainId]) state.activePermitHash[chainId][account] = undefined;
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const resetStore = () => {
|
|
148
|
+
clearStaleStore();
|
|
149
|
+
_permitStore.setState({ permits: {}, activePermitHash: {} });
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const permitStore = {
|
|
153
|
+
store: _permitStore,
|
|
154
|
+
|
|
155
|
+
getPermit,
|
|
156
|
+
getActivePermit,
|
|
157
|
+
getPermits,
|
|
158
|
+
setPermit,
|
|
159
|
+
removePermit,
|
|
160
|
+
|
|
161
|
+
getActivePermitHash,
|
|
162
|
+
setActivePermitHash,
|
|
163
|
+
removeActivePermitHash,
|
|
164
|
+
|
|
165
|
+
resetStore,
|
|
166
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Permit, type SerializedPermit, GenerateSealingKey, PermitUtils } from './index.js';
|
|
2
|
+
|
|
3
|
+
// Mock permit for testing - using Bob's address as issuer
|
|
4
|
+
export const createMockPermit = async (): Promise<Permit> => {
|
|
5
|
+
const sealingPair = GenerateSealingKey();
|
|
6
|
+
const serializedPermit: SerializedPermit = {
|
|
7
|
+
name: 'Test Permit',
|
|
8
|
+
type: 'self',
|
|
9
|
+
issuer: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', // Bob's address
|
|
10
|
+
expiration: 1000000000000,
|
|
11
|
+
recipient: '0x0000000000000000000000000000000000000000',
|
|
12
|
+
validatorId: 0,
|
|
13
|
+
validatorContract: '0x0000000000000000000000000000000000000000',
|
|
14
|
+
sealingPair: sealingPair.serialize(),
|
|
15
|
+
issuerSignature: '0x',
|
|
16
|
+
recipientSignature: '0x',
|
|
17
|
+
_signedDomain: undefined,
|
|
18
|
+
};
|
|
19
|
+
return PermitUtils.deserialize(serializedPermit);
|
|
20
|
+
};
|
package/permits/types.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { SealingKey as SealingKeyClass, type EthEncryptedData } from './sealing.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* EIP712 related types
|
|
5
|
+
*/
|
|
6
|
+
export type EIP712Type = { name: string; type: string };
|
|
7
|
+
export type EIP712Types = Record<string, EIP712Type[]>;
|
|
8
|
+
export type EIP712Message = Record<string, string>;
|
|
9
|
+
export type EIP712Domain = {
|
|
10
|
+
chainId: number;
|
|
11
|
+
name: string;
|
|
12
|
+
verifyingContract: `0x${string}`;
|
|
13
|
+
version: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Sealing key type - using the actual SealingKey class
|
|
18
|
+
*/
|
|
19
|
+
export type SealingKey = SealingKeyClass;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Re-export EthEncryptedData from sealing module
|
|
23
|
+
*/
|
|
24
|
+
export type { EthEncryptedData };
|
|
25
|
+
|
|
26
|
+
// Viem client types will be imported from viem package
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Core Permit interface - immutable design for React compatibility
|
|
30
|
+
*/
|
|
31
|
+
export interface Permit {
|
|
32
|
+
/**
|
|
33
|
+
* Name for this permit, for organization and UI usage, not included in signature.
|
|
34
|
+
*/
|
|
35
|
+
name: string;
|
|
36
|
+
/**
|
|
37
|
+
* The type of the Permit (self / sharing)
|
|
38
|
+
* (self) Permit that will be signed and used by the issuer
|
|
39
|
+
* (sharing) Permit that is signed by the issuer, but intended to be shared with recipient
|
|
40
|
+
* (recipient) Permit that has been received, and signed by the recipient
|
|
41
|
+
*/
|
|
42
|
+
type: 'self' | 'sharing' | 'recipient';
|
|
43
|
+
/**
|
|
44
|
+
* (base) User that initially created the permission, target of data fetching
|
|
45
|
+
*/
|
|
46
|
+
issuer: `0x${string}`;
|
|
47
|
+
/**
|
|
48
|
+
* (base) Expiration timestamp
|
|
49
|
+
*/
|
|
50
|
+
expiration: number;
|
|
51
|
+
/**
|
|
52
|
+
* (sharing) The user that this permission will be shared with
|
|
53
|
+
* ** optional, use `address(0)` to disable **
|
|
54
|
+
*/
|
|
55
|
+
recipient: `0x${string}`;
|
|
56
|
+
/**
|
|
57
|
+
* (issuer defined validation) An id used to query a contract to check this permissions validity
|
|
58
|
+
* ** optional, use `0` to disable **
|
|
59
|
+
*/
|
|
60
|
+
validatorId: number;
|
|
61
|
+
/**
|
|
62
|
+
* (issuer defined validation) The contract to query to determine permission validity
|
|
63
|
+
* ** optional, user `address(0)` to disable **
|
|
64
|
+
*/
|
|
65
|
+
validatorContract: `0x${string}`;
|
|
66
|
+
/**
|
|
67
|
+
* (base) The publicKey of a sealingPair used to re-encrypt `issuer`s confidential data
|
|
68
|
+
* (non-sharing) Populated by `issuer`
|
|
69
|
+
* (sharing) Populated by `recipient`
|
|
70
|
+
*/
|
|
71
|
+
sealingPair: SealingKey;
|
|
72
|
+
/**
|
|
73
|
+
* (base) `signTypedData` signature created by `issuer`.
|
|
74
|
+
* (base) Shared- and Self- permissions differ in signature format: (`sealingKey` absent in shared signature)
|
|
75
|
+
* (non-sharing) < issuer, expiration, recipient, validatorId, validatorContract, sealingKey >
|
|
76
|
+
* (sharing) < issuer, expiration, recipient, validatorId, validatorContract >
|
|
77
|
+
*/
|
|
78
|
+
issuerSignature: `0x${string}`;
|
|
79
|
+
/**
|
|
80
|
+
* (sharing) `signTypedData` signature created by `recipient` with format:
|
|
81
|
+
* (sharing) < sealingKey, issuerSignature>
|
|
82
|
+
* ** required for shared permits **
|
|
83
|
+
*/
|
|
84
|
+
recipientSignature: `0x${string}`;
|
|
85
|
+
/**
|
|
86
|
+
* EIP712 domain used to sign this permit.
|
|
87
|
+
* Should not be set manually, included in metadata as part of serialization flows.
|
|
88
|
+
*/
|
|
89
|
+
_signedDomain?: EIP712Domain;
|
|
90
|
+
}
|
|
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
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Optional additional metadata of a Permit
|
|
111
|
+
* Can be passed into the constructor, but not necessary
|
|
112
|
+
* Useful for deserialization
|
|
113
|
+
*/
|
|
114
|
+
export interface PermitMetadata {
|
|
115
|
+
/**
|
|
116
|
+
* EIP712 domain used to sign this permit.
|
|
117
|
+
* Should not be set manually, included in metadata as part of serialization flows.
|
|
118
|
+
*/
|
|
119
|
+
_signedDomain?: EIP712Domain;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Utility types for permit creation
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
// Specific option types for each permit creation method
|
|
127
|
+
export type CreateSelfPermitOptions = {
|
|
128
|
+
type?: 'self';
|
|
129
|
+
issuer: string;
|
|
130
|
+
name?: string;
|
|
131
|
+
expiration?: number;
|
|
132
|
+
validatorId?: number;
|
|
133
|
+
validatorContract?: string;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export type CreateSharingPermitOptions = {
|
|
137
|
+
type?: 'sharing';
|
|
138
|
+
issuer: string;
|
|
139
|
+
recipient: string;
|
|
140
|
+
name?: string;
|
|
141
|
+
expiration?: number;
|
|
142
|
+
validatorId?: number;
|
|
143
|
+
validatorContract?: string;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export type ImportSharedPermitOptions = {
|
|
147
|
+
type?: 'sharing';
|
|
148
|
+
issuer: string;
|
|
149
|
+
recipient: string;
|
|
150
|
+
issuerSignature: string;
|
|
151
|
+
name?: string;
|
|
152
|
+
expiration?: number;
|
|
153
|
+
validatorId?: number;
|
|
154
|
+
validatorContract?: string;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export type SerializedPermit = Omit<Permit, 'sealingPair'> & {
|
|
158
|
+
_signedDomain?: EIP712Domain;
|
|
159
|
+
sealingPair: {
|
|
160
|
+
privateKey: string;
|
|
161
|
+
publicKey: string;
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* A type representing the Permission struct that is passed to Permissioned.sol to grant encrypted data access.
|
|
167
|
+
*/
|
|
168
|
+
export type Permission = Expand<
|
|
169
|
+
Omit<Permit, 'name' | 'type' | 'sealingPair'> & {
|
|
170
|
+
sealingKey: `0x${string}`;
|
|
171
|
+
}
|
|
172
|
+
>;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Validation result type
|
|
176
|
+
*/
|
|
177
|
+
export interface ValidationResult {
|
|
178
|
+
valid: boolean;
|
|
179
|
+
error: string | null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Signature types for EIP712 signing
|
|
184
|
+
*/
|
|
185
|
+
export type PermitSignaturePrimaryType =
|
|
186
|
+
| 'PermissionedV2IssuerSelf'
|
|
187
|
+
| 'PermissionedV2IssuerShared'
|
|
188
|
+
| 'PermissionedV2Recipient';
|
|
189
|
+
|
|
190
|
+
// Utils
|
|
191
|
+
export type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
package/permits/utils.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Utility functions for sealing key operations
|
|
2
|
+
|
|
3
|
+
declare const BigInt: (value: string | number | bigint) => bigint;
|
|
4
|
+
|
|
5
|
+
export const fromHexString = (hexString: string): Uint8Array => {
|
|
6
|
+
const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
|
|
7
|
+
const arr = cleanString.replace(/^0x/, '').match(/.{1,2}/g);
|
|
8
|
+
if (!arr) return new Uint8Array();
|
|
9
|
+
return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const toHexString = (bytes: Uint8Array) =>
|
|
13
|
+
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
|
|
14
|
+
|
|
15
|
+
export function toBigInt(value: number | string | bigint | Uint8Array): bigint {
|
|
16
|
+
if (typeof value === 'string') {
|
|
17
|
+
return BigInt(value);
|
|
18
|
+
} else if (typeof value === 'number') {
|
|
19
|
+
return BigInt(value);
|
|
20
|
+
} else if (typeof value === 'object') {
|
|
21
|
+
// Uint8Array
|
|
22
|
+
return BigInt('0x' + toHexString(value));
|
|
23
|
+
} else {
|
|
24
|
+
return value as bigint;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function toBeArray(value: bigint | number): Uint8Array {
|
|
29
|
+
const bigIntValue = typeof value === 'number' ? BigInt(value) : value;
|
|
30
|
+
const hex = bigIntValue.toString(16);
|
|
31
|
+
const paddedHex = hex.length % 2 === 0 ? hex : '0' + hex;
|
|
32
|
+
return fromHexString(paddedHex);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function isString(value: unknown) {
|
|
36
|
+
if (typeof value !== 'string') {
|
|
37
|
+
throw new Error(`Expected value which is \`string\`, received value of type \`${typeof value}\`.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isNumber(value: unknown) {
|
|
42
|
+
const is = typeof value === 'number' && !Number.isNaN(value);
|
|
43
|
+
if (!is) {
|
|
44
|
+
throw new Error(`Expected value which is \`number\`, received value of type \`${typeof value}\`.`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function isBigIntOrNumber(value: unknown) {
|
|
49
|
+
const is = typeof value === 'bigint';
|
|
50
|
+
|
|
51
|
+
if (!is) {
|
|
52
|
+
try {
|
|
53
|
+
isNumber(value);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
throw new Error(`Value ${value} is not a number or bigint: ${typeof value}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function is0xPrefixed(value: string): value is `0x${string}` {
|
|
61
|
+
return value.startsWith('0x');
|
|
62
|
+
}
|