@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
|
@@ -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.1",
|
|
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": "^4.0.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"
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"build": "tsup",
|
|
112
112
|
"dev": "tsup --watch",
|
|
113
113
|
"lint": "eslint \"**/*.ts*\"",
|
|
114
|
-
"
|
|
114
|
+
"check:types": "tsc --noEmit",
|
|
115
115
|
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
|
|
116
116
|
"test": "vitest run",
|
|
117
117
|
"test:watch": "vitest",
|
package/permits/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @vitest-environment happy-dom
|
|
3
3
|
*/
|
|
4
|
-
/* eslint-disable no-unused-vars */
|
|
5
4
|
|
|
6
5
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
7
6
|
import {
|
|
@@ -40,7 +39,6 @@ describe('Permits localStorage Tests', () => {
|
|
|
40
39
|
|
|
41
40
|
it('should persist permits to localStorage', async () => {
|
|
42
41
|
const permit = await createMockPermit();
|
|
43
|
-
const hash = PermitUtils.getHash(permit);
|
|
44
42
|
|
|
45
43
|
setPermit(chainId, account, permit);
|
|
46
44
|
|
|
@@ -49,40 +47,38 @@ describe('Permits localStorage Tests', () => {
|
|
|
49
47
|
expect(storedData).toBeDefined();
|
|
50
48
|
|
|
51
49
|
const parsedData = JSON.parse(storedData!);
|
|
52
|
-
expect(parsedData.state.permits[chainId][account][hash]).toBeDefined();
|
|
50
|
+
expect(parsedData.state.permits[chainId][account][permit.hash]).toBeDefined();
|
|
53
51
|
});
|
|
54
52
|
|
|
55
53
|
it('should persist active permit hash to localStorage', async () => {
|
|
56
54
|
const permit = await createMockPermit();
|
|
57
|
-
const hash = PermitUtils.getHash(permit);
|
|
58
55
|
|
|
59
56
|
setPermit(chainId, account, permit);
|
|
60
|
-
setActivePermitHash(chainId, account, hash);
|
|
57
|
+
setActivePermitHash(chainId, account, permit.hash);
|
|
61
58
|
|
|
62
59
|
// Verify active permit hash is stored
|
|
63
60
|
const storedData = localStorage.getItem('cofhesdk-permits');
|
|
64
61
|
expect(storedData).toBeDefined();
|
|
65
62
|
|
|
66
63
|
const parsedData = JSON.parse(storedData!);
|
|
67
|
-
expect(parsedData.state.activePermitHash[chainId][account]).toBe(hash);
|
|
64
|
+
expect(parsedData.state.activePermitHash[chainId][account]).toBe(permit.hash);
|
|
68
65
|
});
|
|
69
66
|
|
|
70
67
|
it('should restore permits from localStorage', async () => {
|
|
71
68
|
const permit = await createMockPermit();
|
|
72
|
-
const hash = PermitUtils.getHash(permit);
|
|
73
69
|
|
|
74
70
|
// Add permit to localStorage
|
|
75
71
|
setPermit(chainId, account, permit);
|
|
76
|
-
setActivePermitHash(chainId, account, hash);
|
|
72
|
+
setActivePermitHash(chainId, account, permit.hash);
|
|
77
73
|
const serializedPermit = PermitUtils.serialize(permit);
|
|
78
74
|
|
|
79
75
|
// Verify data is restored
|
|
80
|
-
const retrievedPermit = getPermit(chainId, account, hash);
|
|
76
|
+
const retrievedPermit = getPermit(chainId, account, permit.hash);
|
|
81
77
|
expect(retrievedPermit).toBeDefined();
|
|
82
78
|
expect(PermitUtils.serialize(retrievedPermit!)).toEqual(serializedPermit);
|
|
83
79
|
|
|
84
80
|
const activeHash = getActivePermitHash(chainId, account);
|
|
85
|
-
expect(activeHash).toBe(hash);
|
|
81
|
+
expect(activeHash).toBe(permit.hash);
|
|
86
82
|
});
|
|
87
83
|
|
|
88
84
|
it('should handle corrupted localStorage data gracefully', () => {
|
|
@@ -97,22 +93,21 @@ describe('Permits localStorage Tests', () => {
|
|
|
97
93
|
|
|
98
94
|
it('should clean up localStorage when permits are removed', async () => {
|
|
99
95
|
const permit = await createMockPermit();
|
|
100
|
-
const hash = PermitUtils.getHash(permit);
|
|
101
96
|
|
|
102
97
|
setPermit(chainId, account, permit);
|
|
103
|
-
setActivePermitHash(chainId, account, hash);
|
|
98
|
+
setActivePermitHash(chainId, account, permit.hash);
|
|
104
99
|
|
|
105
100
|
// Verify data exists
|
|
106
101
|
let storedData = localStorage.getItem('cofhesdk-permits');
|
|
107
102
|
expect(storedData).toBeDefined();
|
|
108
103
|
|
|
109
104
|
// Remove permit
|
|
110
|
-
removePermit(chainId, account, hash
|
|
105
|
+
removePermit(chainId, account, permit.hash);
|
|
111
106
|
|
|
112
107
|
// Verify data is cleaned up
|
|
113
108
|
storedData = localStorage.getItem('cofhesdk-permits');
|
|
114
109
|
const parsedData = JSON.parse(storedData!);
|
|
115
|
-
expect(parsedData.state.permits[chainId][account][hash]).toBeUndefined();
|
|
110
|
+
expect(parsedData.state.permits[chainId][account][permit.hash]).toBeUndefined();
|
|
116
111
|
expect(parsedData.state.activePermitHash[chainId][account]).toBeUndefined();
|
|
117
112
|
});
|
|
118
113
|
});
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseError,
|
|
3
|
+
ContractFunctionRevertedError,
|
|
4
|
+
type Hex,
|
|
5
|
+
type PublicClient,
|
|
6
|
+
decodeErrorResult,
|
|
7
|
+
parseAbi,
|
|
8
|
+
} from 'viem';
|
|
9
|
+
import type { EIP712Domain, Permission } from './types';
|
|
10
|
+
import { TASK_MANAGER_ADDRESS } from '../core/consts.js';
|
|
11
|
+
|
|
12
|
+
export const getAclAddress = async (publicClient: PublicClient): Promise<Hex> => {
|
|
13
|
+
const ACL_IFACE = 'function acl() view returns (address)';
|
|
14
|
+
|
|
15
|
+
// Parse the ABI for the ACL function
|
|
16
|
+
const aclAbi = parseAbi([ACL_IFACE]);
|
|
17
|
+
|
|
18
|
+
// Get the ACL address
|
|
19
|
+
return (await publicClient.readContract({
|
|
20
|
+
address: TASK_MANAGER_ADDRESS as `0x${string}`,
|
|
21
|
+
abi: aclAbi,
|
|
22
|
+
functionName: 'acl',
|
|
23
|
+
})) as `0x${string}`;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const getAclEIP712Domain = async (publicClient: PublicClient): Promise<EIP712Domain> => {
|
|
27
|
+
const aclAddress = await getAclAddress(publicClient);
|
|
28
|
+
const EIP712_DOMAIN_IFACE =
|
|
29
|
+
'function eip712Domain() public view returns (bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions)';
|
|
30
|
+
|
|
31
|
+
// Parse the ABI for the EIP712 domain function
|
|
32
|
+
const domainAbi = parseAbi([EIP712_DOMAIN_IFACE]);
|
|
33
|
+
|
|
34
|
+
// Get the EIP712 domain
|
|
35
|
+
const domain = await publicClient.readContract({
|
|
36
|
+
address: aclAddress,
|
|
37
|
+
abi: domainAbi,
|
|
38
|
+
functionName: 'eip712Domain',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// eslint-disable-next-line no-unused-vars
|
|
42
|
+
const [_fields, name, version, chainId, verifyingContract, _salt, _extensions] = domain;
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
name,
|
|
46
|
+
version,
|
|
47
|
+
chainId: Number(chainId),
|
|
48
|
+
verifyingContract,
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const checkPermitValidityOnChain = async (
|
|
53
|
+
permission: Permission,
|
|
54
|
+
publicClient: PublicClient
|
|
55
|
+
): Promise<boolean> => {
|
|
56
|
+
const aclAddress = await getAclAddress(publicClient);
|
|
57
|
+
|
|
58
|
+
// Check if the permit is valid
|
|
59
|
+
try {
|
|
60
|
+
await publicClient.simulateContract({
|
|
61
|
+
address: aclAddress,
|
|
62
|
+
abi: checkPermitValidityAbi,
|
|
63
|
+
functionName: 'checkPermitValidity',
|
|
64
|
+
args: [
|
|
65
|
+
{
|
|
66
|
+
issuer: permission.issuer,
|
|
67
|
+
expiration: BigInt(permission.expiration),
|
|
68
|
+
recipient: permission.recipient,
|
|
69
|
+
validatorId: BigInt(permission.validatorId),
|
|
70
|
+
validatorContract: permission.validatorContract,
|
|
71
|
+
sealingKey: permission.sealingKey,
|
|
72
|
+
issuerSignature: permission.issuerSignature,
|
|
73
|
+
recipientSignature: permission.recipientSignature,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
return true;
|
|
78
|
+
} catch (err: any) {
|
|
79
|
+
// Viem default handling
|
|
80
|
+
if (err instanceof BaseError) {
|
|
81
|
+
const revertError = err.walk((err: any) => err instanceof ContractFunctionRevertedError);
|
|
82
|
+
if (revertError instanceof ContractFunctionRevertedError) {
|
|
83
|
+
const errorName = revertError.data?.errorName ?? '';
|
|
84
|
+
throw new Error(errorName);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check details field for custom error names (e.g., from Hardhat test nodes)
|
|
89
|
+
const customErrorName = extractCustomErrorFromDetails(err, checkPermitValidityAbi);
|
|
90
|
+
if (customErrorName) {
|
|
91
|
+
throw new Error(customErrorName);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Hardhat wrapped error will need to be unwrapped to get the return data
|
|
95
|
+
const hhDetailsData = extractReturnData(err);
|
|
96
|
+
if (hhDetailsData != null) {
|
|
97
|
+
const decoded = decodeErrorResult({
|
|
98
|
+
abi: checkPermitValidityAbi,
|
|
99
|
+
data: hhDetailsData,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
throw new Error(decoded.errorName);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Fallback throw the original error
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
function extractCustomErrorFromDetails(err: unknown, abi: readonly any[]): string | undefined {
|
|
111
|
+
// Check details field for custom error names (e.g., from Hardhat test nodes)
|
|
112
|
+
const anyErr = err as any;
|
|
113
|
+
const details = anyErr?.details ?? anyErr?.cause?.details;
|
|
114
|
+
|
|
115
|
+
if (typeof details === 'string') {
|
|
116
|
+
// Match pattern: "reverted with custom error 'ErrorName()'"
|
|
117
|
+
const customErrorMatch = details.match(/reverted with custom error '(\w+)\(\)'/);
|
|
118
|
+
if (customErrorMatch) {
|
|
119
|
+
const errorName = customErrorMatch[1];
|
|
120
|
+
// Check if this error exists in our ABI
|
|
121
|
+
const errorExists = abi.some((item) => item.type === 'error' && item.name === errorName);
|
|
122
|
+
if (errorExists) {
|
|
123
|
+
return errorName;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function extractReturnData(err: unknown): `0x${string}` | undefined {
|
|
132
|
+
// viem BaseError has `details`, but fall back to any message-like string we can find
|
|
133
|
+
const anyErr = err as any;
|
|
134
|
+
const s = anyErr?.details ?? anyErr?.cause?.details ?? anyErr?.shortMessage ?? anyErr?.message ?? String(err);
|
|
135
|
+
|
|
136
|
+
return s.match(/return data:\s*(0x[a-fA-F0-9]+)/)?.[1] as `0x${string}` | undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const checkPermitValidityAbi = [
|
|
140
|
+
{
|
|
141
|
+
type: 'function',
|
|
142
|
+
name: 'checkPermitValidity',
|
|
143
|
+
inputs: [
|
|
144
|
+
{
|
|
145
|
+
name: 'permission',
|
|
146
|
+
type: 'tuple',
|
|
147
|
+
internalType: 'struct Permission',
|
|
148
|
+
components: [
|
|
149
|
+
{
|
|
150
|
+
name: 'issuer',
|
|
151
|
+
type: 'address',
|
|
152
|
+
internalType: 'address',
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: 'expiration',
|
|
156
|
+
type: 'uint64',
|
|
157
|
+
internalType: 'uint64',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'recipient',
|
|
161
|
+
type: 'address',
|
|
162
|
+
internalType: 'address',
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'validatorId',
|
|
166
|
+
type: 'uint256',
|
|
167
|
+
internalType: 'uint256',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'validatorContract',
|
|
171
|
+
type: 'address',
|
|
172
|
+
internalType: 'address',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'sealingKey',
|
|
176
|
+
type: 'bytes32',
|
|
177
|
+
internalType: 'bytes32',
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: 'issuerSignature',
|
|
181
|
+
type: 'bytes',
|
|
182
|
+
internalType: 'bytes',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: 'recipientSignature',
|
|
186
|
+
type: 'bytes',
|
|
187
|
+
internalType: 'bytes',
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
outputs: [
|
|
193
|
+
{
|
|
194
|
+
name: '',
|
|
195
|
+
type: 'bool',
|
|
196
|
+
internalType: 'bool',
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
stateMutability: 'view',
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
type: 'error',
|
|
203
|
+
name: 'PermissionInvalid_Disabled',
|
|
204
|
+
inputs: [],
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
type: 'error',
|
|
208
|
+
name: 'PermissionInvalid_Expired',
|
|
209
|
+
inputs: [],
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
type: 'error',
|
|
213
|
+
name: 'PermissionInvalid_IssuerSignature',
|
|
214
|
+
inputs: [],
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
type: 'error',
|
|
218
|
+
name: 'PermissionInvalid_RecipientSignature',
|
|
219
|
+
inputs: [],
|
|
220
|
+
},
|
|
221
|
+
] as const;
|