@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
package/CHANGELOG.md ADDED
@@ -0,0 +1,146 @@
1
+ # @cofhe/sdk Changelog
2
+
3
+ ## 0.0.0-alpha-20260409113701
4
+
5
+ ### Minor Changes
6
+
7
+ - 788a6e2: Add `onPoll` callback support for decrypt polling (tx + view) so consumers can observe poll progress.
8
+
9
+ - SDK decrypt helpers accept `onPoll` and emit `{ operation, requestId, attemptIndex, elapsedMs, intervalMs, timeoutMs }` once per poll attempt.
10
+ - React wiring supports passing the callback end-to-end.
11
+ - Docs updated with usage examples.
12
+
13
+ - 9a06012: Tighten permit validation and treat invalid permits as missing.
14
+
15
+ - SDK: `PermitUtils.validate` now enforces schema + signed + not-expired (use `PermitUtils.validateSchema` for schema-only validation).
16
+ - SDK: `ValidationResult.error` is now a typed union (`'invalid-schema' | 'expired' | 'not-signed' | null`).
17
+ - React: rename `disabledDueToMissingPermit` to `disabledDueToMissingValidPermit` in read/decrypt hooks and token balance helpers, and disable reads when the active permit is invalid.
18
+
19
+ ### Patch Changes
20
+
21
+ - 503536a: Improve logging ergonomics across React + web SDK.
22
+
23
+ - Add a configurable internal logger to `@cofhe/react` via `createCofheConfig({ react: { logger } })`.
24
+ - Make `@cofhe/sdk` `createWebStorage` logging opt-in via `createWebStorage({ enableLog })`.
25
+
26
+ ## 0.4.0
27
+
28
+ ### Patch Changes
29
+
30
+ - e446642: Switch `decryptForTx` to Threshold Network v2 decrypt (submit + poll)
31
+
32
+ ## 0.3.2
33
+
34
+ ### Patch Changes
35
+
36
+ - d4e86ea: Aligns with CTA encrypted variables bytes32 representation.
37
+
38
+ - **@cofhe/hardhat-plugin**: `hre.cofhe.mocks.getTestBed()`, `getMockTaskManager()`, `getMockACL()`, `getMockThresholdNetwork()`, and `getMockZkVerifier()` now return typed contracts (typechain interfaces) instead of untyped `Contract`. `getPlaintext(ctHash)` and `expectPlaintext(ctHash, value)` now accept bytes32 ctHashes as `string` support cofhe-contracts 0.1.0 CTA changes.
39
+ - **@cofhe/mock-contracts**: Export typechain-generated contract types (`TestBed`, `MockACL`, `MockTaskManager`, `MockZkVerifier`, `MockThresholdNetwork`) for use with the hardhat plugin. Typechain is run from artifact ABIs only; factory files are not generated.
40
+ - **@cofhe/abi**: CTA-related types use `bytes32` (string) instead of `uint256`. Decryption and return-type helpers aligned with cofhe-contracts 0.1.0.
41
+ - **@cofhe/sdk**: Decryption APIs (`decryptForTx`, `decryptForView`, and related builders) now also accept `string` for ciphertext hashes (bytes32) as well as `bigint`.
42
+
43
+ - 0feaf3f: `cofheClient.decryptForTx` returns a ready-to-use signature
44
+
45
+ ## 0.3.1
46
+
47
+ ### Patch Changes
48
+
49
+ - 370f0c7: no-op
50
+
51
+ ## 0.3.0
52
+
53
+ ### Minor Changes
54
+
55
+ - 35024b6: Remove `sdk` from function names and exported types. Rename:
56
+
57
+ - `createCofhesdkConfig` -> `createCofheConfig`
58
+ - `createCofhesdkClient` -> `createCofheClient`
59
+ - `hre.cofhesdk.*` -> `hre.cofhe.*`
60
+ - `hre.cofhesdk.createCofheConfig()` → `hre.cofhe.createConfig()`
61
+ - `hre.cofhesdk.createCofheClient()` → `hre.cofhe.createClient()`
62
+ - `hre.cofhesdk.createBatteriesIncludedCofheClient()` → `hre.cofhe.createClientWithBatteries()`
63
+
64
+ - 29c2401: implement decrypt-with-proof flows and related tests:
65
+
66
+ - Implement production `decryptForTx` backed by Threshold Network `POST /decrypt`, with explicit permit vs global-allowance selection.
67
+ - Rename mocks “Query Decrypter” -> “Threshold Network” and update SDK constants/contracts/artifacts accordingly.
68
+ - Extend mock contracts + hardhat plugin to publish & verify decryption results on-chain, and add end-to-end integration tests.
69
+
70
+ - 650ea48: Align builder patterns in cofhe client api (`client.encryptInputs(..).encrypt()` and `client.decryptHandles(..).decrypt()`) to use the same terminator function `.execute()` instead of `.encrypt()`/`.decrypt()`.
71
+
72
+ Rename `setStepCallback` of encryptInputs builder to `onStep` to improve ergonomics.
73
+
74
+ ### Patch Changes
75
+
76
+ - 5467d77: Adds `config.mocks.encryptDelay: number | [number, number, number, number, number]` to allow configurable mock encryption delay. Defaults to 0 delay on hardhat to keep tests quick.
77
+ - 73b1502: Add `cofheClient.connection` getter which exposes inner connection state without using `getSnapshot()` reactive utility.
78
+
79
+ ## 0.2.1
80
+
81
+ ### Patch Changes
82
+
83
+ - 409bfdf: Add `hash` field to permits, calculated at permit creation time. Replaces `PermitUtils.getHash(permit)` with `permit.hash`.
84
+ - ac47e2f: Add `PermitUtils.checkValidityOnChain` to validate permits against the on-chain deployed ACL (source of truth).
85
+ - 8af1b70: Updated to Zod 4 for improved performance and type safety. Includes internal changes to validation schemas and error handling that should not affect external API usage.
86
+
87
+ ## 0.2.0
88
+
89
+ ### Minor Changes
90
+
91
+ - 8fda09a: Removes `Promise<boolean>` return type from `client.connect(...)`, instead throws an error if the connection fails.
92
+ - e0caeca: Adds `environment: 'node' | 'web' | 'hardhat' | 'react'` option to config. Exposed via `client.config.enviroment`. Automatically populated appropriately within the various `createCofhesdkConfig` functions.
93
+ - 2a9d6c5: Updated to new CoFHE /sealoutputV2 endpoint - uses polling to fetch decryption results instead of long running open HTTP connections.
94
+
95
+ ### Patch Changes
96
+
97
+ - 4057a76: Add WebWorker for zkProve
98
+ - dba2759: Add getOrCreate permit functions
99
+ - 7c861af: Remove `initializationResults` from cofhesdk client.
100
+
101
+ ## 0.1.1
102
+
103
+ ### Patch Changes
104
+
105
+ - a1d1323: Add repository info to package.json of public packages to fix npm publish provenance issue.
106
+ - d232d11: Ensure publish includes correct src and dist files
107
+ - b6521fb: Update publish workflow to create versioning PR upon merge with changeset.
108
+
109
+ ## 0.1.0
110
+
111
+ ### Minor Changes
112
+
113
+ - 87fc8a0: Initial extraction from cofhejs. Split permit `create` into type specific creators: `createSelf`, `createShared`, and `importShared`
114
+ - 8d41cf2: Combine existing packages into more reasonable defaults. New package layout is @cofhe/sdk (includes all the core logic for configuring and creating a @cofhe/sdk client, encrypting values, and decrypting handles), mock-contracts, hardhat-plugin, and react.
115
+ - 738b9f5: Adding adapters for ethers5/6, Wagmi and HardHat
116
+ - a83facb: Prepare for initial release. Rename scope from `@cofhesdk` to `@cofhe` and rename `cofhesdk` package to `@cofhe/sdk`. Create `publish.yml` to publish `beta` packages on merged PR, and `latest` on changeset PR.
117
+ - 9a7c98e: Create core store, split initialize into `create` and `connect`, and port `encryptInputs` with improvements.
118
+ - 58e93a8: Migrate cofhe-mock-contracts and cofhe-hardhat-plugin into @cofhe/sdk.
119
+ - fdf26d4: Move storage handling to web/node targets; add `fheKeysStorage` config with environment-specific defaults.
120
+ - f5b8e25: Add @cofhe/sdk config type and parsing.
121
+ - 3b135a8: Create @cofhe/sdk/node and @cofhe/sdk/web
122
+
123
+ Additional changes:
124
+
125
+ - Fhe keys aren't fetched until `client.encryptInputs(...).encrypt()`, they aren't used anywhere else other than encrypting inputs, so their fetching is deferred until then.
126
+ - Initializing the tfhe wasm is also deferred until `client.encryptInputs(...).encrypt()` is called (allows for deferred async initialization)
127
+
128
+ - 5b7c43b: Create @cofhe/sdk client, hook in permits.
129
+ Create encryptInputs functionality. (+ mocks)
130
+ Create decryptHandles functionality. (+ mocks)
131
+ Improve @cofhe/sdk errors and error handling.
132
+
133
+ fix - seal_output include error_message in failing Error.
134
+
135
+ ### Patch Changes
136
+
137
+ - cba73fd: Add duration and context information to the step callback function for encryptInputs. Split fetch keys step into InitTfhe and FetchKeys.
138
+ InitTfhe context includes a `tfheInitializationExecuted` indicating if tfhe was initialized (true) or already initialized (false).
139
+ FetchKeys returns flags for whether the fhe and crs keys were fetch from remote (true) or from cache (false).
140
+ - 4bc8182: Add storage and key store
141
+
142
+ This changelog is maintained by Changesets and will be populated on each release.
143
+
144
+ - Do not edit this file by hand.
145
+ - Upcoming changes can be previewed with `pnpm changeset status --verbose`.
146
+ - Entries are generated when the Changesets "Version Packages" PR is created/merged.
@@ -0,0 +1,174 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { parseEther } from 'viem';
3
+ import { Ethers5Adapter } from './ethers5.js';
4
+ import { createMockEIP1193Provider } from './test-utils.js';
5
+ import * as ethers5 from 'ethers5';
6
+
7
+ describe('Ethers5Adapter', () => {
8
+ const testRpcUrl = 'https://ethereum-sepolia.rpc.subquery.network/public';
9
+ const SEPOLIA_CHAIN_ID = 11155111;
10
+ let provider: ethers5.providers.JsonRpcProvider;
11
+ let wallet: ethers5.Wallet;
12
+
13
+ beforeEach(() => {
14
+ // Create common setup for all tests
15
+ provider = new ethers5.providers.JsonRpcProvider(testRpcUrl);
16
+ wallet = new ethers5.Wallet('0x' + '1'.repeat(64), provider);
17
+ });
18
+
19
+ it('should work with real JsonRpcProvider and Wallet', async () => {
20
+ const result = await Ethers5Adapter(provider, wallet);
21
+
22
+ expect(result).toHaveProperty('publicClient');
23
+ expect(result).toHaveProperty('walletClient');
24
+ expect(result.publicClient).toBeDefined();
25
+ expect(result.walletClient).toBeDefined();
26
+ });
27
+
28
+ it('should work with Web3Provider signer', async () => {
29
+ const mockEthereum = createMockEIP1193Provider();
30
+ const web3Provider = new ethers5.providers.Web3Provider(mockEthereum as any);
31
+ const wallet = new ethers5.Wallet('0x' + '1'.repeat(64), web3Provider);
32
+
33
+ const result = await Ethers5Adapter(web3Provider, wallet);
34
+
35
+ expect(result).toHaveProperty('publicClient');
36
+ expect(result).toHaveProperty('walletClient');
37
+ });
38
+
39
+ it('should work without configuration', async () => {
40
+ const result = await Ethers5Adapter(provider, wallet);
41
+
42
+ expect(result).toHaveProperty('publicClient');
43
+ expect(result).toHaveProperty('walletClient');
44
+ expect(result.publicClient).toBeDefined();
45
+ expect(result.walletClient).toBeDefined();
46
+ });
47
+
48
+ it('should throw error when signer has no provider', async () => {
49
+ const signerWithoutProvider = { provider: null };
50
+
51
+ await expect(async () => {
52
+ await Ethers5Adapter(signerWithoutProvider as any, wallet);
53
+ }).rejects.toThrow('Provider does not support EIP-1193 interface');
54
+ });
55
+
56
+ it('should work with real network call', async () => {
57
+ const result = await Ethers5Adapter(provider, wallet);
58
+
59
+ // Test real network call
60
+ const chainId = await result.publicClient.getChainId();
61
+ expect(typeof chainId).toBe('number');
62
+ expect(chainId).toBeGreaterThan(0);
63
+ }, 10000);
64
+
65
+ describe('Provider Functions', () => {
66
+ it('should support getChainId', async () => {
67
+ const { publicClient } = await Ethers5Adapter(provider, wallet);
68
+
69
+ const chainId = await publicClient.getChainId();
70
+ expect(typeof chainId).toBe('number');
71
+ expect(chainId).toBe(SEPOLIA_CHAIN_ID); // Sepolia
72
+ }, 10000);
73
+
74
+ it('should support call (contract read)', async () => {
75
+ const { publicClient } = await Ethers5Adapter(provider, wallet);
76
+
77
+ // Test eth_call - get ETH balance of zero address
78
+ const balance = await publicClient.getBalance({
79
+ address: '0x0000000000000000000000000000000000000000',
80
+ });
81
+ expect(typeof balance).toBe('bigint');
82
+ }, 10000);
83
+
84
+ it('should support request (raw RPC)', async () => {
85
+ const { publicClient } = await Ethers5Adapter(provider, wallet);
86
+
87
+ // Test raw RPC request - viem requires both method and params
88
+ const blockNumber = (await publicClient.request({
89
+ method: 'eth_blockNumber',
90
+ })) as string;
91
+ expect(typeof blockNumber).toBe('string');
92
+ expect(blockNumber.startsWith('0x')).toBe(true);
93
+ }, 10000);
94
+ });
95
+
96
+ describe('Signer Functions', () => {
97
+ it('should support getAddress', async () => {
98
+ const { walletClient } = await Ethers5Adapter(provider, wallet);
99
+
100
+ const addresses = await walletClient.getAddresses();
101
+ expect(Array.isArray(addresses)).toBe(true);
102
+ // Note: For HTTP transport, this might return empty array
103
+ // For EIP-1193 providers, it would return actual addresses
104
+ }, 10000);
105
+
106
+ it('should support signTypedData', async () => {
107
+ const { walletClient } = await Ethers5Adapter(provider, wallet);
108
+
109
+ const domain = {
110
+ name: 'Test',
111
+ version: '1',
112
+ chainId: SEPOLIA_CHAIN_ID, // Sepolia
113
+ verifyingContract: '0x0000000000000000000000000000000000000000' as const,
114
+ };
115
+
116
+ const types = {
117
+ Message: [{ name: 'content', type: 'string' }],
118
+ };
119
+
120
+ const message = { content: 'Hello World' };
121
+
122
+ const signature = await walletClient.signTypedData({
123
+ domain,
124
+ types,
125
+ primaryType: 'Message',
126
+ message,
127
+ account: walletClient.account!,
128
+ });
129
+
130
+ expect(typeof signature).toBe('string');
131
+ expect(signature.startsWith('0x')).toBe(true);
132
+ }, 10000);
133
+
134
+ it('should support sendTransaction', async () => {
135
+ const { publicClient, walletClient } = await Ethers5Adapter(provider, wallet);
136
+
137
+ const account = await walletClient.account;
138
+ const chain = await publicClient.getChainId();
139
+
140
+ console.log('Wallet client ethers5', account, chain);
141
+
142
+ // Try to send a transaction - this will fail due to insufficient funds
143
+ try {
144
+ console.log('estimating gas');
145
+ const gas = await publicClient.estimateGas({
146
+ account: wallet.address as `0x${string}`,
147
+ to: '0x0000000000000000000000000000000000000000',
148
+ value: parseEther('0'),
149
+ });
150
+
151
+ console.log('sending transaction', await wallet.getAddress());
152
+ const hash = await walletClient.sendTransaction({
153
+ to: '0x0000000000000000000000000000000000000000' as `0x${string}`,
154
+ value: parseEther('0'),
155
+ gas,
156
+ account: walletClient.account!,
157
+ chain: walletClient.chain,
158
+ });
159
+ console.log('transaction sent', hash);
160
+
161
+ // If it succeeds (shouldn't due to no funds), verify the format
162
+ expect(typeof hash).toBe('string');
163
+ expect(hash.startsWith('0x')).toBe(true);
164
+ expect(hash.length).toBe(66);
165
+ } catch (error: any) {
166
+ // Expected errors: either insufficient funds (good!) or method not supported
167
+ const isInsufficientFunds = error.message.includes('insufficient funds') || error.message.includes('balance');
168
+
169
+ expect(isInsufficientFunds).toBe(true);
170
+ console.log('Expected error (insufficient funds):', error.message);
171
+ }
172
+ }, 10000);
173
+ });
174
+ });
@@ -0,0 +1,36 @@
1
+ import { createPublicClient, createWalletClient, custom } from 'viem';
2
+ import { privateKeyToAccount, toAccount } from 'viem/accounts';
3
+ import type { Signer as AbstractSigner, Wallet, providers } from 'ethers5';
4
+ import { type AdapterResult } from './types.js';
5
+
6
+ type Ethers5Signer = AbstractSigner | Wallet;
7
+
8
+ export async function Ethers5Adapter(provider: providers.Provider, signer: Ethers5Signer): Promise<AdapterResult> {
9
+ // Create transport from provider
10
+ const transport =
11
+ provider && 'send' in provider && typeof provider.send === 'function'
12
+ ? // @ts-ignore - ethers5 provider.send is not typed
13
+ custom({ request: ({ method, params }: any) => provider.send(method, params ?? []) })
14
+ : (() => {
15
+ throw new Error('Provider does not support EIP-1193 interface');
16
+ })();
17
+
18
+ // build a viem Account
19
+ const address = (await signer.getAddress()) as `0x${string}`;
20
+ let account: ReturnType<typeof privateKeyToAccount> | ReturnType<typeof toAccount> | `0x${string}`;
21
+
22
+ if ('privateKey' in signer && typeof (signer as Wallet).privateKey === 'string') {
23
+ // Local (true offline) signing → works with Infura via sendRawTransaction
24
+ account = privateKeyToAccount((signer as Wallet).privateKey as `0x${string}`);
25
+ } else if (provider && typeof provider.send === 'function') {
26
+ // Injected wallet (MetaMask/Coinbase) → wallet signs via eth_sendTransaction
27
+ account = address; // JSON-RPC account (not local signing)
28
+ } else {
29
+ throw new Error('Signer does not expose a private key and no injected wallet is available.');
30
+ }
31
+
32
+ const publicClient = createPublicClient({ transport });
33
+ const walletClient = createWalletClient({ transport, account });
34
+
35
+ return { publicClient, walletClient };
36
+ }
@@ -0,0 +1,169 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { parseEther } from 'viem';
3
+ import { Ethers6Adapter } from './ethers6.js';
4
+ import { createMockEIP1193Provider } from './test-utils.js';
5
+ import * as ethers6 from 'ethers6';
6
+
7
+ describe('Ethers6Adapter', () => {
8
+ const testRpcUrl = 'https://ethereum-sepolia.rpc.subquery.network/public';
9
+ const SEPOLIA_CHAIN_ID = 11155111;
10
+ let provider: ethers6.JsonRpcProvider;
11
+ let wallet: ethers6.Wallet;
12
+
13
+ beforeEach(() => {
14
+ // Create common setup for all tests
15
+ provider = new ethers6.JsonRpcProvider(testRpcUrl);
16
+ wallet = new ethers6.Wallet('0x' + '1'.repeat(64), provider);
17
+ });
18
+
19
+ it('should work with real JsonRpcProvider and Wallet', async () => {
20
+ const result = await Ethers6Adapter(provider, wallet);
21
+
22
+ expect(result).toHaveProperty('publicClient');
23
+ expect(result).toHaveProperty('walletClient');
24
+ expect(result.publicClient).toBeDefined();
25
+ expect(result.walletClient).toBeDefined();
26
+ });
27
+
28
+ it('should work with BrowserProvider signer', async () => {
29
+ const mockEthereum = createMockEIP1193Provider();
30
+ const browserProvider = new ethers6.BrowserProvider(mockEthereum as any);
31
+ const wallet = new ethers6.Wallet('0x' + '1'.repeat(64), browserProvider);
32
+
33
+ const result = await Ethers6Adapter(browserProvider, wallet);
34
+
35
+ expect(result).toHaveProperty('publicClient');
36
+ expect(result).toHaveProperty('walletClient');
37
+ });
38
+
39
+ it('should work without configuration', async () => {
40
+ const result = await Ethers6Adapter(provider, wallet);
41
+
42
+ expect(result).toHaveProperty('publicClient');
43
+ expect(result).toHaveProperty('walletClient');
44
+ expect(result.publicClient).toBeDefined();
45
+ expect(result.walletClient).toBeDefined();
46
+ });
47
+
48
+ it('should throw error when signer has no provider', async () => {
49
+ const signerWithoutProvider = { provider: null };
50
+
51
+ await expect(async () => {
52
+ await Ethers6Adapter(signerWithoutProvider as any, wallet);
53
+ }).rejects.toThrow('Provider does not support EIP-1193 interface');
54
+ });
55
+
56
+ it('should work with real network call', async () => {
57
+ const result = await Ethers6Adapter(provider, wallet);
58
+
59
+ // Test real network call
60
+ const chainId = await result.publicClient.getChainId();
61
+ expect(typeof chainId).toBe('number');
62
+ expect(chainId).toBeGreaterThan(0);
63
+ }, 10000);
64
+
65
+ describe('Provider Functions', () => {
66
+ it('should support getChainId', async () => {
67
+ const { publicClient } = await Ethers6Adapter(provider, wallet);
68
+
69
+ const chainId = await publicClient.getChainId();
70
+ expect(typeof chainId).toBe('number');
71
+ expect(chainId).toBe(SEPOLIA_CHAIN_ID); // sepolia
72
+ }, 10000);
73
+
74
+ it('should support call (contract read)', async () => {
75
+ const { publicClient } = await Ethers6Adapter(provider, wallet);
76
+
77
+ // Test eth_call - get ETH balance of zero address
78
+ const balance = await publicClient.getBalance({
79
+ address: '0x0000000000000000000000000000000000000000',
80
+ });
81
+ expect(typeof balance).toBe('bigint');
82
+ }, 10000);
83
+
84
+ it('should support request (raw RPC)', async () => {
85
+ const { publicClient } = await Ethers6Adapter(provider, wallet);
86
+
87
+ // Test raw RPC request - viem requires both method and params
88
+ const blockNumber = (await publicClient.request({
89
+ method: 'eth_blockNumber',
90
+ })) as string;
91
+ expect(typeof blockNumber).toBe('string');
92
+ expect(blockNumber.startsWith('0x')).toBe(true);
93
+ }, 10000);
94
+ });
95
+
96
+ describe('Signer Functions', () => {
97
+ it('should support getAddress', async () => {
98
+ const { walletClient } = await Ethers6Adapter(provider, wallet);
99
+
100
+ const addresses = await walletClient.getAddresses();
101
+ expect(Array.isArray(addresses)).toBe(true);
102
+ // Note: For HTTP transport, this might return empty array
103
+ // For EIP-1193 providers, it would return actual addresses
104
+ }, 10000);
105
+
106
+ it('should support signTypedData', async () => {
107
+ const { walletClient } = await Ethers6Adapter(provider, wallet);
108
+
109
+ const domain = {
110
+ name: 'Test',
111
+ version: '1',
112
+ chainId: SEPOLIA_CHAIN_ID, // Sepolia
113
+ verifyingContract: '0x0000000000000000000000000000000000000000' as const,
114
+ };
115
+
116
+ const types = {
117
+ Message: [{ name: 'content', type: 'string' }],
118
+ };
119
+
120
+ const message = { content: 'Hello World' };
121
+
122
+ const signature = await walletClient.signTypedData({
123
+ domain,
124
+ types,
125
+ primaryType: 'Message',
126
+ message,
127
+ account: walletClient.account!,
128
+ });
129
+
130
+ expect(typeof signature).toBe('string');
131
+ expect(signature.startsWith('0x')).toBe(true);
132
+ }, 10000);
133
+
134
+ it('should support sendTransaction', async () => {
135
+ const { publicClient, walletClient } = await Ethers6Adapter(provider, wallet);
136
+
137
+ // Try to send a transaction - this will fail with Infura but we'll catch the error
138
+ try {
139
+ console.log('estimating gas');
140
+ const gas = await publicClient.estimateGas({
141
+ account: wallet.address as `0x${string}`,
142
+ to: '0x0000000000000000000000000000000000000000',
143
+ value: parseEther('0'),
144
+ });
145
+
146
+ console.log('sending transaction', await wallet.getAddress());
147
+ const hash = await walletClient.sendTransaction({
148
+ to: '0x0000000000000000000000000000000000000000',
149
+ value: parseEther('0'),
150
+ gas,
151
+ account: walletClient.account!,
152
+ chain: walletClient.chain,
153
+ });
154
+ console.log('transaction sent', hash);
155
+
156
+ // If it succeeds (shouldn't with Infura), verify the format
157
+ expect(typeof hash).toBe('string');
158
+ expect(hash.startsWith('0x')).toBe(true);
159
+ expect(hash.length).toBe(66);
160
+ } catch (error: any) {
161
+ // Expected errors: either insufficient funds (good!) or method not supported
162
+ const isInsufficientFunds = error.message.includes('insufficient funds') || error.message.includes('balance');
163
+
164
+ expect(isInsufficientFunds).toBe(true);
165
+ console.log('Expected error (insufficient funds or method not supported):', error.message);
166
+ }
167
+ }, 20000);
168
+ });
169
+ });
@@ -0,0 +1,36 @@
1
+ import { createPublicClient, createWalletClient, custom } from 'viem';
2
+ import { privateKeyToAccount, toAccount } from 'viem/accounts';
3
+ import type { Wallet, AbstractSigner, Provider } from 'ethers6';
4
+ import { type AdapterResult } from './types.js';
5
+
6
+ type Ethers6Signer = AbstractSigner | Wallet;
7
+
8
+ export async function Ethers6Adapter(provider: Provider, signer: Ethers6Signer): Promise<AdapterResult> {
9
+ // Create transport from provider
10
+ const transport =
11
+ provider && 'send' in provider && typeof provider.send === 'function'
12
+ ? // @ts-ignore - ethers6 provider.send is not typed
13
+ custom({ request: ({ method, params }: any) => provider.send(method, params ?? []) })
14
+ : (() => {
15
+ throw new Error('Provider does not support EIP-1193 interface');
16
+ })();
17
+
18
+ // build a viem Account
19
+ const address = (await signer.getAddress()) as `0x${string}`;
20
+ let account: ReturnType<typeof privateKeyToAccount> | ReturnType<typeof toAccount> | `0x${string}`;
21
+
22
+ if ('privateKey' in signer && typeof (signer as Wallet).privateKey === 'string') {
23
+ // Local (true offline) signing → works with Infura via sendRawTransaction
24
+ account = privateKeyToAccount((signer as Wallet).privateKey as `0x${string}`); // local account
25
+ } else if (provider && typeof provider.send === 'function') {
26
+ // Injected wallet (MetaMask/Coinbase) → wallet signs via eth_sendTransaction
27
+ account = address; // JSON-RPC account (not local signing)
28
+ } else {
29
+ throw new Error('Signer does not expose a private key and no injected wallet is available.');
30
+ }
31
+
32
+ const publicClient = createPublicClient({ transport });
33
+ const walletClient = createWalletClient({ transport, account });
34
+
35
+ return { publicClient, walletClient };
36
+ }