Skip to content

Commit 51cfddb

Browse files
committed
WIP Adds eip-1193 provider
1 parent b2dc321 commit 51cfddb

File tree

10 files changed

+353
-479
lines changed

10 files changed

+353
-479
lines changed

packages/eip-1193-provider/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@
4848
"access": "public"
4949
},
5050
"dependencies": {
51-
"@turnkey/api-key-stamper": "workspace:^",
52-
"@turnkey/http": "workspace:^",
53-
"@turnkey/webauthn-stamper": "workspace:^",
51+
"@turnkey/api-key-stamper": "workspace:*",
52+
"@turnkey/http": "workspace:*",
53+
"@turnkey/webauthn-stamper": "workspace:*",
5454
"@types/react-dom": "^18.2.21",
5555
"react-dom": "^18.2.0",
5656
"viem": "^2.7.19"

packages/eip-1193-provider/src/__tests__/index.test.ts

+178-156
Original file line numberDiff line numberDiff line change
@@ -1,165 +1,187 @@
1-
// import 'dotenv/config';
2-
// import { TurnkeyClient } from '@turnkey/http';
3-
// import {
4-
// Address,
5-
// EIP1193Provider,
6-
// Index,
7-
// Quantity,
8-
// RpcTransactionRequest,
9-
// TransactionRequestEIP1559,
10-
// createWalletClient,
11-
// getAddress,
12-
// http,
13-
// numberToHex,
14-
// parseEther,
15-
// parseGwei,
16-
// recoverAddress,
17-
// serializeTransaction,
18-
// stringToHex,
19-
// } from 'viem';
20-
// import { sepolia } from 'viem/chains';
21-
22-
// import { createEIP1193Provider } from '../packages/eip-1193-provider/src';
23-
// import { createAPIKeyStamper } from '../packages/eip-1193-provider/src/turnkey';
1+
import 'dotenv/config';
2+
3+
import {
4+
type Address,
5+
type EIP1193Provider,
6+
type Index,
7+
type Quantity,
8+
type RpcTransactionRequest,
9+
type TransactionRequestEIP1559,
10+
createWalletClient,
11+
getAddress,
12+
http,
13+
numberToHex,
14+
parseEther,
15+
parseGwei,
16+
recoverAddress,
17+
serializeTransaction,
18+
stringToHex,
19+
} from 'viem';
20+
21+
import { sepolia } from 'viem/chains';
22+
23+
import { beforeEach, describe, it, expect } from '@jest/globals';
24+
import { createAPIKeyStamper, createEIP1193Provider } from '../';
25+
import { TurnkeyClient } from '@turnkey/http';
26+
2427
// import { preprocessTransaction } from '../packages/eip-1193-provider/src/utils';
2528
// import { createAccount } from '@turnkey/viem';
2629

27-
// const ORG_ID = process.env.ORG_ID;
28-
// const WALLET_ID = process.env.WALLET_ID;
29-
// const TURNKEY_API_PUBLIC_KEY = process.env.TURNKEY_API_PUBLIC_KEY ?? '';
30-
// const TURNKEY_API_PRIVATE_KEY = process.env.TURNKEY_API_PRIVATE_KEY ?? '';
31-
// const EXPECTED_WALLET_ADDRESS: Address =
32-
// '0xb9d2e69E033b3cFBa1877b86041958778E1ae919';
33-
// const RPC_URL = 'https://sepolia.infura.io/v3/c20bd1f24c384a0484d689caff11cacd';
34-
// const RECEIVER_ADDRESS: Address = '0x6f85Eb534E14D605d4e82bF97ddF59c18F686699';
35-
// const TEST_TIMEOUT = 10_000;
36-
37-
// declare module 'vitest' {
38-
// export interface TestContext {
39-
// eip1193Provider?: EIP1193Provider;
40-
// }
41-
// }
42-
43-
// beforeEach(async (context) => {
44-
// const tk = new TurnkeyClient(
45-
// { baseUrl: 'https://api.turnkey.com' },
46-
// await createAPIKeyStamper({
47-
// apiPublicKey: TURNKEY_API_PUBLIC_KEY,
48-
// apiPrivateKey: TURNKEY_API_PRIVATE_KEY,
49-
// })
50-
// );
51-
// // extend context
52-
// context.eip1193Provider = createEIP1193Provider(
53-
// RPC_URL,
54-
// {
55-
// walletId: WALLET_ID,
56-
// organizationId: ORG_ID,
57-
// },
58-
// tk
59-
// );
60-
// });
61-
62-
// describe('eth_accounts', () => {
63-
// it("should get accounts associated with the user's wallet", async ({
64-
// eip1193Provider,
65-
// }) => {
66-
// const accounts = await eip1193Provider?.request({ method: 'eth_accounts' });
67-
68-
// assert.isArray(accounts);
69-
// assert.isNotEmpty(accounts, 'accounts should be non-empty');
70-
// expect(accounts).toContain(EXPECTED_WALLET_ADDRESS);
71-
// });
72-
// });
73-
74-
// describe('eth_requestAccounts', () => {
75-
// it("should request accounts associated with the user's wallet", async ({
76-
// eip1193Provider,
77-
// }) => {
78-
// const accounts = await eip1193Provider?.request({
79-
// method: 'eth_requestAccounts',
80-
// });
30+
const ORG_ID = process.env.ORG_ID;
31+
const WALLET_ID = process.env.WALLET_ID;
32+
const TURNKEY_API_PUBLIC_KEY = process.env.TURNKEY_API_PUBLIC_KEY ?? '';
33+
const TURNKEY_API_PRIVATE_KEY = process.env.TURNKEY_API_PRIVATE_KEY ?? '';
34+
const EXPECTED_WALLET_ADDRESS: Address =
35+
'0xb9d2e69E033b3cFBa1877b86041958778E1ae919';
36+
const RPC_URL = 'https://sepolia.infura.io/v3/c20bd1f24c384a0484d689caff11cacd';
37+
const RECEIVER_ADDRESS: Address = '0x6f85Eb534E14D605d4e82bF97ddF59c18F686699';
38+
const TEST_TIMEOUT = 10_000;
8139

82-
// assert.isArray(accounts);
83-
// assert.isNotEmpty(accounts, 'accounts should be non-empty');
84-
// expect(accounts).toContain(EXPECTED_WALLET_ADDRESS);
85-
// });
86-
// });
87-
88-
// describe('eth_sign/person_sign', () => {
89-
// it(
90-
// 'should sign a message',
91-
// async ({ eip1193Provider }) => {
92-
// const messageDigest = stringToHex('A man, a plan, a canal, Panama');
93-
// const signerAddress = EXPECTED_WALLET_ADDRESS;
94-
// const signature = await eip1193Provider?.request({
95-
// method: 'personal_sign',
96-
// params: [signerAddress, messageDigest],
97-
// });
98-
// assert.isNotEmpty(signature, '');
99-
// const address = await recoverAddress({
100-
// hash: messageDigest,
101-
// signature: signature!,
102-
// });
103-
// assert.equal(getAddress(address), getAddress(signerAddress));
104-
// assert.match(signature!, /^0x.*$/);
105-
// },
106-
// TEST_TIMEOUT
107-
// );
108-
// });
109-
110-
// describe('eth_signTransaction', () => {
111-
// it('should sign a transaction', async ({ eip1193Provider }) => {
112-
// const from = EXPECTED_WALLET_ADDRESS;
113-
// const to = RECEIVER_ADDRESS;
114-
115-
// const serializedTransaction = preprocessTransaction({
116-
// from,
117-
// to,
118-
// value: numberToHex(parseEther('0.001')),
119-
// chainId: numberToHex(sepolia.id),
120-
// nonce: numberToHex(0),
121-
// gas: numberToHex(21000n),
122-
// maxFeePerGas: numberToHex(parseGwei('20')),
123-
// maxPriorityFeePerGas: numberToHex(parseGwei('2')),
124-
// type: '0x2',
125-
// } as TransactionRequestEIP1559<Quantity, Index, '0x2'>);
126-
127-
// const signature = await eip1193Provider?.request({
128-
// method: 'eth_signTransaction',
129-
// params: [
130-
// {
131-
// from,
132-
// to,
133-
// value: numberToHex(parseEther('0.001')),
134-
// chainId: numberToHex(sepolia.id),
135-
// nonce: numberToHex(0),
136-
// gas: numberToHex(21000n),
137-
// maxFeePerGas: numberToHex(parseGwei('20')),
138-
// maxPriorityFeePerGas: numberToHex(parseGwei('2')),
139-
// type: '0x2',
140-
// } as TransactionRequestEIP1559<Quantity, Index, '0x2'>,
141-
// ],
142-
// });
40+
describe('Test Turnkey EIP-1193 Provider', () => {
41+
let eip1193Provider;
14342

144-
// assert.isNotEmpty(signature, '');
145-
// assert.match(signature!, /^0x.*$/);
146-
// });
147-
// });
148-
149-
// describe('eth_getBlockByNumber', () => {
150-
// it('should get blocknumber using the underlying RPC provider', async ({
151-
// eip1193Provider,
152-
// }) => {
153-
// const blockNumber = await eip1193Provider?.request({
154-
// method: 'eth_blockNumber',
155-
// });
156-
// console.log(blockNumber);
157-
// assert.isNotEmpty(blockNumber, '');
158-
// assert.match(blockNumber!, /^0x.*$/);
159-
// });
160-
// });
43+
beforeEach(async () => {
44+
const tk = new TurnkeyClient(
45+
{ baseUrl: 'https://api.turnkey.com' },
46+
await createAPIKeyStamper({
47+
apiPublicKey: TURNKEY_API_PUBLIC_KEY,
48+
apiPrivateKey: TURNKEY_API_PRIVATE_KEY,
49+
})
50+
);
51+
52+
eip1193Provider = await createEIP1193Provider({
53+
rpcUrl: RPC_URL,
54+
walletId: WALLET_ID,
55+
organizationId: ORG_ID,
56+
turnkeyClient: tk,
57+
chainId: sepolia.id,
58+
});
59+
});
60+
61+
describe('Supported Methods', () => {
62+
describe('EIP-1193 Wallet Methods', () => {
63+
describe('eth_accounts', () => {
64+
it("should get accounts associated with the user's wallet", async () => {
65+
const accounts = await eip1193Provider?.request({
66+
method: 'eth_accounts',
67+
});
68+
expect(accounts).toBeDefined();
69+
expect(Array.isArray(accounts)).toBeTruthy();
70+
expect(accounts.length).toBeGreaterThan(0); // Corrected line
71+
expect(accounts).toContain(EXPECTED_WALLET_ADDRESS);
72+
});
73+
});
74+
75+
describe('eth_requestAccounts', () => {
76+
it("should request accounts associated with the user's wallet", async () => {
77+
const accounts = await eip1193Provider.request({
78+
method: 'eth_requestAccounts',
79+
});
80+
81+
expect(accounts).toBeInstanceOf(Array);
82+
expect(accounts.length).toBeGreaterThan(0);
83+
expect(accounts).toContain(EXPECTED_WALLET_ADDRESS);
84+
});
85+
});
86+
87+
describe('eth_sign/person_sign', () => {
88+
it(
89+
'should sign a message',
90+
async () => {
91+
const messageDigest = stringToHex('A man, a plan, a canal, Panama');
92+
const signerAddress = EXPECTED_WALLET_ADDRESS;
93+
const signature = await eip1193Provider?.request({
94+
method: 'personal_sign',
95+
params: [signerAddress, messageDigest],
96+
});
97+
expect(signature).toBeDefined();
98+
expect(signature).not.toBe('');
99+
const address = await recoverAddress({
100+
hash: messageDigest,
101+
signature: signature!,
102+
});
103+
expect(getAddress(address)).toBe(getAddress(signerAddress));
104+
expect(signature).toMatch(/^0x.*$/);
105+
},
106+
TEST_TIMEOUT
107+
);
108+
});
109+
110+
describe.only('eth_signTransaction', () => {
111+
it('should sign a transaction', async () => {
112+
const from = EXPECTED_WALLET_ADDRESS;
113+
const to = RECEIVER_ADDRESS;
114+
115+
// const serializedTransaction = preprocessTransaction({
116+
// from,
117+
// to,
118+
// value: numberToHex(parseEther('0.001')),
119+
// chainId: numberToHex(sepolia.id),
120+
// nonce: numberToHex(0),
121+
// gas: numberToHex(21000n),
122+
// maxFeePerGas: numberToHex(parseGwei('20')),
123+
// maxPriorityFeePerGas: numberToHex(parseGwei('2')),
124+
// type: '0x2',
125+
// } as TransactionRequestEIP1559<Quantity, Index, '0x2'>);
126+
127+
const signature = await eip1193Provider?.request({
128+
method: 'eth_signTransaction',
129+
params: [
130+
{
131+
from,
132+
to,
133+
value: numberToHex(parseEther('0.001')),
134+
chainId: numberToHex(sepolia.id),
135+
nonce: numberToHex(0),
136+
gas: numberToHex(21000n),
137+
maxFeePerGas: numberToHex(parseGwei('20')),
138+
maxPriorityFeePerGas: numberToHex(parseGwei('2')),
139+
type: '0x2',
140+
} as TransactionRequestEIP1559<Quantity, Index, '0x2'>,
141+
],
142+
});
143+
144+
expect(signature).toBeDefined();
145+
expect(signature).not.toBe('');
146+
expect(signature).toMatch(/^0x.*$/);
147+
});
148+
});
149+
150+
describe('eth_getBlockByNumber', () => {
151+
it('should get blocknumber using the underlying RPC provider', async () => {
152+
const blockNumber = await eip1193Provider?.request({
153+
method: 'eth_blockNumber',
154+
});
155+
expect(blockNumber).toBeDefined();
156+
expect(blockNumber).not.toBe('');
157+
expect(blockNumber).toMatch(/^0x.*$/);
158+
});
159+
});
160+
});
161+
162+
describe('Public RPC Methods', () => {
163+
describe('eth_chainId', () => {
164+
it(
165+
'should get the correct chain id using the underlying RPC provider',
166+
async () => {
167+
const chainId = await eip1193Provider?.request({
168+
method: 'eth_chainId',
169+
});
170+
expect(chainId).toBeDefined();
171+
expect(chainId).not.toBe('');
172+
expect(chainId).toMatch(/^0x.*$/);
173+
expect(parseInt(chainId!, 16)).toBe(sepolia.id);
174+
},
175+
TEST_TIMEOUT
176+
);
177+
});
178+
});
179+
});
180+
181+
describe('Unsupported Methods', () => {});
182+
});
161183

162-
// describe.only(
184+
// describe(
163185
// 'eth_chainId',
164186
// () => {
165187
// it('should get the correct chain id using the underlying RPC provider', async ({
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { UUID } from 'crypto';
2+
3+
export {};
4+
5+
declare global {
6+
namespace NodeJS {
7+
interface ProcessEnv {
8+
WALLET_ID: UUID;
9+
ORG_ID: UUID;
10+
TURNKEY_API_PUBLIC_KEY: string;
11+
TURNKEY_API_PRIVATE_KEY: string;
12+
}
13+
}
14+
}

0 commit comments

Comments
 (0)