@zerodev/wallet-react 0.0.1-alpha.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/_cjs/actions.js +193 -0
  3. package/dist/_cjs/connector.js +221 -0
  4. package/dist/_cjs/constants.js +4 -0
  5. package/dist/_cjs/hooks/useAuthenticateOAuth.js +18 -0
  6. package/dist/_cjs/hooks/useExportWallet.js +18 -0
  7. package/dist/_cjs/hooks/useLoginPasskey.js +18 -0
  8. package/dist/_cjs/hooks/useRefreshSession.js +18 -0
  9. package/dist/_cjs/hooks/useRegisterPasskey.js +18 -0
  10. package/dist/_cjs/hooks/useSendOTP.js +18 -0
  11. package/dist/_cjs/hooks/useVerifyOTP.js +18 -0
  12. package/dist/_cjs/index.js +23 -0
  13. package/dist/_cjs/oauth.js +84 -0
  14. package/dist/_cjs/package.json +1 -0
  15. package/dist/_cjs/provider.js +163 -0
  16. package/dist/_cjs/store.js +51 -0
  17. package/dist/_cjs/utils/aaUtils.js +7 -0
  18. package/dist/_cjs/utils/timers.js +50 -0
  19. package/dist/_esm/actions.js +225 -0
  20. package/dist/_esm/connector.js +248 -0
  21. package/dist/_esm/constants.js +1 -0
  22. package/dist/_esm/hooks/useAuthenticateOAuth.js +18 -0
  23. package/dist/_esm/hooks/useExportWallet.js +18 -0
  24. package/dist/_esm/hooks/useLoginPasskey.js +18 -0
  25. package/dist/_esm/hooks/useRefreshSession.js +18 -0
  26. package/dist/_esm/hooks/useRegisterPasskey.js +18 -0
  27. package/dist/_esm/hooks/useSendOTP.js +18 -0
  28. package/dist/_esm/hooks/useVerifyOTP.js +18 -0
  29. package/dist/_esm/index.js +10 -0
  30. package/dist/_esm/oauth.js +77 -0
  31. package/dist/_esm/package.json +1 -0
  32. package/dist/_esm/provider.js +169 -0
  33. package/dist/_esm/store.js +51 -0
  34. package/dist/_esm/utils/aaUtils.js +4 -0
  35. package/dist/_esm/utils/timers.js +55 -0
  36. package/dist/_types/actions.d.ts +124 -0
  37. package/dist/_types/actions.d.ts.map +1 -0
  38. package/dist/_types/connector.d.ts +18 -0
  39. package/dist/_types/connector.d.ts.map +1 -0
  40. package/dist/_types/constants.d.ts +2 -0
  41. package/dist/_types/constants.d.ts.map +1 -0
  42. package/dist/_types/hooks/useAuthenticateOAuth.d.ts +18 -0
  43. package/dist/_types/hooks/useAuthenticateOAuth.d.ts.map +1 -0
  44. package/dist/_types/hooks/useExportWallet.d.ts +18 -0
  45. package/dist/_types/hooks/useExportWallet.d.ts.map +1 -0
  46. package/dist/_types/hooks/useLoginPasskey.d.ts +18 -0
  47. package/dist/_types/hooks/useLoginPasskey.d.ts.map +1 -0
  48. package/dist/_types/hooks/useRefreshSession.d.ts +18 -0
  49. package/dist/_types/hooks/useRefreshSession.d.ts.map +1 -0
  50. package/dist/_types/hooks/useRegisterPasskey.d.ts +18 -0
  51. package/dist/_types/hooks/useRegisterPasskey.d.ts.map +1 -0
  52. package/dist/_types/hooks/useSendOTP.d.ts +18 -0
  53. package/dist/_types/hooks/useSendOTP.d.ts.map +1 -0
  54. package/dist/_types/hooks/useVerifyOTP.d.ts +18 -0
  55. package/dist/_types/hooks/useVerifyOTP.d.ts.map +1 -0
  56. package/dist/_types/index.d.ts +15 -0
  57. package/dist/_types/index.d.ts.map +1 -0
  58. package/dist/_types/oauth.d.ts +21 -0
  59. package/dist/_types/oauth.d.ts.map +1 -0
  60. package/dist/_types/provider.d.ts +19 -0
  61. package/dist/_types/provider.d.ts.map +1 -0
  62. package/dist/_types/store.d.ts +52 -0
  63. package/dist/_types/store.d.ts.map +1 -0
  64. package/dist/_types/utils/aaUtils.d.ts +2 -0
  65. package/dist/_types/utils/aaUtils.d.ts.map +1 -0
  66. package/dist/_types/utils/timers.d.ts +22 -0
  67. package/dist/_types/utils/timers.d.ts.map +1 -0
  68. package/dist/tsconfig.build.tsbuildinfo +1 -0
  69. package/package.json +48 -0
  70. package/package.json.type +1 -0
  71. package/src/actions.ts +402 -0
  72. package/src/connector.ts +336 -0
  73. package/src/constants.ts +1 -0
  74. package/src/hooks/useAuthenticateOAuth.ts +57 -0
  75. package/src/hooks/useExportWallet.ts +57 -0
  76. package/src/hooks/useLoginPasskey.ts +57 -0
  77. package/src/hooks/useRefreshSession.ts +57 -0
  78. package/src/hooks/useRegisterPasskey.ts +57 -0
  79. package/src/hooks/useSendOTP.ts +57 -0
  80. package/src/hooks/useVerifyOTP.ts +57 -0
  81. package/src/index.ts +14 -0
  82. package/src/oauth.ts +124 -0
  83. package/src/provider.ts +235 -0
  84. package/src/store.ts +113 -0
  85. package/src/utils/aaUtils.ts +5 -0
  86. package/src/utils/timers.ts +80 -0
  87. package/tsconfig.build.json +10 -0
  88. package/tsconfig.build.tsbuildinfo +1 -0
@@ -0,0 +1,248 @@
1
+ import { createConnector } from '@wagmi/core';
2
+ import { createKernelAccount, createKernelAccountClient, createZeroDevPaymasterClient, } from '@zerodev/sdk';
3
+ import { getEntryPoint, KERNEL_V3_3 } from '@zerodev/sdk/constants';
4
+ import { createZeroDevWallet } from '@zerodev/wallet-core';
5
+ import { createPublicClient, http } from 'viem';
6
+ import { createProvider } from './provider.js';
7
+ import { createZeroDevWalletStore } from './store.js';
8
+ import { getAAUrl } from './utils/aaUtils.js';
9
+ export function zeroDevWallet(params) {
10
+ return createConnector((wagmiConfig) => {
11
+ let store;
12
+ let provider;
13
+ let initPromise;
14
+ // Get transports from Wagmi config (uses user's RPC URLs)
15
+ const transports = wagmiConfig.transports;
16
+ // Lazy initialization - only runs on client side
17
+ const initialize = async () => {
18
+ initPromise ??= (async () => {
19
+ console.log('Initializing ZeroDevWallet connector...');
20
+ // Initialize wallet SDK
21
+ const wallet = await createZeroDevWallet({
22
+ projectId: params.projectId,
23
+ ...(params.organizationId && {
24
+ organizationId: params.organizationId,
25
+ }),
26
+ ...(params.proxyBaseUrl && { proxyBaseUrl: params.proxyBaseUrl }),
27
+ ...(params.sessionStorage && {
28
+ sessionStorage: params.sessionStorage,
29
+ }),
30
+ ...(params.rpId && { rpId: params.rpId }),
31
+ });
32
+ // Create store
33
+ store = createZeroDevWalletStore();
34
+ store.getState().setWallet(wallet);
35
+ // Initialize chainIds
36
+ const chainIds = params.chains.map((c) => c.id);
37
+ store.setState({ chainIds });
38
+ // Store OAuth config if provided
39
+ if (params.oauthConfig) {
40
+ store.getState().setOAuthConfig(params.oauthConfig);
41
+ }
42
+ // Create EIP-1193 provider
43
+ provider = createProvider({
44
+ store,
45
+ config: params,
46
+ chains: Array.from(params.chains),
47
+ });
48
+ // Check for existing session (page reload)
49
+ const session = await wallet.getSession();
50
+ if (session) {
51
+ console.log('Found existing session, restoring...');
52
+ const eoaAccount = await wallet.toAccount();
53
+ store.getState().setEoaAccount(eoaAccount);
54
+ store.getState().setSession(session);
55
+ }
56
+ console.log('ZeroDevWallet connector initialized');
57
+ })();
58
+ return initPromise;
59
+ };
60
+ return {
61
+ id: 'zerodev-wallet',
62
+ name: 'ZeroDevWallet',
63
+ type: 'injected',
64
+ async setup() {
65
+ // Initialize on client-side mount (setup only runs client-side)
66
+ if (typeof window !== 'undefined') {
67
+ await initialize();
68
+ }
69
+ },
70
+ async connect({ chainId, ...rest } = {}) {
71
+ const withCapabilities = ('withCapabilities' in rest && rest.withCapabilities) || false;
72
+ const isReconnecting = ('isReconnecting' in rest && rest.isReconnecting) || false;
73
+ // Ensure wallet is initialized (lazy init on first connect)
74
+ await initialize();
75
+ console.log(isReconnecting
76
+ ? 'Reconnecting ZeroDevWallet...'
77
+ : 'Connecting ZeroDevWallet...');
78
+ const state = store.getState();
79
+ // Determine active chain
80
+ const activeChainId = chainId || state.chainIds[0];
81
+ if (!activeChainId) {
82
+ throw new Error('No chain configured');
83
+ }
84
+ // If reconnecting and already have kernel account, return immediately
85
+ if (isReconnecting && state.kernelAccounts.has(activeChainId)) {
86
+ const kernelAccount = state.kernelAccounts.get(activeChainId);
87
+ if (kernelAccount?.address) {
88
+ console.log('Already connected:', kernelAccount.address);
89
+ return {
90
+ accounts: [kernelAccount.address],
91
+ chainId: activeChainId,
92
+ };
93
+ }
94
+ }
95
+ if (!state.eoaAccount) {
96
+ throw new Error('Not authenticated. Please authenticate first using passkey, OAuth, or OTP.');
97
+ }
98
+ // Create KernelAccount for this chain if doesn't exist
99
+ if (!state.kernelAccounts.has(activeChainId)) {
100
+ const chain = params.chains.find((c) => c.id === activeChainId);
101
+ if (!chain) {
102
+ throw new Error(`Chain ${activeChainId} not found in config`);
103
+ }
104
+ // Use transport from Wagmi config (has user's RPC URL)
105
+ const transport = transports?.[activeChainId] ?? http();
106
+ const publicClient = createPublicClient({
107
+ chain,
108
+ transport,
109
+ });
110
+ console.log(`Creating kernel account for chain ${activeChainId}...`);
111
+ const kernelAccount = await createKernelAccount(publicClient, {
112
+ entryPoint: getEntryPoint('0.7'),
113
+ kernelVersion: KERNEL_V3_3,
114
+ eip7702Account: state.eoaAccount,
115
+ });
116
+ // Store kernel account for this chain
117
+ store.getState().setKernelAccount(activeChainId, kernelAccount);
118
+ // Create and store kernel client for transactions
119
+ const kernelClient = createKernelAccountClient({
120
+ account: kernelAccount,
121
+ bundlerTransport: http(getAAUrl(activeChainId, params.aaUrl)),
122
+ chain,
123
+ client: publicClient,
124
+ paymaster: createZeroDevPaymasterClient({
125
+ chain,
126
+ transport: http(getAAUrl(activeChainId, params.aaUrl)),
127
+ }),
128
+ });
129
+ store.getState().setKernelClient(activeChainId, kernelClient);
130
+ }
131
+ // Set as active chain
132
+ store.getState().setActiveChain(activeChainId);
133
+ // Get fresh state after updates
134
+ const freshState = store.getState();
135
+ const kernelAccount = freshState.kernelAccounts.get(activeChainId);
136
+ console.log('ZeroDevWallet connected:', kernelAccount.address);
137
+ const address = kernelAccount.address;
138
+ return {
139
+ accounts: (withCapabilities
140
+ ? [{ address, capabilities: {} }]
141
+ : [address]),
142
+ chainId: activeChainId,
143
+ };
144
+ },
145
+ async disconnect() {
146
+ console.log('Disconnecting ZeroDevWallet...');
147
+ if (!store)
148
+ return;
149
+ const wallet = store.getState().wallet;
150
+ // Cleanup provider (clears timers)
151
+ provider?.destroy();
152
+ await wallet?.logout();
153
+ store.getState().clear();
154
+ },
155
+ async getAccounts() {
156
+ if (!store)
157
+ return [];
158
+ const { eoaAccount, kernelAccounts, chainIds } = store.getState();
159
+ // Return EOA address if we have it (EIP-7702: EOA address = kernel address)
160
+ if (eoaAccount) {
161
+ return [eoaAccount.address];
162
+ }
163
+ // Fallback: check kernel accounts
164
+ const activeAccount = chainIds[0]
165
+ ? kernelAccounts.get(chainIds[0])
166
+ : null;
167
+ return activeAccount ? [activeAccount.address] : [];
168
+ },
169
+ async getChainId() {
170
+ if (!store)
171
+ return params.chains[0].id;
172
+ return store.getState().chainIds[0] || params.chains[0].id;
173
+ },
174
+ async getProvider() {
175
+ await initialize();
176
+ return provider;
177
+ },
178
+ async switchChain({ chainId }) {
179
+ await initialize();
180
+ console.log(`Switching to chain ${chainId}...`);
181
+ const state = store.getState();
182
+ if (!state.eoaAccount) {
183
+ throw new Error('Not authenticated');
184
+ }
185
+ // Update active chain
186
+ store.getState().setActiveChain(chainId);
187
+ // Create kernel account for new chain if doesn't exist
188
+ if (!state.kernelAccounts.has(chainId)) {
189
+ const chain = params.chains.find((c) => c.id === chainId);
190
+ if (!chain) {
191
+ throw new Error(`Chain ${chainId} not found in config`);
192
+ }
193
+ // Use transport from Wagmi config (has user's RPC URL)
194
+ const transport = transports?.[chainId] ?? http();
195
+ const publicClient = createPublicClient({
196
+ chain,
197
+ transport,
198
+ });
199
+ console.log(`Creating kernel account for chain ${chainId}...`);
200
+ const kernelAccount = await createKernelAccount(publicClient, {
201
+ entryPoint: getEntryPoint('0.7'),
202
+ kernelVersion: KERNEL_V3_3,
203
+ eip7702Account: state.eoaAccount,
204
+ });
205
+ store.getState().setKernelAccount(chainId, kernelAccount);
206
+ const kernelClient = createKernelAccountClient({
207
+ account: kernelAccount,
208
+ bundlerTransport: http(getAAUrl(chainId, params.aaUrl)),
209
+ chain,
210
+ client: publicClient,
211
+ paymaster: createZeroDevPaymasterClient({
212
+ chain,
213
+ transport: http(getAAUrl(chainId, params.aaUrl)),
214
+ }),
215
+ });
216
+ store.getState().setKernelClient(chainId, kernelClient);
217
+ }
218
+ return params.chains.find((c) => c.id === chainId);
219
+ },
220
+ async isAuthorized() {
221
+ // Just check if we have a session - don't initialize here (too slow)
222
+ if (!store)
223
+ return false;
224
+ return !!store.getState().eoaAccount;
225
+ },
226
+ // Custom method for hooks to access store
227
+ async getStore() {
228
+ await initialize();
229
+ return store;
230
+ },
231
+ // Event listeners
232
+ onAccountsChanged() {
233
+ // Not applicable for this wallet type
234
+ },
235
+ onChainChanged() {
236
+ // Handled by Wagmi
237
+ },
238
+ onConnect() {
239
+ // Handled by Wagmi
240
+ },
241
+ onDisconnect() {
242
+ console.log('Disconnect event');
243
+ provider?.destroy();
244
+ store.getState().clear();
245
+ },
246
+ };
247
+ });
248
+ }
@@ -0,0 +1 @@
1
+ export const ZERODEV_AA_URL = 'https://rpc.zerodev.app/api/v3/';
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+ import { useMutation, } from '@tanstack/react-query';
3
+ import { useConfig } from 'wagmi';
4
+ import { authenticateOAuth } from '../actions.js';
5
+ /**
6
+ * Hook to authenticate with OAuth (opens popup)
7
+ */
8
+ export function useAuthenticateOAuth(parameters = {}) {
9
+ const { mutation } = parameters;
10
+ const config = useConfig(parameters);
11
+ return useMutation({
12
+ ...mutation,
13
+ async mutationFn(variables) {
14
+ return authenticateOAuth(config, variables);
15
+ },
16
+ mutationKey: ['authenticateOAuth'],
17
+ });
18
+ }
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+ import { useMutation, } from '@tanstack/react-query';
3
+ import { useConfig } from 'wagmi';
4
+ import { exportWallet } from '../actions.js';
5
+ /**
6
+ * Hook to export wallet seed phrase
7
+ */
8
+ export function useExportWallet(parameters = {}) {
9
+ const { mutation } = parameters;
10
+ const config = useConfig(parameters);
11
+ return useMutation({
12
+ ...mutation,
13
+ async mutationFn(variables) {
14
+ return exportWallet(config, variables);
15
+ },
16
+ mutationKey: ['exportWallet'],
17
+ });
18
+ }
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+ import { useMutation, } from '@tanstack/react-query';
3
+ import { useConfig } from 'wagmi';
4
+ import { loginPasskey } from '../actions.js';
5
+ /**
6
+ * Hook to login with passkey
7
+ */
8
+ export function useLoginPasskey(parameters = {}) {
9
+ const { mutation } = parameters;
10
+ const config = useConfig(parameters);
11
+ return useMutation({
12
+ ...mutation,
13
+ async mutationFn(variables) {
14
+ return loginPasskey(config, variables);
15
+ },
16
+ mutationKey: ['loginPasskey'],
17
+ });
18
+ }
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+ import { useMutation, } from '@tanstack/react-query';
3
+ import { useConfig } from 'wagmi';
4
+ import { refreshSession } from '../actions.js';
5
+ /**
6
+ * Hook to manually refresh session
7
+ */
8
+ export function useRefreshSession(parameters = {}) {
9
+ const { mutation } = parameters;
10
+ const config = useConfig(parameters);
11
+ return useMutation({
12
+ ...mutation,
13
+ async mutationFn(variables) {
14
+ return refreshSession(config, variables);
15
+ },
16
+ mutationKey: ['refreshSession'],
17
+ });
18
+ }
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+ import { useMutation, } from '@tanstack/react-query';
3
+ import { useConfig } from 'wagmi';
4
+ import { registerPasskey } from '../actions.js';
5
+ /**
6
+ * Hook to register with passkey
7
+ */
8
+ export function useRegisterPasskey(parameters = {}) {
9
+ const { mutation } = parameters;
10
+ const config = useConfig(parameters);
11
+ return useMutation({
12
+ ...mutation,
13
+ async mutationFn(variables) {
14
+ return registerPasskey(config, variables);
15
+ },
16
+ mutationKey: ['registerPasskey'],
17
+ });
18
+ }
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+ import { useMutation, } from '@tanstack/react-query';
3
+ import { useConfig } from 'wagmi';
4
+ import { sendOTP } from '../actions.js';
5
+ /**
6
+ * Hook to send OTP via email
7
+ */
8
+ export function useSendOTP(parameters = {}) {
9
+ const { mutation } = parameters;
10
+ const config = useConfig(parameters);
11
+ return useMutation({
12
+ ...mutation,
13
+ async mutationFn(variables) {
14
+ return sendOTP(config, variables);
15
+ },
16
+ mutationKey: ['sendOTP'],
17
+ });
18
+ }
@@ -0,0 +1,18 @@
1
+ 'use client';
2
+ import { useMutation, } from '@tanstack/react-query';
3
+ import { useConfig } from 'wagmi';
4
+ import { verifyOTP } from '../actions.js';
5
+ /**
6
+ * Hook to verify OTP code
7
+ */
8
+ export function useVerifyOTP(parameters = {}) {
9
+ const { mutation } = parameters;
10
+ const config = useConfig(parameters);
11
+ return useMutation({
12
+ ...mutation,
13
+ async mutationFn(variables) {
14
+ return verifyOTP(config, variables);
15
+ },
16
+ mutationKey: ['verifyOTP'],
17
+ });
18
+ }
@@ -0,0 +1,10 @@
1
+ export { zeroDevWallet } from './connector.js';
2
+ export { useAuthenticateOAuth } from './hooks/useAuthenticateOAuth.js';
3
+ export { useExportWallet } from './hooks/useExportWallet.js';
4
+ export { useLoginPasskey } from './hooks/useLoginPasskey.js';
5
+ export { useRefreshSession } from './hooks/useRefreshSession.js';
6
+ export { useRegisterPasskey } from './hooks/useRegisterPasskey.js';
7
+ export { useSendOTP } from './hooks/useSendOTP.js';
8
+ export { useVerifyOTP } from './hooks/useVerifyOTP.js';
9
+ export { OAUTH_PROVIDERS } from './oauth.js';
10
+ export { createZeroDevWalletStore } from './store.js';
@@ -0,0 +1,77 @@
1
+ import { sha256 } from 'viem';
2
+ export const OAUTH_PROVIDERS = {
3
+ GOOGLE: 'google',
4
+ };
5
+ const GOOGLE_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
6
+ const POPUP_WIDTH = 500;
7
+ const POPUP_HEIGHT = 600;
8
+ export function buildOAuthUrl(params) {
9
+ const { provider, clientId, redirectUri, nonce, state } = params;
10
+ if (provider !== OAUTH_PROVIDERS.GOOGLE) {
11
+ throw new Error(`Unsupported OAuth provider: ${provider}`);
12
+ }
13
+ const authUrl = new URL(GOOGLE_AUTH_URL);
14
+ authUrl.searchParams.set('client_id', clientId);
15
+ authUrl.searchParams.set('redirect_uri', redirectUri);
16
+ authUrl.searchParams.set('response_type', 'id_token');
17
+ authUrl.searchParams.set('scope', 'openid email profile');
18
+ authUrl.searchParams.set('nonce', nonce);
19
+ authUrl.searchParams.set('prompt', 'select_account');
20
+ let stateParam = `provider=${provider}`;
21
+ if (state) {
22
+ const additionalState = Object.entries(state)
23
+ .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
24
+ .join('&');
25
+ if (additionalState) {
26
+ stateParam += `&${additionalState}`;
27
+ }
28
+ }
29
+ authUrl.searchParams.set('state', stateParam);
30
+ return authUrl.toString();
31
+ }
32
+ export function openOAuthPopup(url) {
33
+ const width = POPUP_WIDTH;
34
+ const height = POPUP_HEIGHT;
35
+ const left = window.screenX + (window.innerWidth - width) / 2;
36
+ const top = window.screenY + (window.innerHeight - height) / 2;
37
+ const authWindow = window.open('about:blank', '_blank', `width=${width},height=${height},top=${top},left=${left},scrollbars=yes,resizable=yes`);
38
+ if (authWindow) {
39
+ authWindow.location.href = url;
40
+ }
41
+ return authWindow;
42
+ }
43
+ export function extractOAuthToken(url) {
44
+ const hashParams = new URLSearchParams(url.split('#')[1]);
45
+ let idToken = hashParams.get('id_token');
46
+ if (!idToken) {
47
+ const queryParams = new URLSearchParams(url.split('?')[1]);
48
+ idToken = queryParams.get('id_token');
49
+ }
50
+ return idToken;
51
+ }
52
+ export function pollOAuthPopup(authWindow, originUrl, onSuccess, onError) {
53
+ const interval = setInterval(() => {
54
+ try {
55
+ if (authWindow.closed) {
56
+ clearInterval(interval);
57
+ onError(new Error('Authentication window was closed'));
58
+ return;
59
+ }
60
+ const url = authWindow.location.href || '';
61
+ if (url.startsWith(originUrl)) {
62
+ const token = extractOAuthToken(url);
63
+ if (token) {
64
+ authWindow.close();
65
+ clearInterval(interval);
66
+ onSuccess(token);
67
+ }
68
+ }
69
+ }
70
+ catch {
71
+ // Ignore cross-origin errors
72
+ }
73
+ }, 500);
74
+ }
75
+ export function generateOAuthNonce(publicKey) {
76
+ return sha256(publicKey).replace(/^0x/, '');
77
+ }
@@ -0,0 +1 @@
1
+ {"type": "module","sideEffects":false}
@@ -0,0 +1,169 @@
1
+ import { normalizeTimestamp } from '@zerodev/wallet-core';
2
+ import { Provider } from 'ox';
3
+ const SESSION_WARNING_THRESHOLD_MS = 60 * 1000; // 1 minute before expiry
4
+ export function createProvider({ store, config, }) {
5
+ const emitter = Provider.createEmitter();
6
+ let sessionRefreshTimer = null;
7
+ // Session auto-refresh logic
8
+ const scheduleSessionRefresh = () => {
9
+ if (config.autoRefreshSession === false)
10
+ return;
11
+ const state = store.getState();
12
+ if (!state.session || !state.wallet)
13
+ return;
14
+ const expiryMs = normalizeTimestamp(state.session.expiry);
15
+ const now = Date.now();
16
+ const timeUntilExpiry = expiryMs - now;
17
+ if (timeUntilExpiry <= 0) {
18
+ console.log('Session already expired');
19
+ return;
20
+ }
21
+ // Clear existing timer
22
+ if (sessionRefreshTimer) {
23
+ clearTimeout(sessionRefreshTimer);
24
+ sessionRefreshTimer = null;
25
+ }
26
+ const threshold = config.sessionWarningThreshold || SESSION_WARNING_THRESHOLD_MS;
27
+ const refreshAt = expiryMs - threshold;
28
+ const timeUntilRefresh = refreshAt - now;
29
+ if (timeUntilRefresh <= 0) {
30
+ console.log('Session expiring soon, refreshing immediately...');
31
+ refreshSessionNow();
32
+ }
33
+ else {
34
+ console.log(`Scheduling session refresh in ${timeUntilRefresh}ms`);
35
+ sessionRefreshTimer = setTimeout(() => {
36
+ refreshSessionNow();
37
+ }, timeUntilRefresh);
38
+ }
39
+ };
40
+ const refreshSessionNow = async () => {
41
+ const state = store.getState();
42
+ if (!state.wallet || !state.session)
43
+ return;
44
+ console.log('Auto-refreshing session...');
45
+ store.getState().setIsExpiring(true);
46
+ try {
47
+ const newSession = await state.wallet.refreshSession(state.session.id);
48
+ console.log('Session refreshed successfully');
49
+ store.getState().setSession(newSession || null);
50
+ store.getState().setIsExpiring(false);
51
+ if (newSession) {
52
+ scheduleSessionRefresh();
53
+ }
54
+ }
55
+ catch (err) {
56
+ console.error('Session refresh failed:', err);
57
+ store.getState().setIsExpiring(false);
58
+ store.getState().clear();
59
+ }
60
+ };
61
+ // Subscribe to session changes
62
+ const unsubscribe = store.subscribe((state) => state.session, () => {
63
+ scheduleSessionRefresh();
64
+ });
65
+ // Schedule initial refresh if session exists
66
+ const initialSession = store.getState().session;
67
+ if (initialSession) {
68
+ scheduleSessionRefresh();
69
+ }
70
+ return {
71
+ ...emitter,
72
+ destroy() {
73
+ // Cleanup timer and subscription
74
+ if (sessionRefreshTimer) {
75
+ clearTimeout(sessionRefreshTimer);
76
+ sessionRefreshTimer = null;
77
+ }
78
+ unsubscribe();
79
+ },
80
+ async request({ method, params }) {
81
+ const state = store.getState();
82
+ const activeChainId = state.chainIds[0];
83
+ switch (method) {
84
+ case 'eth_accounts': {
85
+ const account = state.kernelAccounts.get(activeChainId);
86
+ return account ? [account.address] : [];
87
+ }
88
+ case 'eth_requestAccounts': {
89
+ const account = state.kernelAccounts.get(activeChainId);
90
+ if (!account)
91
+ throw new Error('Not authenticated');
92
+ return [account.address];
93
+ }
94
+ case 'eth_chainId': {
95
+ return `0x${activeChainId.toString(16)}`;
96
+ }
97
+ case 'wallet_sendTransaction':
98
+ case 'eth_sendTransaction': {
99
+ if (!params || params.length === 0) {
100
+ throw new Error('Missing transaction parameters');
101
+ }
102
+ const [tx] = params;
103
+ const chainId = tx.chainId ? parseInt(tx.chainId, 16) : activeChainId;
104
+ // Get kernel client for this chain
105
+ const kernelClient = store.getState().kernelClients.get(chainId);
106
+ if (!kernelClient) {
107
+ throw new Error(`No kernel client for chain ${chainId}`);
108
+ }
109
+ // Send gasless transaction (always UserOp for EIP-7702)
110
+ const hash = await kernelClient.sendTransaction({
111
+ calls: [
112
+ {
113
+ to: tx.to,
114
+ value: tx.value ? BigInt(tx.value) : 0n,
115
+ data: tx.data || '0x',
116
+ },
117
+ ],
118
+ });
119
+ return hash;
120
+ }
121
+ case 'personal_sign': {
122
+ if (!params || params.length < 2) {
123
+ throw new Error('Missing sign parameters');
124
+ }
125
+ const [message] = params;
126
+ let account = state.kernelAccounts.get(activeChainId);
127
+ if (account &&
128
+ 'isDeployed' in account &&
129
+ !(await account.isDeployed())) {
130
+ account = state.eoaAccount;
131
+ }
132
+ if (!account)
133
+ throw new Error('Not authenticated');
134
+ return await account.signMessage({ message });
135
+ }
136
+ case 'eth_signTypedData_v4': {
137
+ if (!params || params.length < 2) {
138
+ throw new Error('Missing typed data parameters');
139
+ }
140
+ const [, typedDataJson] = params;
141
+ let account = state.kernelAccounts.get(activeChainId);
142
+ if (account &&
143
+ 'isDeployed' in account &&
144
+ !(await account.isDeployed())) {
145
+ account = state.eoaAccount;
146
+ }
147
+ if (!account)
148
+ throw new Error('Not authenticated');
149
+ const typedData = JSON.parse(typedDataJson);
150
+ return await account.signTypedData(typedData);
151
+ }
152
+ case 'wallet_switchEthereumChain': {
153
+ if (!params || params.length === 0) {
154
+ throw new Error('Missing chain parameter');
155
+ }
156
+ const [{ chainId }] = params;
157
+ const chainId_number = parseInt(chainId, 16);
158
+ // Update active chain
159
+ store.getState().setActiveChain(chainId_number);
160
+ // Emit chainChanged event
161
+ emitter.emit('chainChanged', chainId);
162
+ return null;
163
+ }
164
+ default:
165
+ throw new Error(`Method not supported: ${method}`);
166
+ }
167
+ },
168
+ };
169
+ }