@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,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
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { parseEther, createPublicClient, createWalletClient, http, type PublicClient, type WalletClient } from 'viem';
|
|
3
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
4
|
+
import { WagmiAdapter } from './wagmi.js';
|
|
5
|
+
|
|
6
|
+
describe('WagmiAdapter', () => {
|
|
7
|
+
const testRpcUrl = 'https://ethereum-sepolia.rpc.subquery.network/public';
|
|
8
|
+
const SEPOLIA_CHAIN_ID = 11155111;
|
|
9
|
+
let account: ReturnType<typeof privateKeyToAccount>;
|
|
10
|
+
let publicClient: PublicClient;
|
|
11
|
+
let walletClient: WalletClient;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
// Create common setup for all tests - no chain needed
|
|
15
|
+
account = privateKeyToAccount(('0x' + '1'.repeat(64)) as `0x${string}`);
|
|
16
|
+
|
|
17
|
+
publicClient = createPublicClient({
|
|
18
|
+
transport: http(testRpcUrl),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
walletClient = createWalletClient({
|
|
22
|
+
transport: http(testRpcUrl),
|
|
23
|
+
account,
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should work with real Wagmi clients', async () => {
|
|
28
|
+
const result = await WagmiAdapter(walletClient, publicClient);
|
|
29
|
+
|
|
30
|
+
expect(result).toHaveProperty('publicClient');
|
|
31
|
+
expect(result).toHaveProperty('walletClient');
|
|
32
|
+
expect(result.publicClient).toBe(publicClient);
|
|
33
|
+
expect(result.walletClient).toBe(walletClient);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should throw error when wallet client is missing', async () => {
|
|
37
|
+
const mockPublicClient = {} as any;
|
|
38
|
+
|
|
39
|
+
await expect(async () => {
|
|
40
|
+
await WagmiAdapter(null as any, mockPublicClient);
|
|
41
|
+
}).rejects.toThrow('WalletClient is required');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should throw error when public client is missing', async () => {
|
|
45
|
+
const mockWalletClient = {} as any;
|
|
46
|
+
|
|
47
|
+
await expect(async () => {
|
|
48
|
+
await WagmiAdapter(mockWalletClient, null as any);
|
|
49
|
+
}).rejects.toThrow('PublicClient is required');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('Provider Functions', () => {
|
|
53
|
+
it('should support getChainId', async () => {
|
|
54
|
+
const { publicClient: resultPublic } = await WagmiAdapter(walletClient, publicClient);
|
|
55
|
+
|
|
56
|
+
const chainId = await resultPublic.getChainId();
|
|
57
|
+
expect(typeof chainId).toBe('number');
|
|
58
|
+
expect(chainId).toBe(SEPOLIA_CHAIN_ID);
|
|
59
|
+
}, 10000);
|
|
60
|
+
|
|
61
|
+
it('should support call (contract read)', async () => {
|
|
62
|
+
const { publicClient: resultPublic } = await WagmiAdapter(walletClient, publicClient);
|
|
63
|
+
|
|
64
|
+
// Test eth_call - get ETH balance of zero address
|
|
65
|
+
const balance = await resultPublic.getBalance({
|
|
66
|
+
address: '0x0000000000000000000000000000000000000000',
|
|
67
|
+
});
|
|
68
|
+
expect(typeof balance).toBe('bigint');
|
|
69
|
+
}, 10000);
|
|
70
|
+
|
|
71
|
+
it('should support request (raw RPC)', async () => {
|
|
72
|
+
const { publicClient: resultPublic } = await WagmiAdapter(walletClient, publicClient);
|
|
73
|
+
|
|
74
|
+
// Test raw RPC request
|
|
75
|
+
const blockNumber = (await resultPublic.request({
|
|
76
|
+
method: 'eth_blockNumber',
|
|
77
|
+
})) as string;
|
|
78
|
+
expect(typeof blockNumber).toBe('string');
|
|
79
|
+
expect(blockNumber.startsWith('0x')).toBe(true);
|
|
80
|
+
}, 10000);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('Signer Functions', () => {
|
|
84
|
+
it('should support getAddress', async () => {
|
|
85
|
+
const { walletClient: resultWallet } = await WagmiAdapter(walletClient, publicClient);
|
|
86
|
+
|
|
87
|
+
const addresses = await resultWallet.getAddresses();
|
|
88
|
+
expect(Array.isArray(addresses)).toBe(true);
|
|
89
|
+
// Should contain the account address
|
|
90
|
+
expect(addresses).toContain(account.address);
|
|
91
|
+
}, 10000);
|
|
92
|
+
|
|
93
|
+
it('should support signTypedData', async () => {
|
|
94
|
+
const { walletClient: resultWallet } = await WagmiAdapter(walletClient, publicClient);
|
|
95
|
+
|
|
96
|
+
const domain = {
|
|
97
|
+
name: 'Test',
|
|
98
|
+
version: '1',
|
|
99
|
+
chainId: SEPOLIA_CHAIN_ID, // Sepolia
|
|
100
|
+
verifyingContract: '0x0000000000000000000000000000000000000000' as const,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const types = {
|
|
104
|
+
Message: [{ name: 'content', type: 'string' }],
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const message = { content: 'Hello World' };
|
|
108
|
+
|
|
109
|
+
const signature = await resultWallet.signTypedData({
|
|
110
|
+
domain,
|
|
111
|
+
types,
|
|
112
|
+
primaryType: 'Message',
|
|
113
|
+
message,
|
|
114
|
+
account: resultWallet.account!,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(typeof signature).toBe('string');
|
|
118
|
+
expect(signature.startsWith('0x')).toBe(true);
|
|
119
|
+
}, 10000);
|
|
120
|
+
|
|
121
|
+
it('should support sendTransaction', async () => {
|
|
122
|
+
const { publicClient: resultPublic, walletClient: resultWallet } = await WagmiAdapter(walletClient, publicClient);
|
|
123
|
+
|
|
124
|
+
// Try to send a transaction - this will fail due to insufficient funds
|
|
125
|
+
try {
|
|
126
|
+
console.log('estimating gas');
|
|
127
|
+
const gas = await resultPublic.estimateGas({
|
|
128
|
+
account: account.address,
|
|
129
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
130
|
+
value: parseEther('0'),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
console.log('sending transaction', account.address);
|
|
134
|
+
const hash = await resultWallet.sendTransaction({
|
|
135
|
+
to: '0x0000000000000000000000000000000000000000' as `0x${string}`,
|
|
136
|
+
value: parseEther('0'),
|
|
137
|
+
gas,
|
|
138
|
+
account: resultWallet.account!,
|
|
139
|
+
chain: resultWallet.chain,
|
|
140
|
+
});
|
|
141
|
+
console.log('transaction sent', hash);
|
|
142
|
+
|
|
143
|
+
// If it succeeds (shouldn't due to no funds), verify the format
|
|
144
|
+
expect(typeof hash).toBe('string');
|
|
145
|
+
expect(hash.startsWith('0x')).toBe(true);
|
|
146
|
+
expect(hash.length).toBe(66);
|
|
147
|
+
} catch (error: any) {
|
|
148
|
+
// Expected error: insufficient funds (good!)
|
|
149
|
+
const isInsufficientFunds = error.message.includes('insufficient funds') || error.message.includes('balance');
|
|
150
|
+
|
|
151
|
+
expect(isInsufficientFunds).toBe(true);
|
|
152
|
+
console.log('Expected error (insufficient funds):', error.message);
|
|
153
|
+
}
|
|
154
|
+
}, 10000);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type PublicClient, type WalletClient } from 'viem';
|
|
2
|
+
|
|
3
|
+
export async function WagmiAdapter(walletClient: WalletClient, publicClient: PublicClient) {
|
|
4
|
+
if (!walletClient) {
|
|
5
|
+
throw new Error('WalletClient is required');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (!publicClient) {
|
|
9
|
+
throw new Error('PublicClient is required');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Wagmi provides real viem clients, so we just pass them through
|
|
13
|
+
return {
|
|
14
|
+
publicClient,
|
|
15
|
+
walletClient,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineChain } from '../defineChain.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Arbitrum Sepolia testnet chain configuration
|
|
5
|
+
*/
|
|
6
|
+
export const arbSepolia = defineChain({
|
|
7
|
+
id: 421614,
|
|
8
|
+
name: 'Arbitrum Sepolia',
|
|
9
|
+
network: 'arb-sepolia',
|
|
10
|
+
coFheUrl: 'https://testnet-cofhe.fhenix.zone',
|
|
11
|
+
verifierUrl: 'https://testnet-cofhe-vrf.fhenix.zone',
|
|
12
|
+
thresholdNetworkUrl: 'https://testnet-cofhe-tn.fhenix.zone',
|
|
13
|
+
environment: 'TESTNET',
|
|
14
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineChain } from '../defineChain.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base Sepolia testnet chain configuration
|
|
5
|
+
*/
|
|
6
|
+
export const baseSepolia = defineChain({
|
|
7
|
+
id: 84532,
|
|
8
|
+
name: 'Base Sepolia',
|
|
9
|
+
network: 'base-sepolia',
|
|
10
|
+
coFheUrl: 'https://testnet-cofhe.fhenix.zone',
|
|
11
|
+
verifierUrl: 'https://testnet-cofhe-vrf.fhenix.zone',
|
|
12
|
+
thresholdNetworkUrl: 'https://testnet-cofhe-tn.fhenix.zone',
|
|
13
|
+
environment: 'TESTNET',
|
|
14
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineChain } from '../defineChain.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hardhat local development chain configuration
|
|
5
|
+
*/
|
|
6
|
+
export const hardhat = defineChain({
|
|
7
|
+
id: 31337,
|
|
8
|
+
name: 'Hardhat',
|
|
9
|
+
network: 'localhost',
|
|
10
|
+
// These are unused in the mock environment
|
|
11
|
+
coFheUrl: 'http://127.0.0.1:8448',
|
|
12
|
+
verifierUrl: 'http://127.0.0.1:3001',
|
|
13
|
+
thresholdNetworkUrl: 'http://127.0.0.1:3000',
|
|
14
|
+
environment: 'MOCK',
|
|
15
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineChain } from '../defineChain.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Localcofhe chain configuration
|
|
5
|
+
*/
|
|
6
|
+
export const localcofhe = defineChain({
|
|
7
|
+
id: 420105,
|
|
8
|
+
name: 'Local Cofhe',
|
|
9
|
+
network: 'localhost',
|
|
10
|
+
coFheUrl: 'http://127.0.0.1:9448',
|
|
11
|
+
verifierUrl: 'http://127.0.0.1:3001',
|
|
12
|
+
thresholdNetworkUrl: 'http://127.0.0.1:3000',
|
|
13
|
+
environment: 'TESTNET',
|
|
14
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineChain } from '../defineChain.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sepolia testnet chain configuration
|
|
5
|
+
*/
|
|
6
|
+
export const sepolia = defineChain({
|
|
7
|
+
id: 11155111,
|
|
8
|
+
name: 'Sepolia',
|
|
9
|
+
network: 'sepolia',
|
|
10
|
+
coFheUrl: 'https://testnet-cofhe.fhenix.zone',
|
|
11
|
+
verifierUrl: 'https://testnet-cofhe-vrf.fhenix.zone',
|
|
12
|
+
thresholdNetworkUrl: 'https://testnet-cofhe-tn.fhenix.zone',
|
|
13
|
+
environment: 'TESTNET',
|
|
14
|
+
});
|