@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
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
|
+
}
|