@cofhe/sdk 0.0.0-alpha-20260409113701
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 +146 -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 +429 -0
- package/core/client.ts +341 -0
- package/core/clientTypes.ts +119 -0
- package/core/config.test.ts +242 -0
- package/core/config.ts +225 -0
- package/core/consts.ts +22 -0
- package/core/decrypt/MockThresholdNetworkAbi.ts +179 -0
- package/core/decrypt/cofheMocksDecryptForTx.ts +84 -0
- package/core/decrypt/cofheMocksDecryptForView.ts +48 -0
- package/core/decrypt/decryptForTxBuilder.ts +359 -0
- package/core/decrypt/decryptForViewBuilder.ts +332 -0
- package/core/decrypt/decryptUtils.ts +28 -0
- package/core/decrypt/pollCallbacks.test.ts +194 -0
- package/core/decrypt/polling.ts +14 -0
- package/core/decrypt/tnDecryptUtils.ts +65 -0
- package/core/decrypt/tnDecryptV1.ts +171 -0
- package/core/decrypt/tnDecryptV2.ts +365 -0
- package/core/decrypt/tnSealOutputV1.ts +59 -0
- package/core/decrypt/tnSealOutputV2.ts +324 -0
- package/core/decrypt/verifyDecryptResult.ts +52 -0
- package/core/encrypt/MockZkVerifierAbi.ts +106 -0
- package/core/encrypt/cofheMocksZkVerifySign.ts +281 -0
- package/core/encrypt/encryptInputsBuilder.test.ts +747 -0
- package/core/encrypt/encryptInputsBuilder.ts +583 -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 +106 -0
- package/core/keyStore.test.ts +226 -0
- package/core/keyStore.ts +154 -0
- package/core/permits.test.ts +493 -0
- package/core/permits.ts +201 -0
- package/core/types.ts +419 -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 +111 -0
- package/dist/chains.d.cts +121 -0
- package/dist/chains.d.ts +121 -0
- package/dist/chains.js +1 -0
- package/dist/chunk-36FBWLUS.js +3310 -0
- package/dist/chunk-7HLGHV67.js +990 -0
- package/dist/chunk-TBLR7NNE.js +102 -0
- package/dist/clientTypes-AVSCBet7.d.cts +998 -0
- package/dist/clientTypes-flH1ju82.d.ts +998 -0
- package/dist/core.cjs +4362 -0
- package/dist/core.d.cts +138 -0
- package/dist/core.d.ts +138 -0
- package/dist/core.js +3 -0
- package/dist/node.cjs +4225 -0
- package/dist/node.d.cts +22 -0
- package/dist/node.d.ts +22 -0
- package/dist/node.js +91 -0
- package/dist/permit-jRirYqFt.d.cts +376 -0
- package/dist/permit-jRirYqFt.d.ts +376 -0
- package/dist/permits.cjs +1025 -0
- package/dist/permits.d.cts +353 -0
- package/dist/permits.d.ts +353 -0
- package/dist/permits.js +1 -0
- package/dist/types-YiAC4gig.d.cts +33 -0
- package/dist/types-YiAC4gig.d.ts +33 -0
- package/dist/web.cjs +4434 -0
- package/dist/web.d.cts +42 -0
- package/dist/web.d.ts +42 -0
- package/dist/web.js +256 -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 +159 -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 +121 -0
- package/permits/index.ts +68 -0
- package/permits/localstorage.test.ts +113 -0
- package/permits/onchain-utils.ts +221 -0
- package/permits/permit.test.ts +534 -0
- package/permits/permit.ts +386 -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 +88 -0
- package/permits/store.ts +156 -0
- package/permits/test-utils.ts +28 -0
- package/permits/types.ts +204 -0
- package/permits/utils.ts +58 -0
- package/permits/validation.test.ts +361 -0
- package/permits/validation.ts +327 -0
- package/web/client.web.test.ts +159 -0
- package/web/config.web.test.ts +69 -0
- package/web/const.ts +2 -0
- package/web/encryptInputs.web.test.ts +172 -0
- package/web/index.ts +166 -0
- package/web/storage.ts +49 -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,167 @@
|
|
|
1
|
+
import { spawn, ChildProcess } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import { createConnection } from 'net';
|
|
4
|
+
|
|
5
|
+
const sleep = promisify(setTimeout);
|
|
6
|
+
|
|
7
|
+
async function isPortAvailable(port: number): Promise<boolean> {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
const connection = createConnection({ port, host: '127.0.0.1' });
|
|
10
|
+
|
|
11
|
+
connection.on('connect', () => {
|
|
12
|
+
connection.destroy();
|
|
13
|
+
resolve(false); // Port is in use
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
connection.on('error', () => {
|
|
17
|
+
resolve(true); // Port is available
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function testHardhatNode(port: number): Promise<boolean> {
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetch(`http://127.0.0.1:${port}`, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
jsonrpc: '2.0',
|
|
29
|
+
method: 'eth_chainId',
|
|
30
|
+
params: [],
|
|
31
|
+
id: 1,
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (response.ok) {
|
|
36
|
+
const data = (await response.json()) as { result: string };
|
|
37
|
+
// Check if it returns Hardhat's chain ID (31337 = 0x7a69)
|
|
38
|
+
return data.result === '0x7a69';
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class HardhatNode {
|
|
47
|
+
private process: ChildProcess | null = null;
|
|
48
|
+
private isReady = false;
|
|
49
|
+
|
|
50
|
+
async start(): Promise<void> {
|
|
51
|
+
if (this.process) {
|
|
52
|
+
return; // Already started
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check if port 8545 is already in use
|
|
56
|
+
const portAvailable = await isPortAvailable(8545);
|
|
57
|
+
if (!portAvailable) {
|
|
58
|
+
// Port is in use, check if it's a Hardhat node we can use
|
|
59
|
+
const isHardhatNode = await testHardhatNode(8545);
|
|
60
|
+
if (isHardhatNode) {
|
|
61
|
+
console.log('Found existing Hardhat node on port 8545, using it...');
|
|
62
|
+
this.isReady = true;
|
|
63
|
+
return; // Use the existing node
|
|
64
|
+
} else {
|
|
65
|
+
throw new Error('Port 8545 is in use by a non-Hardhat service. Please free the port.');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log('Starting Hardhat node...');
|
|
70
|
+
|
|
71
|
+
this.process = spawn('npx', ['hardhat', 'node'], {
|
|
72
|
+
cwd: process.cwd(),
|
|
73
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Wait for the node to be ready
|
|
77
|
+
await new Promise<void>((resolve, reject) => {
|
|
78
|
+
const timeout = setTimeout(() => {
|
|
79
|
+
reject(new Error('Hardhat node failed to start within 30 seconds'));
|
|
80
|
+
}, 30000);
|
|
81
|
+
|
|
82
|
+
const onData = (data: Buffer) => {
|
|
83
|
+
const output = data.toString();
|
|
84
|
+
if (output.includes('Started HTTP and WebSocket JSON-RPC server at')) {
|
|
85
|
+
clearTimeout(timeout);
|
|
86
|
+
this.isReady = true;
|
|
87
|
+
console.log('Hardhat node is ready!');
|
|
88
|
+
resolve();
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
this.process!.stdout?.on('data', onData);
|
|
93
|
+
this.process!.stderr?.on('data', onData);
|
|
94
|
+
|
|
95
|
+
this.process!.on('error', (error) => {
|
|
96
|
+
clearTimeout(timeout);
|
|
97
|
+
reject(error);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
this.process!.on('exit', (code) => {
|
|
101
|
+
if (code !== 0 && !this.isReady) {
|
|
102
|
+
clearTimeout(timeout);
|
|
103
|
+
reject(new Error(`Hardhat node exited with code ${code}`));
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Give it a bit more time to be fully ready
|
|
109
|
+
await sleep(2000);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async stop(): Promise<void> {
|
|
113
|
+
if (this.process) {
|
|
114
|
+
console.log('Stopping Hardhat node...');
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
// Immediately force kill - no graceful shutdown to avoid hanging
|
|
118
|
+
this.process.kill('SIGKILL');
|
|
119
|
+
this.process = null;
|
|
120
|
+
this.isReady = false;
|
|
121
|
+
|
|
122
|
+
// Quick port cleanup
|
|
123
|
+
await this.killProcessOnPort(8545);
|
|
124
|
+
|
|
125
|
+
console.log('Hardhat node stopped');
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.log('Error stopping node:', error);
|
|
128
|
+
// Force cleanup regardless
|
|
129
|
+
this.process = null;
|
|
130
|
+
this.isReady = false;
|
|
131
|
+
await this.killProcessOnPort(8545);
|
|
132
|
+
}
|
|
133
|
+
} else if (this.isReady) {
|
|
134
|
+
// We're using an existing node, just mark as not ready
|
|
135
|
+
console.log('Using external Hardhat node, not stopping it...');
|
|
136
|
+
this.isReady = false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
isRunning(): boolean {
|
|
141
|
+
return this.process !== null && this.isReady;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private async killProcessOnPort(port: number): Promise<void> {
|
|
145
|
+
try {
|
|
146
|
+
const { spawn } = await import('child_process');
|
|
147
|
+
|
|
148
|
+
// Kill any process using the port (Linux/macOS) - faster timeout
|
|
149
|
+
const killProcess = spawn('sh', ['-c', `lsof -ti :${port} | xargs -r kill -9`], {
|
|
150
|
+
stdio: 'ignore',
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await new Promise<void>((resolve) => {
|
|
154
|
+
killProcess.on('exit', () => resolve());
|
|
155
|
+
killProcess.on('error', () => resolve()); // Ignore errors
|
|
156
|
+
setTimeout(resolve, 300); // Shorter timeout - 300ms
|
|
157
|
+
});
|
|
158
|
+
} catch {
|
|
159
|
+
// Ignore errors - this is a cleanup attempt
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Global instance
|
|
165
|
+
export const hardhatNode = new HardhatNode();
|
|
166
|
+
|
|
167
|
+
// Global instance - no process handlers to avoid interfering with test process
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import { parseEther } from 'viem';
|
|
3
|
+
import { hardhat } from 'viem/chains';
|
|
4
|
+
import { HardhatSignerAdapter } from './hardhat.js';
|
|
5
|
+
import hre from 'hardhat';
|
|
6
|
+
import '@nomicfoundation/hardhat-ethers';
|
|
7
|
+
import type { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers.js';
|
|
8
|
+
import { hardhatNode } from './hardhat-node.js';
|
|
9
|
+
|
|
10
|
+
describe('HardhatSignerAdapter', () => {
|
|
11
|
+
const HARDHAT_CHAIN_ID = 31337; // Hardhat local network
|
|
12
|
+
let signer: HardhatEthersSigner;
|
|
13
|
+
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
// Start Hardhat node before running tests
|
|
16
|
+
// await hardhatNode.start()
|
|
17
|
+
}, 60000); // 60 second timeout for node startup
|
|
18
|
+
|
|
19
|
+
afterAll(async () => {
|
|
20
|
+
// Immediate cleanup - no waiting
|
|
21
|
+
console.log('Starting cleanup...');
|
|
22
|
+
if ((hardhatNode as any).process) {
|
|
23
|
+
try {
|
|
24
|
+
(hardhatNode as any).process.kill('SIGKILL');
|
|
25
|
+
(hardhatNode as any).process = null;
|
|
26
|
+
(hardhatNode as any).isReady = false;
|
|
27
|
+
} catch (e) {
|
|
28
|
+
console.log('Kill error:', e);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Force port cleanup
|
|
33
|
+
try {
|
|
34
|
+
const { spawn } = await import('child_process');
|
|
35
|
+
const killCmd = spawn('sh', ['-c', 'lsof -ti :8545 | xargs -r kill -9'], { stdio: 'ignore' });
|
|
36
|
+
setTimeout(() => killCmd.kill('SIGKILL'), 1000); // Kill the kill command after 1s
|
|
37
|
+
} catch (e) {
|
|
38
|
+
console.log('Port cleanup error:', e);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log('Cleanup done');
|
|
42
|
+
}, 3000); // 3 second timeout
|
|
43
|
+
|
|
44
|
+
beforeEach(async () => {
|
|
45
|
+
// Use real Hardhat runtime environment
|
|
46
|
+
const [firstSigner] = await hre.ethers.getSigners();
|
|
47
|
+
signer = firstSigner;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should work with Hardhat signer', async () => {
|
|
51
|
+
const result = await HardhatSignerAdapter(signer);
|
|
52
|
+
|
|
53
|
+
expect(result).toHaveProperty('publicClient');
|
|
54
|
+
expect(result).toHaveProperty('walletClient');
|
|
55
|
+
expect(result.publicClient).toBeDefined();
|
|
56
|
+
expect(result.walletClient).toBeDefined();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should work without configuration', async () => {
|
|
60
|
+
const result = await HardhatSignerAdapter(signer);
|
|
61
|
+
|
|
62
|
+
expect(result).toHaveProperty('publicClient');
|
|
63
|
+
expect(result).toHaveProperty('walletClient');
|
|
64
|
+
expect(result.publicClient).toBeDefined();
|
|
65
|
+
expect(result.walletClient).toBeDefined();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should throw error when signer has no provider', async () => {
|
|
69
|
+
const signerWithoutProvider = { provider: null };
|
|
70
|
+
|
|
71
|
+
await expect(async () => {
|
|
72
|
+
await HardhatSignerAdapter(signerWithoutProvider as any);
|
|
73
|
+
}).rejects.toThrow('Signer must have a provider');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('Provider Functions', () => {
|
|
77
|
+
it('should support getChainId', async () => {
|
|
78
|
+
const { publicClient } = await HardhatSignerAdapter(signer);
|
|
79
|
+
|
|
80
|
+
const chainId = await publicClient.getChainId();
|
|
81
|
+
expect(typeof chainId).toBe('number');
|
|
82
|
+
expect(chainId).toBe(HARDHAT_CHAIN_ID); // Hardhat local network
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should support call (contract read)', async () => {
|
|
86
|
+
const { publicClient } = await HardhatSignerAdapter(signer);
|
|
87
|
+
|
|
88
|
+
// Test eth_call via getBalance
|
|
89
|
+
const balance = await publicClient.getBalance({
|
|
90
|
+
address: '0x0000000000000000000000000000000000000000',
|
|
91
|
+
});
|
|
92
|
+
expect(typeof balance).toBe('bigint');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should support request (raw RPC)', async () => {
|
|
96
|
+
const { publicClient } = await HardhatSignerAdapter(signer);
|
|
97
|
+
|
|
98
|
+
// Test raw RPC request
|
|
99
|
+
const blockNumber = (await publicClient.request({
|
|
100
|
+
method: 'eth_blockNumber',
|
|
101
|
+
})) as string;
|
|
102
|
+
expect(typeof blockNumber).toBe('string');
|
|
103
|
+
expect(blockNumber.startsWith('0x')).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('Signer Functions', () => {
|
|
108
|
+
it('should support getAddress', async () => {
|
|
109
|
+
const { walletClient } = await HardhatSignerAdapter(signer);
|
|
110
|
+
|
|
111
|
+
const addresses = await walletClient.getAddresses();
|
|
112
|
+
expect(Array.isArray(addresses)).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should support signTypedData', async () => {
|
|
116
|
+
const { walletClient } = await HardhatSignerAdapter(signer);
|
|
117
|
+
|
|
118
|
+
const domain = {
|
|
119
|
+
name: 'Test',
|
|
120
|
+
version: '1',
|
|
121
|
+
chainId: HARDHAT_CHAIN_ID,
|
|
122
|
+
verifyingContract: '0x0000000000000000000000000000000000000000' as const,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const types = {
|
|
126
|
+
Message: [{ name: 'content', type: 'string' }],
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const message = { content: 'Hello World' };
|
|
130
|
+
|
|
131
|
+
const signature = await walletClient.signTypedData({
|
|
132
|
+
account: (await signer.getAddress()) as `0x${string}`,
|
|
133
|
+
domain,
|
|
134
|
+
types,
|
|
135
|
+
primaryType: 'Message',
|
|
136
|
+
message,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(typeof signature).toBe('string');
|
|
140
|
+
expect(signature.startsWith('0x')).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should support sendTransaction', async () => {
|
|
144
|
+
const { walletClient } = await HardhatSignerAdapter(signer);
|
|
145
|
+
|
|
146
|
+
const hash = await walletClient.sendTransaction({
|
|
147
|
+
account: (await signer.getAddress()) as `0x${string}`,
|
|
148
|
+
to: '0x0000000000000000000000000000000000000000' as `0x${string}`,
|
|
149
|
+
value: parseEther('0'),
|
|
150
|
+
chain: hardhat, // Provide chain directly in the call
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Should succeed with Hardhat local network (has funds)
|
|
154
|
+
expect(typeof hash).toBe('string');
|
|
155
|
+
expect(hash.startsWith('0x')).toBe(true);
|
|
156
|
+
expect(hash.length).toBe(66);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createPublicClient, createWalletClient, custom } from 'viem';
|
|
2
|
+
import { type AdapterResult } from './types.js';
|
|
3
|
+
import type { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers.js';
|
|
4
|
+
|
|
5
|
+
export async function HardhatSignerAdapter(signer: HardhatEthersSigner): Promise<AdapterResult> {
|
|
6
|
+
// Get provider from signer
|
|
7
|
+
const provider = signer.provider;
|
|
8
|
+
if (!provider) {
|
|
9
|
+
throw new Error('Signer must have a provider');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Create transport from provider (Hardhat providers are EIP-1193 compatible)
|
|
13
|
+
const transport = custom({
|
|
14
|
+
request: async ({ method, params }: { method: string; params?: unknown[] }) => {
|
|
15
|
+
if ('request' in provider && typeof provider.request === 'function') {
|
|
16
|
+
return await provider.request({ method, params });
|
|
17
|
+
} else if ('send' in provider && typeof provider.send === 'function') {
|
|
18
|
+
return await (provider as { send: (method: string, params?: unknown[]) => Promise<unknown> }).send(
|
|
19
|
+
method,
|
|
20
|
+
params || []
|
|
21
|
+
);
|
|
22
|
+
} else {
|
|
23
|
+
throw new Error('Provider does not support EIP-1193 request method');
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Get account from signer for local signing
|
|
29
|
+
const address = await signer.getAddress();
|
|
30
|
+
const account = address as `0x${string}`;
|
|
31
|
+
|
|
32
|
+
const publicClient = createPublicClient({ transport });
|
|
33
|
+
const walletClient = createWalletClient({ transport, account });
|
|
34
|
+
|
|
35
|
+
return { publicClient, walletClient };
|
|
36
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import * as adapters from './index.js';
|
|
3
|
+
|
|
4
|
+
describe('Index Exports', () => {
|
|
5
|
+
it('should export main adapter functions', () => {
|
|
6
|
+
expect(typeof adapters.Ethers5Adapter).toBe('function');
|
|
7
|
+
expect(typeof adapters.Ethers6Adapter).toBe('function');
|
|
8
|
+
expect(typeof adapters.WagmiAdapter).toBe('function');
|
|
9
|
+
expect(typeof adapters.HardhatSignerAdapter).toBe('function');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should have the expected simple adapters', () => {
|
|
13
|
+
const expectedAdapters = ['Ethers5Adapter', 'Ethers6Adapter', 'WagmiAdapter', 'HardhatSignerAdapter'];
|
|
14
|
+
|
|
15
|
+
expectedAdapters.forEach((adapterName) => {
|
|
16
|
+
expect(adapters).toHaveProperty(adapterName);
|
|
17
|
+
expect(typeof (adapters as any)[adapterName]).toBe('function');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { PublicClient, WalletClient, Chain, Hex } from 'viem';
|
|
2
|
+
import { createWalletClient, custom } from 'viem';
|
|
3
|
+
|
|
4
|
+
type SmartAccountClient = {
|
|
5
|
+
account: { address: `0x${string}` };
|
|
6
|
+
// Sends a UserOperation, returns a hash (usually userOpHash)
|
|
7
|
+
sendTransaction: (tx: any) => Promise<`0x${string}`>;
|
|
8
|
+
// EIP-712 via smart account (e.g., Safe EIP-1271)
|
|
9
|
+
signTypedData: (_args: {
|
|
10
|
+
domain: any;
|
|
11
|
+
types: Record<string, any>;
|
|
12
|
+
primaryType: string;
|
|
13
|
+
message: Record<string, any>;
|
|
14
|
+
}) => Promise<`0x${string}`>;
|
|
15
|
+
// Optional:
|
|
16
|
+
signMessage?: (_args: { message: string | Hex }) => Promise<`0x${string}`>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Adapter: (publicClient, smartAccountClient) -> { publicClient, walletClient }
|
|
21
|
+
* - publicClient: passthrough of the given viem PublicClient
|
|
22
|
+
* - walletClient: viem WalletClient-shaped object whose sendTransaction/sign* delegate to the smart account
|
|
23
|
+
*/
|
|
24
|
+
export function smartWalletViemAdapter(
|
|
25
|
+
publicClient: PublicClient,
|
|
26
|
+
smartAccountClient: SmartAccountClient,
|
|
27
|
+
opts: { chain?: Chain } = {}
|
|
28
|
+
): { publicClient: PublicClient; walletClient: WalletClient } {
|
|
29
|
+
const chain = opts.chain ?? (publicClient as any).chain;
|
|
30
|
+
|
|
31
|
+
// Use the existing publicClient for all JSON-RPC calls
|
|
32
|
+
const transport = custom({
|
|
33
|
+
request: ({ method, params }: { method: string; params?: any[] }) =>
|
|
34
|
+
publicClient.request({ method: method as any, params: (params ?? []) as any }),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Create a base viem WalletClient (JSON-RPC account placeholder)
|
|
38
|
+
// We’ll override methods to route through the smart account.
|
|
39
|
+
const base = createWalletClient({
|
|
40
|
+
chain,
|
|
41
|
+
transport,
|
|
42
|
+
account: smartAccountClient.account.address, // not used for signing; just keeps API shape
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Override methods that must go through the smart account
|
|
46
|
+
const walletClient: WalletClient = {
|
|
47
|
+
...base,
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* For AA, this sends a UserOperation via your smartAccountClient.
|
|
51
|
+
* Return value is typically a userOp hash (not a raw tx hash).
|
|
52
|
+
*/
|
|
53
|
+
async sendTransaction(tx: any) {
|
|
54
|
+
return smartAccountClient.sendTransaction(tx);
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Sign typed data via the smart account (EIP-1271 flow).
|
|
59
|
+
* Supports both single-object and (domain, types, message) forms.
|
|
60
|
+
*/
|
|
61
|
+
async signTypedData(arg1: any, types?: any, message?: any) {
|
|
62
|
+
let domain, typesObj, messageObj, primaryType: string;
|
|
63
|
+
if (types === undefined && message === undefined) {
|
|
64
|
+
// Single object: { domain, types, message, primaryType }
|
|
65
|
+
domain = arg1.domain;
|
|
66
|
+
typesObj = arg1.types;
|
|
67
|
+
messageObj = arg1.message;
|
|
68
|
+
primaryType = arg1.primaryType;
|
|
69
|
+
} else {
|
|
70
|
+
// Separate params
|
|
71
|
+
domain = arg1;
|
|
72
|
+
typesObj = types;
|
|
73
|
+
messageObj = message;
|
|
74
|
+
primaryType = Object.keys(typesObj).find((k) => k !== 'EIP712Domain') ?? Object.keys(typesObj)[0];
|
|
75
|
+
}
|
|
76
|
+
return smartAccountClient.signTypedData({ domain, types: typesObj, primaryType, message: messageObj });
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Optional message signing if your smart account client supports it.
|
|
81
|
+
* Otherwise, fall back to base (may throw for smart accounts).
|
|
82
|
+
*/
|
|
83
|
+
async signMessage(args: { message: string | Hex }) {
|
|
84
|
+
if (typeof smartAccountClient.signMessage === 'function') {
|
|
85
|
+
return smartAccountClient.signMessage(args);
|
|
86
|
+
}
|
|
87
|
+
// Fallback to base signMessage if smart account doesn't support it
|
|
88
|
+
return base.signMessage({ ...args, account: base.account });
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Smart accounts generally cannot produce a raw signed EOA tx.
|
|
93
|
+
* Keep viem’s default, but expect it to throw if invoked.
|
|
94
|
+
*/
|
|
95
|
+
// signTransaction: base.signTransaction,
|
|
96
|
+
} as WalletClient;
|
|
97
|
+
|
|
98
|
+
return { publicClient, walletClient };
|
|
99
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
import type { EIP1193Provider } from 'viem';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mock EIP-1193 provider for testing
|
|
6
|
+
*/
|
|
7
|
+
export function createMockEIP1193Provider(): EIP1193Provider {
|
|
8
|
+
const mockProvider = {
|
|
9
|
+
request: vi.fn().mockImplementation(async ({ method, params }) => {
|
|
10
|
+
switch (method) {
|
|
11
|
+
case 'eth_chainId':
|
|
12
|
+
return '0x2aa36a7'; // Sepolia chainId
|
|
13
|
+
case 'eth_accounts':
|
|
14
|
+
return ['0x742d35Cc6634C0532925a3b8D0Ea4E686C9b8A'];
|
|
15
|
+
case 'eth_getBalance':
|
|
16
|
+
return '0x1bc16d674ec80000'; // 2 ETH
|
|
17
|
+
case 'eth_blockNumber':
|
|
18
|
+
return '0x12345';
|
|
19
|
+
case 'eth_gasPrice':
|
|
20
|
+
return '0x9184e72a000'; // 10 gwei
|
|
21
|
+
case 'personal_sign':
|
|
22
|
+
case 'eth_sign':
|
|
23
|
+
return '0x' + 'a'.repeat(130); // Mock signature
|
|
24
|
+
case 'eth_signTypedData_v4':
|
|
25
|
+
return '0x' + 'a'.repeat(130); // Mock typed data signature
|
|
26
|
+
case 'eth_sendTransaction':
|
|
27
|
+
return '0x' + 'b'.repeat(64); // Mock transaction hash
|
|
28
|
+
default:
|
|
29
|
+
throw new Error(`Unsupported method: ${method}`);
|
|
30
|
+
}
|
|
31
|
+
}),
|
|
32
|
+
on: vi.fn(),
|
|
33
|
+
removeListener: vi.fn(),
|
|
34
|
+
// Add additional EIP-1193 properties
|
|
35
|
+
send: vi.fn(),
|
|
36
|
+
sendAsync: vi.fn(),
|
|
37
|
+
isMetaMask: false,
|
|
38
|
+
isConnected: vi.fn().mockReturnValue(true),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Add bind method that viem expects and bind all methods
|
|
42
|
+
const boundProvider = {
|
|
43
|
+
...mockProvider,
|
|
44
|
+
request: mockProvider.request.bind(mockProvider),
|
|
45
|
+
on: mockProvider.on.bind(mockProvider),
|
|
46
|
+
removeListener: mockProvider.removeListener.bind(mockProvider),
|
|
47
|
+
bind: function () {
|
|
48
|
+
return this;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return boundProvider as any;
|
|
53
|
+
}
|