@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.
Files changed (132) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/adapters/ethers5.test.ts +174 -0
  3. package/adapters/ethers5.ts +36 -0
  4. package/adapters/ethers6.test.ts +169 -0
  5. package/adapters/ethers6.ts +36 -0
  6. package/adapters/hardhat-node.ts +167 -0
  7. package/adapters/hardhat.hh2.test.ts +159 -0
  8. package/adapters/hardhat.ts +36 -0
  9. package/adapters/index.test.ts +20 -0
  10. package/adapters/index.ts +5 -0
  11. package/adapters/smartWallet.ts +99 -0
  12. package/adapters/test-utils.ts +53 -0
  13. package/adapters/types.ts +6 -0
  14. package/adapters/wagmi.test.ts +156 -0
  15. package/adapters/wagmi.ts +17 -0
  16. package/chains/chains/arbSepolia.ts +14 -0
  17. package/chains/chains/baseSepolia.ts +14 -0
  18. package/chains/chains/hardhat.ts +15 -0
  19. package/chains/chains/localcofhe.ts +14 -0
  20. package/chains/chains/sepolia.ts +14 -0
  21. package/chains/chains.test.ts +50 -0
  22. package/chains/defineChain.ts +18 -0
  23. package/chains/index.ts +35 -0
  24. package/chains/types.ts +32 -0
  25. package/core/baseBuilder.ts +119 -0
  26. package/core/client.test.ts +429 -0
  27. package/core/client.ts +341 -0
  28. package/core/clientTypes.ts +119 -0
  29. package/core/config.test.ts +242 -0
  30. package/core/config.ts +225 -0
  31. package/core/consts.ts +22 -0
  32. package/core/decrypt/MockThresholdNetworkAbi.ts +179 -0
  33. package/core/decrypt/cofheMocksDecryptForTx.ts +84 -0
  34. package/core/decrypt/cofheMocksDecryptForView.ts +48 -0
  35. package/core/decrypt/decryptForTxBuilder.ts +359 -0
  36. package/core/decrypt/decryptForViewBuilder.ts +332 -0
  37. package/core/decrypt/decryptUtils.ts +28 -0
  38. package/core/decrypt/pollCallbacks.test.ts +194 -0
  39. package/core/decrypt/polling.ts +14 -0
  40. package/core/decrypt/tnDecryptUtils.ts +65 -0
  41. package/core/decrypt/tnDecryptV1.ts +171 -0
  42. package/core/decrypt/tnDecryptV2.ts +365 -0
  43. package/core/decrypt/tnSealOutputV1.ts +59 -0
  44. package/core/decrypt/tnSealOutputV2.ts +324 -0
  45. package/core/decrypt/verifyDecryptResult.ts +52 -0
  46. package/core/encrypt/MockZkVerifierAbi.ts +106 -0
  47. package/core/encrypt/cofheMocksZkVerifySign.ts +281 -0
  48. package/core/encrypt/encryptInputsBuilder.test.ts +747 -0
  49. package/core/encrypt/encryptInputsBuilder.ts +583 -0
  50. package/core/encrypt/encryptUtils.ts +67 -0
  51. package/core/encrypt/zkPackProveVerify.ts +335 -0
  52. package/core/error.ts +168 -0
  53. package/core/fetchKeys.test.ts +195 -0
  54. package/core/fetchKeys.ts +144 -0
  55. package/core/index.ts +106 -0
  56. package/core/keyStore.test.ts +226 -0
  57. package/core/keyStore.ts +154 -0
  58. package/core/permits.test.ts +493 -0
  59. package/core/permits.ts +201 -0
  60. package/core/types.ts +419 -0
  61. package/core/utils.ts +130 -0
  62. package/dist/adapters.cjs +88 -0
  63. package/dist/adapters.d.cts +14576 -0
  64. package/dist/adapters.d.ts +14576 -0
  65. package/dist/adapters.js +83 -0
  66. package/dist/chains.cjs +111 -0
  67. package/dist/chains.d.cts +121 -0
  68. package/dist/chains.d.ts +121 -0
  69. package/dist/chains.js +1 -0
  70. package/dist/chunk-36FBWLUS.js +3310 -0
  71. package/dist/chunk-7HLGHV67.js +990 -0
  72. package/dist/chunk-TBLR7NNE.js +102 -0
  73. package/dist/clientTypes-AVSCBet7.d.cts +998 -0
  74. package/dist/clientTypes-flH1ju82.d.ts +998 -0
  75. package/dist/core.cjs +4362 -0
  76. package/dist/core.d.cts +138 -0
  77. package/dist/core.d.ts +138 -0
  78. package/dist/core.js +3 -0
  79. package/dist/node.cjs +4225 -0
  80. package/dist/node.d.cts +22 -0
  81. package/dist/node.d.ts +22 -0
  82. package/dist/node.js +91 -0
  83. package/dist/permit-jRirYqFt.d.cts +376 -0
  84. package/dist/permit-jRirYqFt.d.ts +376 -0
  85. package/dist/permits.cjs +1025 -0
  86. package/dist/permits.d.cts +353 -0
  87. package/dist/permits.d.ts +353 -0
  88. package/dist/permits.js +1 -0
  89. package/dist/types-YiAC4gig.d.cts +33 -0
  90. package/dist/types-YiAC4gig.d.ts +33 -0
  91. package/dist/web.cjs +4434 -0
  92. package/dist/web.d.cts +42 -0
  93. package/dist/web.d.ts +42 -0
  94. package/dist/web.js +256 -0
  95. package/dist/zkProve.worker.cjs +93 -0
  96. package/dist/zkProve.worker.d.cts +2 -0
  97. package/dist/zkProve.worker.d.ts +2 -0
  98. package/dist/zkProve.worker.js +91 -0
  99. package/node/client.test.ts +159 -0
  100. package/node/config.test.ts +68 -0
  101. package/node/encryptInputs.test.ts +155 -0
  102. package/node/index.ts +97 -0
  103. package/node/storage.ts +51 -0
  104. package/package.json +121 -0
  105. package/permits/index.ts +68 -0
  106. package/permits/localstorage.test.ts +113 -0
  107. package/permits/onchain-utils.ts +221 -0
  108. package/permits/permit.test.ts +534 -0
  109. package/permits/permit.ts +386 -0
  110. package/permits/sealing.test.ts +84 -0
  111. package/permits/sealing.ts +131 -0
  112. package/permits/signature.ts +79 -0
  113. package/permits/store.test.ts +88 -0
  114. package/permits/store.ts +156 -0
  115. package/permits/test-utils.ts +28 -0
  116. package/permits/types.ts +204 -0
  117. package/permits/utils.ts +58 -0
  118. package/permits/validation.test.ts +361 -0
  119. package/permits/validation.ts +327 -0
  120. package/web/client.web.test.ts +159 -0
  121. package/web/config.web.test.ts +69 -0
  122. package/web/const.ts +2 -0
  123. package/web/encryptInputs.web.test.ts +172 -0
  124. package/web/index.ts +166 -0
  125. package/web/storage.ts +49 -0
  126. package/web/worker.builder.web.test.ts +148 -0
  127. package/web/worker.config.web.test.ts +329 -0
  128. package/web/worker.output.web.test.ts +84 -0
  129. package/web/workerManager.test.ts +80 -0
  130. package/web/workerManager.ts +214 -0
  131. package/web/workerManager.web.test.ts +114 -0
  132. 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,5 @@
1
+ export { Ethers5Adapter } from './ethers5.js';
2
+ export { Ethers6Adapter } from './ethers6.js';
3
+ export { WagmiAdapter } from './wagmi.js';
4
+ export { HardhatSignerAdapter } from './hardhat.js';
5
+ export type { AdapterResult } from './types.js';
@@ -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
+ }
@@ -0,0 +1,6 @@
1
+ import type { PublicClient, WalletClient } from 'viem';
2
+
3
+ export interface AdapterResult {
4
+ publicClient: PublicClient;
5
+ walletClient: WalletClient;
6
+ }