@bounded-sh/client 0.0.25 → 0.0.27

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 (35) hide show
  1. package/REACT_NATIVE.md +144 -0
  2. package/README.md +24 -18
  3. package/dist/auth/index.d.ts +18 -21
  4. package/dist/auth/oidc-auth.d.ts +7 -0
  5. package/dist/auth/providers/email-auth-provider.d.ts +8 -19
  6. package/dist/auth/providers/privy-expo-provider.d.ts +1 -1
  7. package/dist/global.d.ts +2 -2
  8. package/dist/index.d.ts +0 -3
  9. package/dist/index.js +88 -4617
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +98 -4621
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/index.native.d.ts +2 -2
  14. package/dist/index.native.js +5184 -78
  15. package/dist/index.native.js.map +1 -1
  16. package/dist/index.native.mjs +5095 -4
  17. package/dist/index.native.mjs.map +1 -1
  18. package/dist/platform.d.ts +1 -1
  19. package/package.json +8 -4
  20. package/dist/index.native-CcpKp_mg.mjs +0 -5738
  21. package/dist/index.native-CcpKp_mg.mjs.map +0 -1
  22. package/dist/index.native-DIw0cB5C.js +0 -5798
  23. package/dist/index.native-DIw0cB5C.js.map +0 -1
  24. package/dist/phantom-wallet-provider-Cagl3roP.js +0 -1452
  25. package/dist/phantom-wallet-provider-Cagl3roP.js.map +0 -1
  26. package/dist/phantom-wallet-provider-CyMav6HM.mjs +0 -1431
  27. package/dist/phantom-wallet-provider-CyMav6HM.mjs.map +0 -1
  28. package/dist/privy-wallet-provider-Cb1GUmOi.js +0 -949
  29. package/dist/privy-wallet-provider-Cb1GUmOi.js.map +0 -1
  30. package/dist/privy-wallet-provider-YR9Eisgl.mjs +0 -926
  31. package/dist/privy-wallet-provider-YR9Eisgl.mjs.map +0 -1
  32. package/dist/solana-mobile-wallet-provider-BEpjk4Sz.js +0 -1492
  33. package/dist/solana-mobile-wallet-provider-BEpjk4Sz.js.map +0 -1
  34. package/dist/solana-mobile-wallet-provider-DOSBGM8C.mjs +0 -1471
  35. package/dist/solana-mobile-wallet-provider-DOSBGM8C.mjs.map +0 -1
@@ -1,926 +0,0 @@
1
- import { getActiveSessionManager, convertRemainingAccounts, buildSetDocumentsTransaction, genAuthNonce, genSolanaMessage, createSessionWithSignature } from '@bounded-sh/core';
2
- import { n as normalizeSolanaRpcUrl, r as resolveSolanaRpcUrl, f as resolveSolanaWalletStandardChain, b as bufferExports, h as bs58, c as confirmAndCheckTransaction, g as getPlatform, s as setCurrentUser, S as SURFNET_RPC_URL } from './index.native-CcpKp_mg.mjs';
3
- import { Connection, VersionedTransaction, PublicKey, Transaction } from '@solana/web3.js';
4
- import * as anchor from '@coral-xyz/anchor';
5
- import * as React from 'react';
6
- import * as ReactDOM from 'react-dom/client';
7
-
8
- // @privy-io/react-auth(/solana) are OPTIONAL peer deps. They are loaded lazily
9
- // via dynamic import() rather than a static top-level import: a static import
10
- // would (a) in the ESM bundle still be a bare `import` the consumer resolves —
11
- // fine — but (b) in the CJS bundle become an eager top-level require() that
12
- // throws for any consumer who hasn't installed Privy (this module is pulled
13
- // into the entry via a static re-export). The lazy import keeps the browser
14
- // ESM bundle free of any runtime `require('@privy-io...')` AND lets a consumer
15
- // who never uses Privy load the package without it installed. Populated once
16
- // in `initialize()` (browser-only) before the Privy React tree renders.
17
- let privyImports;
18
- let privySolana;
19
- async function loadPrivyModules() {
20
- if (privyImports && privySolana)
21
- return;
22
- [privyImports, privySolana] = await Promise.all([
23
- import('@privy-io/react-auth'),
24
- import('@privy-io/react-auth/solana'),
25
- ]);
26
- }
27
- // @solana/kit is an OPTIONAL wallet-only dep, used only to build the Privy
28
- // Solana RPC transports. Loaded lazily (like the Privy SDKs) via dynamic
29
- // import() rather than a static top-level import: a static import is hoisted by
30
- // rollup into the entry chunk, forcing every consumer (including vanilla,
31
- // non-wallet web apps) to have @solana/kit installed and eagerly require()d.
32
- // Deferring it here keeps it out of the entry bundle.
33
- let solanaKit;
34
- async function loadSolanaKit() {
35
- if (!solanaKit) {
36
- solanaKit = await import('@solana/kit');
37
- }
38
- return solanaKit;
39
- }
40
- function requirePrivyWalletConfig(privyConfig) {
41
- if (!privyConfig || typeof privyConfig.appId !== 'string' || privyConfig.appId.trim() === '') {
42
- throw new Error('Privy auth requires config.privyConfig.appId; no default Privy app is used.');
43
- }
44
- if (!privyConfig.config || typeof privyConfig.config !== 'object') {
45
- throw new Error('Privy auth requires config.privyConfig.config for the app-scoped Privy provider.');
46
- }
47
- return {
48
- appId: privyConfig.appId,
49
- config: privyConfig.config,
50
- };
51
- }
52
- class PrivyWalletProvider {
53
- constructor(appName, appLogoUrl, privyConfig, networkUrl = null, configuredNetwork = null) {
54
- this.containerElement = null;
55
- this.chainId = null;
56
- this.root = null;
57
- this.privyMethods = null;
58
- this.pendingLogin = null;
59
- // Resolved Solana RPC URLs (computed in the constructor; consumed in
60
- // initialize() where @solana/kit is lazily loaded to build the transports).
61
- this.configuredRpcUrl = null;
62
- // This is used to store the pending transaction until the wallet is connected for workaround for the wallet not being connected after login completion
63
- this.pendingTransaction = null;
64
- // This is used to store pending signTransaction calls until the wallet is connected
65
- this.pendingSignTransaction = null;
66
- // This is used to store pending signMessage calls until the wallet is connected
67
- this.pendingSignMessage = null;
68
- // This is used to store pending signAndSubmitTransaction calls until the wallet is connected
69
- this.pendingSignAndSubmitTransaction = null;
70
- this.networkUrl = normalizeSolanaRpcUrl(networkUrl);
71
- this.configuredNetwork = configuredNetwork;
72
- if (typeof window === 'undefined') {
73
- throw new Error('PrivyWalletProvider can only be instantiated in a browser environment');
74
- }
75
- const requiredPrivyConfig = requirePrivyWalletConfig(privyConfig);
76
- if (PrivyWalletProvider.instance) {
77
- return PrivyWalletProvider.instance;
78
- }
79
- this.containerElement = null;
80
- this.root = null;
81
- this.privyMethods = null;
82
- this.privyConfig = requiredPrivyConfig;
83
- if (!this.privyConfig.config.appearance) {
84
- this.privyConfig.config.appearance = {};
85
- }
86
- if (appName != null && appName != '' && !this.privyConfig.config.appearance.landingHeader) {
87
- this.privyConfig.config.appearance.landingHeader = appName;
88
- }
89
- if (appLogoUrl != null && appLogoUrl != '' && !this.privyConfig.config.appearance.logo) {
90
- this.privyConfig.config.appearance.logo = appLogoUrl;
91
- }
92
- // (externalWallets connectors are configured in initialize() once the
93
- // Privy SDK is lazily loaded — see loadPrivyModules.)
94
- // Resolve Solana RPC URLs for v3. The @solana/kit transports are built
95
- // lazily in initialize() so a non-Privy consumer never needs @solana/kit.
96
- if (this.networkUrl || configuredNetwork) {
97
- this.configuredRpcUrl = resolveSolanaRpcUrl(this.networkUrl, configuredNetwork, "Privy Solana RPC");
98
- }
99
- if (configuredNetwork) {
100
- this.chainId = resolveSolanaWalletStandardChain(configuredNetwork, "Privy Solana transaction");
101
- }
102
- const existingProviders = document.querySelectorAll('[data-privy-provider]');
103
- existingProviders.forEach(el => el.remove());
104
- // Fire-and-forget: initialize() lazily loads the Privy SDK then renders
105
- // the hidden React tree. privyMethods stays null until that render runs,
106
- // which the login/connect methods already wait for. Surface a clear error
107
- // if the optional @privy-io/react-auth peer dep isn't installed.
108
- this.initialize().catch((err) => {
109
- console.error('PrivyWalletProvider: failed to load @privy-io/react-auth — ensure it is installed to use Privy login.', err);
110
- });
111
- // Assign this instance to the static instance property
112
- PrivyWalletProvider.instance = this;
113
- }
114
- // Optional: Provide a static method to get the instance
115
- static getInstance(appName, appLogoUrl, privyConfig, networkUrl = null, configuredNetwork = null) {
116
- if (!PrivyWalletProvider.instance) {
117
- new PrivyWalletProvider(appName, appLogoUrl, privyConfig, networkUrl, configuredNetwork);
118
- }
119
- return PrivyWalletProvider.instance;
120
- }
121
- async initialize() {
122
- // Lazily load the Privy SDK (browser-only) before building the React tree.
123
- await loadPrivyModules();
124
- // Build the Solana RPC transports now that we're on the async path, so
125
- // @solana/kit can be lazily imported (kept out of the entry bundle).
126
- const { createSolanaRpc, createSolanaRpcSubscriptions } = await loadSolanaKit();
127
- if (this.configuredRpcUrl && this.chainId && !this.privyConfig.config.solana) {
128
- this.privyConfig.config.solana = {};
129
- }
130
- if (this.configuredRpcUrl && this.chainId) {
131
- this.privyConfig.config.solana.rpcs = {
132
- [this.chainId]: {
133
- rpc: createSolanaRpc(this.configuredRpcUrl),
134
- rpcSubscriptions: createSolanaRpcSubscriptions(this.configuredRpcUrl.replace('https://', 'wss://'))
135
- }
136
- };
137
- }
138
- // Ensure externalWallets connectors are always configured — required for
139
- // useWallets().ready to resolve even when no browser extension is present.
140
- if (!this.privyConfig.config.externalWallets && privySolana) {
141
- this.privyConfig.config.externalWallets = {
142
- solana: { connectors: privySolana.toSolanaWalletConnectors() }
143
- };
144
- }
145
- if (!this.containerElement) {
146
- this.containerElement = document.createElement('div');
147
- this.containerElement.setAttribute('data-privy-provider', 'true');
148
- document.body.appendChild(this.containerElement);
149
- const that = this;
150
- const PrivyComponent = () => {
151
- const privy = privyImports.usePrivy();
152
- const useOAuthTokens = privyImports.useOAuthTokens();
153
- const useFundWalletSolana = privySolana.useFundWallet();
154
- const useSendTransactionSolana = privySolana.useSignAndSendTransaction();
155
- const { signTransaction } = privySolana.useSignTransaction();
156
- const { signMessage: signMessageHook } = privySolana.useSignMessage();
157
- const { ready: walletReady, wallets } = privySolana.useWallets();
158
- const { createWallet } = privySolana.useCreateWallet();
159
- const { exportWallet } = privySolana.useExportWallet();
160
- const useSolanaWallets = { createWallet, exportWallet, ready: walletReady, wallets };
161
- const useIdentityToken = privyImports.useIdentityToken();
162
- const { connectWallet } = privyImports.useConnectWallet({
163
- onSuccess: async ({ wallet }) => {
164
- await that.createSession(wallet.address, await privy.getAccessToken(), useIdentityToken.identityToken);
165
- // If there's a pending transaction, execute it now
166
- if (that.pendingTransaction) {
167
- const { evmTransactionData, solTransactionData, options, resolve, reject } = that.pendingTransaction;
168
- try {
169
- const result = await that.runTransaction(evmTransactionData, solTransactionData, options);
170
- resolve(result);
171
- }
172
- catch (error) {
173
- reject(error);
174
- }
175
- that.pendingTransaction = null;
176
- }
177
- // If there's a pending signTransaction, execute it now
178
- if (that.pendingSignTransaction) {
179
- const { transaction, resolve, reject } = that.pendingSignTransaction;
180
- try {
181
- const result = await that.signTransaction(transaction);
182
- resolve(result);
183
- }
184
- catch (error) {
185
- reject(error);
186
- }
187
- that.pendingSignTransaction = null;
188
- }
189
- // If there's a pending signMessage, execute it now
190
- if (that.pendingSignMessage) {
191
- const { message, resolve, reject } = that.pendingSignMessage;
192
- try {
193
- const result = await that.signMessage(message);
194
- resolve(result);
195
- }
196
- catch (error) {
197
- reject(error);
198
- }
199
- that.pendingSignMessage = null;
200
- }
201
- // If there's a pending signAndSubmitTransaction, execute it now
202
- if (that.pendingSignAndSubmitTransaction) {
203
- const { transaction, feePayer, resolve, reject } = that.pendingSignAndSubmitTransaction;
204
- try {
205
- const result = await that.signAndSubmitTransaction(transaction, feePayer);
206
- resolve(result);
207
- }
208
- catch (error) {
209
- reject(error);
210
- }
211
- that.pendingSignAndSubmitTransaction = null;
212
- }
213
- },
214
- onError: (error) => {
215
- // Only log errors that aren't user-initiated cancellations
216
- if (error !== 'exited_auth_flow' && (error === null || error === void 0 ? void 0 : error.message) !== 'exited_auth_flow') {
217
- console.error('Wallet connection error:', error);
218
- }
219
- if (that.pendingTransaction) {
220
- that.pendingTransaction.reject(error);
221
- that.pendingTransaction = null;
222
- }
223
- if (that.pendingSignTransaction) {
224
- that.pendingSignTransaction.reject(error);
225
- that.pendingSignTransaction = null;
226
- }
227
- if (that.pendingSignMessage) {
228
- that.pendingSignMessage.reject(error);
229
- that.pendingSignMessage = null;
230
- }
231
- if (that.pendingSignAndSubmitTransaction) {
232
- that.pendingSignAndSubmitTransaction.reject(error);
233
- that.pendingSignAndSubmitTransaction = null;
234
- }
235
- }
236
- });
237
- const { login } = privyImports.useLogin({
238
- onComplete: async ({ user }) => {
239
- var _a;
240
- // This is a hack to indicate to the app that privy login is not complete
241
- // @ts-ignore
242
- window.runningPrivyOnComplete = true;
243
- if (!user) {
244
- getActiveSessionManager().clearSession();
245
- return;
246
- }
247
- const solanaLinkedAccounts = user.linkedAccounts.filter((account) => account.chainType === 'solana');
248
- const sortedLinkedAccounts = solanaLinkedAccounts.sort((a, b) => new Date(b.latestVerifiedAt).getTime() - new Date(a.latestVerifiedAt).getTime());
249
- let finalAddress = (_a = sortedLinkedAccounts[0]) === null || _a === void 0 ? void 0 : _a.address;
250
- if (!finalAddress) {
251
- const solanaWallet = await createWallet();
252
- finalAddress = solanaWallet.address;
253
- getActiveSessionManager().clearSession();
254
- return;
255
- }
256
- const session = await getActiveSessionManager().getSession();
257
- if (!session || finalAddress !== session.address) {
258
- try {
259
- const accessToken = await privy.getAccessToken();
260
- const idToken = useIdentityToken.identityToken;
261
- await that.createSession(finalAddress, accessToken, idToken);
262
- }
263
- catch (error) {
264
- console.error('Error creating session:', error);
265
- }
266
- }
267
- // @ts-ignore
268
- window.runningPrivyOnComplete = false;
269
- setCurrentUser({ address: finalAddress, provider: that });
270
- if (that.pendingLogin) {
271
- that.pendingLogin.resolve({ address: finalAddress, provider: that });
272
- that.pendingLogin = null;
273
- }
274
- },
275
- onError: (error) => {
276
- // Only log errors that aren't user-initiated cancellations
277
- if (error !== 'exited_auth_flow' && (error === null || error === void 0 ? void 0 : error.message) !== 'exited_auth_flow') {
278
- console.error('Login error:', error);
279
- }
280
- if (that.pendingLogin) {
281
- that.pendingLogin.reject(error);
282
- that.pendingLogin = null;
283
- }
284
- }
285
- });
286
- const { logout } = privyImports.useLogout({
287
- onSuccess: () => {
288
- getActiveSessionManager().clearSession();
289
- setCurrentUser(null);
290
- }
291
- });
292
- React.useEffect(() => {
293
- if (privy.ready) {
294
- that.privyMethods = {
295
- ready: privy.ready,
296
- walletReady: walletReady,
297
- authenticated: privy.authenticated,
298
- wallets,
299
- login,
300
- logout,
301
- connectWallet,
302
- getAccessToken: privy.getAccessToken,
303
- identityToken: useIdentityToken.identityToken,
304
- usePrivy: privy,
305
- useOAuthTokens: useOAuthTokens,
306
- useFundWalletSolana: useFundWalletSolana,
307
- useSendTransactionSolana: useSendTransactionSolana,
308
- useSolanaWallets: useSolanaWallets,
309
- signTransaction: signTransaction,
310
- signMessage: signMessageHook,
311
- };
312
- }
313
- }, [privy.ready, privy.authenticated, wallets, login, logout, walletReady]);
314
- return null;
315
- };
316
- const FinalPrivyComponent = () => {
317
- if (typeof window === 'undefined') {
318
- return null;
319
- }
320
- return React.createElement(privyImports.PrivyProvider, {
321
- appId: this.privyConfig.appId,
322
- config: this.privyConfig.config,
323
- children: React.createElement(PrivyComponent)
324
- });
325
- };
326
- this.root = ReactDOM.createRoot(this.containerElement);
327
- this.root.render(React.createElement(FinalPrivyComponent));
328
- }
329
- }
330
- async login() {
331
- var _a, _b;
332
- await this.ensureReady();
333
- // Check if we have a valid session that matches an authenticated wallet
334
- const session = await getActiveSessionManager().getSession();
335
- if (((_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.authenticated) && ((_b = this.privyMethods.wallets) === null || _b === void 0 ? void 0 : _b.length) > 0 && session) {
336
- // Find the wallet matching the session
337
- const matchingWallet = this.privyMethods.wallets.find((wallet) => wallet.address === session.address);
338
- if (matchingWallet) {
339
- // Valid session with matching wallet, return immediately
340
- setCurrentUser({ address: matchingWallet.address, provider: this });
341
- return {
342
- address: matchingWallet.address,
343
- provider: this
344
- };
345
- }
346
- // Session exists but doesn't match any connected wallet
347
- // This can happen after wallet switching - force fresh login
348
- console.log('Session wallet mismatch detected, forcing fresh login flow');
349
- }
350
- // If no valid session or wallet mismatch, force a fresh login flow
351
- // This ensures the user can select the correct wallet, especially after logout
352
- if (this.pendingLogin) {
353
- return new Promise((resolve, reject) => {
354
- this.pendingLogin.resolve = resolve;
355
- this.pendingLogin.reject = reject;
356
- });
357
- }
358
- return new Promise((resolve, reject) => {
359
- this.pendingLogin = { resolve, reject };
360
- // open Privy modal to let user select/verify wallet
361
- this.privyMethods.login();
362
- // safety timeout
363
- setTimeout(() => {
364
- if (this.pendingLogin) {
365
- this.pendingLogin.reject(new Error("Login timed out"));
366
- this.pendingLogin = null;
367
- }
368
- }, 180000);
369
- });
370
- }
371
- async getNativeMethods() {
372
- var _a;
373
- await this.ensureReady();
374
- return (_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.usePrivy;
375
- }
376
- async logout() {
377
- await this.ensureReady();
378
- await this.privyMethods.logout();
379
- }
380
- async runTransaction(_evmTransactionData, solTransactionData, options) {
381
- var _a, _b, _c;
382
- await this.ensureReady({ waitForWallets: true });
383
- let session = await getActiveSessionManager().getSession();
384
- let sessionAddress = session === null || session === void 0 ? void 0 : session.address;
385
- let privyWallets = (_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.wallets;
386
- let privyWallet = privyWallets === null || privyWallets === void 0 ? void 0 : privyWallets.find((wallet) => wallet.address === sessionAddress);
387
- // If wallet not found yet, it may be an embedded wallet (email login) that hasn't
388
- // appeared in the wallets array due to React state timing. Wait for it before
389
- // falling back to connectWallet() which shows an unwanted wallet selection modal.
390
- if (!privyWallets || privyWallets.length === 0 || !privyWallet) {
391
- if (((_b = this.privyMethods) === null || _b === void 0 ? void 0 : _b.authenticated) && sessionAddress) {
392
- privyWallet = await this.waitForMatchingWallet(sessionAddress);
393
- }
394
- }
395
- if (!privyWallet) {
396
- // If there's already a pending transaction, throw an error to prevent overlapping calls
397
- if (this.pendingTransaction) {
398
- throw new Error("Oops... something went wrong. Please try again.");
399
- }
400
- // Create a promise to wait for the transaction result
401
- return new Promise((resolve, reject) => {
402
- this.pendingTransaction = {
403
- resolve,
404
- reject,
405
- evmTransactionData: _evmTransactionData,
406
- solTransactionData,
407
- options
408
- };
409
- this.privyMethods.connectWallet();
410
- // Set a timeout to reject the promise if connection takes too long
411
- setTimeout(() => {
412
- if (this.pendingTransaction) {
413
- this.pendingTransaction.reject(new Error("Wallet connection timed out"));
414
- this.pendingTransaction = null;
415
- }
416
- }, 30000); // 30 seconds timeout
417
- });
418
- }
419
- // Proceed with the transaction using privyWallet
420
- const wallet = privyWallet;
421
- if (solTransactionData == null) {
422
- throw new Error("Solana transaction data is required");
423
- }
424
- const rpcUrl = this.getRpcUrl(solTransactionData.network);
425
- const connection = new Connection(rpcUrl, "confirmed");
426
- const remainingAccounts = convertRemainingAccounts(solTransactionData.txArgs[0].remainingAccounts);
427
- const app_id = solTransactionData.appId;
428
- if (!app_id) {
429
- throw new Error("App ID is required");
430
- }
431
- const anchorProvider = new anchor.AnchorProvider(connection, wallet.provider, anchor.AnchorProvider.defaultOptions());
432
- const finalDeduped = [];
433
- for (const acc of remainingAccounts) {
434
- const existing = finalDeduped.find((d) => d.pubkey.equals(acc.pubkey));
435
- if (existing) {
436
- existing.isSigner = existing.isSigner || acc.isSigner;
437
- existing.isWritable = existing.isWritable || acc.isWritable;
438
- }
439
- else {
440
- finalDeduped.push(acc);
441
- }
442
- }
443
- let tx;
444
- let blockhash;
445
- let lastValidBlockHeight;
446
- if (solTransactionData.signedTransaction) {
447
- // Server already set the blockhash and co-signed — do NOT overwrite
448
- // the blockhash or it will invalidate the server's signature.
449
- tx = VersionedTransaction.deserialize(bufferExports.Buffer.from(solTransactionData.signedTransaction, 'base64'));
450
- // Extract the blockhash that was baked into the signed message
451
- blockhash = tx.message.recentBlockhash;
452
- const latestBlockhash = await connection.getLatestBlockhash('confirmed');
453
- lastValidBlockHeight = latestBlockhash.lastValidBlockHeight;
454
- }
455
- else {
456
- const result = await buildSetDocumentsTransaction(connection, solTransactionData.txArgs[0].idl, anchorProvider, new PublicKey(wallet.address), { app_id, documents: solTransactionData.txArgs[0].setDocumentData, delete_paths: solTransactionData.txArgs[0].deletePaths, txData: solTransactionData.txArgs[0].txData }, finalDeduped, solTransactionData.lutKey, solTransactionData.preInstructions, false, solTransactionData.additionalLutAddresses);
457
- tx = result.tx;
458
- blockhash = result.blockhash;
459
- lastValidBlockHeight = result.lastValidBlockHeight;
460
- }
461
- // Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
462
- const isLegacyTx = 'recentBlockhash' in tx && !('message' in tx && 'staticAccountKeys' in tx.message);
463
- if (isLegacyTx) {
464
- tx.recentBlockhash = blockhash;
465
- tx.lastValidBlockHeight = lastValidBlockHeight;
466
- }
467
- else {
468
- tx.message.recentBlockhash = blockhash;
469
- }
470
- try {
471
- if ((options === null || options === void 0 ? void 0 : options.shouldSubmitTx) === false) {
472
- // Just sign, don't submit - return raw signed bytes (same as original runTransaction)
473
- const signedTx = await this.signTransactionRaw(tx, privyWallet, solTransactionData.network);
474
- return {
475
- signedTransaction: signedTx, // Raw Uint8Array from Privy
476
- blockNumber: 0,
477
- gasUsed: "0",
478
- data: ""
479
- };
480
- }
481
- // Sign and submit using shared helper
482
- const { signature, txInfo } = await this.signAndSubmitInternal(tx, privyWallet, rpcUrl, connection, solTransactionData.network);
483
- return {
484
- transactionSignature: signature,
485
- blockNumber: (txInfo === null || txInfo === void 0 ? void 0 : txInfo.slot) || 0,
486
- gasUsed: ((_c = txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta) === null || _c === void 0 ? void 0 : _c.fee.toString()) || '0',
487
- data: txInfo === null || txInfo === void 0 ? void 0 : txInfo.meta,
488
- };
489
- }
490
- catch (err) {
491
- console.error(err);
492
- throw err;
493
- }
494
- }
495
- /**
496
- * Signs a Solana transaction without submitting it.
497
- *
498
- * This method handles blockhash automatically if not set - you do NOT need to
499
- * set recentBlockhash on the transaction before calling this method.
500
- * The network/RPC URL is derived from the provider's configuration (set during initialization).
501
- *
502
- * @param transaction - The transaction to sign (Transaction or VersionedTransaction)
503
- * @returns The signed transaction
504
- */
505
- async signTransaction(transaction) {
506
- var _a, _b;
507
- await this.ensureReady({ waitForWallets: true });
508
- let privyWallets = (_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.wallets;
509
- let session = await getActiveSessionManager().getSession();
510
- let sessionAddress = session === null || session === void 0 ? void 0 : session.address;
511
- let privyWallet = privyWallets === null || privyWallets === void 0 ? void 0 : privyWallets.find((wallet) => wallet.address === sessionAddress);
512
- // If there's already a pending sign transaction, throw an error to prevent overlapping calls
513
- if (this.pendingSignTransaction) {
514
- throw new Error("Oops... something went wrong. Please try again.");
515
- }
516
- // If wallet not found yet, it may be an embedded wallet (email login) that hasn't
517
- // appeared in the wallets array due to React state timing. Wait for it before
518
- // falling back to connectWallet() which shows an unwanted wallet selection modal.
519
- if (!privyWallets || privyWallets.length === 0 || !privyWallet) {
520
- if (((_b = this.privyMethods) === null || _b === void 0 ? void 0 : _b.authenticated) && sessionAddress) {
521
- privyWallet = await this.waitForMatchingWallet(sessionAddress);
522
- }
523
- }
524
- // If still no wallet after waiting, trigger connection and queue the signing
525
- if (!privyWallet) {
526
- return new Promise((resolve, reject) => {
527
- this.pendingSignTransaction = {
528
- resolve,
529
- reject,
530
- transaction
531
- };
532
- this.privyMethods.connectWallet();
533
- // Set a timeout to reject the promise if connection takes too long
534
- setTimeout(() => {
535
- if (this.pendingSignTransaction) {
536
- this.pendingSignTransaction.reject(new Error("Wallet connection timed out"));
537
- this.pendingSignTransaction = null;
538
- }
539
- }, 30000); // 30 seconds timeout
540
- });
541
- }
542
- // Privy requires serialized transaction bytes, which requires a blockhash.
543
- // If the transaction doesn't have one, get a fresh blockhash and set it.
544
- // Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
545
- const isLegacyTransaction = 'recentBlockhash' in transaction && !('message' in transaction && 'staticAccountKeys' in transaction.message);
546
- if (isLegacyTransaction) {
547
- const legacyTx = transaction;
548
- if (!legacyTx.recentBlockhash) {
549
- const rpcUrl = this.getRpcUrl();
550
- const connection = new Connection(rpcUrl, 'confirmed');
551
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
552
- legacyTx.recentBlockhash = blockhash;
553
- legacyTx.lastValidBlockHeight = lastValidBlockHeight;
554
- }
555
- // Set feePayer if not already set
556
- if (!legacyTx.feePayer) {
557
- legacyTx.feePayer = new PublicKey(privyWallet.address);
558
- }
559
- }
560
- else {
561
- // VersionedTransaction
562
- const versionedTx = transaction;
563
- if (!versionedTx.message.recentBlockhash) {
564
- const rpcUrl = this.getRpcUrl();
565
- const connection = new Connection(rpcUrl, 'confirmed');
566
- const { blockhash } = await connection.getLatestBlockhash('confirmed');
567
- versionedTx.message.recentBlockhash = blockhash;
568
- }
569
- }
570
- // Sign using raw method, then deserialize for the public API
571
- const signedTxRaw = await this.signTransactionRaw(transaction, privyWallet);
572
- return this.deserializeSignedTransaction(signedTxRaw);
573
- }
574
- /**
575
- * Signs and submits a Solana transaction to the network.
576
- *
577
- * This method handles blockhash and transaction confirmation automatically - you do NOT need to
578
- * set recentBlockhash or lastValidBlockHeight on the transaction before calling this method.
579
- * The network/RPC URL is derived from the provider's configuration (set during initialization).
580
- *
581
- * @param transaction - The transaction to sign and submit (Transaction or VersionedTransaction)
582
- * @param feePayer - Optional fee payer public key. If not provided and the transaction doesn't
583
- * already have a feePayer set, the connected wallet address will be used.
584
- * Useful for co-signing scenarios where a different account pays the fees.
585
- * @returns The transaction signature
586
- */
587
- async signAndSubmitTransaction(transaction, feePayer) {
588
- var _a, _b;
589
- await this.ensureReady({ waitForWallets: true });
590
- let privyWallets = (_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.wallets;
591
- let session = await getActiveSessionManager().getSession();
592
- let sessionAddress = session === null || session === void 0 ? void 0 : session.address;
593
- let privyWallet = privyWallets === null || privyWallets === void 0 ? void 0 : privyWallets.find((wallet) => wallet.address === sessionAddress);
594
- // If there's already a pending signAndSubmitTransaction, throw an error to prevent overlapping calls
595
- if (this.pendingSignAndSubmitTransaction) {
596
- throw new Error("Oops... something went wrong. Please try again.");
597
- }
598
- // If wallet not found yet, it may be an embedded wallet (email login) that hasn't
599
- // appeared in the wallets array due to React state timing. Wait for it before
600
- // falling back to connectWallet() which shows an unwanted wallet selection modal.
601
- if (!privyWallets || privyWallets.length === 0 || !privyWallet) {
602
- if (((_b = this.privyMethods) === null || _b === void 0 ? void 0 : _b.authenticated) && sessionAddress) {
603
- privyWallet = await this.waitForMatchingWallet(sessionAddress);
604
- }
605
- }
606
- // If still no wallet after waiting, trigger connection and queue the signing
607
- if (!privyWallet) {
608
- return new Promise((resolve, reject) => {
609
- this.pendingSignAndSubmitTransaction = {
610
- resolve,
611
- reject,
612
- transaction,
613
- feePayer
614
- };
615
- this.privyMethods.connectWallet();
616
- // Set a timeout to reject the promise if connection takes too long
617
- setTimeout(() => {
618
- if (this.pendingSignAndSubmitTransaction) {
619
- this.pendingSignAndSubmitTransaction.reject(new Error("Wallet connection timed out"));
620
- this.pendingSignAndSubmitTransaction = null;
621
- }
622
- }, 30000); // 30 seconds timeout
623
- });
624
- }
625
- // Get RPC URL and create connection
626
- const rpcUrl = this.getRpcUrl();
627
- const connection = new Connection(rpcUrl, 'confirmed');
628
- // Get fresh blockhash and set it on the transaction before signing
629
- const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
630
- // Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
631
- const isLegacyTransaction = 'recentBlockhash' in transaction && !('message' in transaction && 'staticAccountKeys' in transaction.message);
632
- if (isLegacyTransaction) {
633
- const legacyTx = transaction;
634
- legacyTx.recentBlockhash = blockhash;
635
- legacyTx.lastValidBlockHeight = lastValidBlockHeight;
636
- // Set feePayer if not already set
637
- if (!legacyTx.feePayer) {
638
- legacyTx.feePayer = feePayer !== null && feePayer !== void 0 ? feePayer : new PublicKey(privyWallet.address);
639
- }
640
- }
641
- else {
642
- // VersionedTransaction
643
- const versionedTx = transaction;
644
- versionedTx.message.recentBlockhash = blockhash;
645
- // Note: VersionedTransaction feePayer is set in the message at creation time
646
- // and cannot be modified after creation
647
- }
648
- // Use shared sign and submit logic
649
- const { signature } = await this.signAndSubmitInternal(transaction, privyWallet, rpcUrl, connection);
650
- return signature;
651
- }
652
- // ============ Private Helpers ============
653
- getRpcUrl(network) {
654
- return resolveSolanaRpcUrl(this.networkUrl, network !== null && network !== void 0 ? network : this.configuredNetwork, "Privy Solana transaction");
655
- }
656
- getChainId(network) {
657
- if (network) {
658
- return resolveSolanaWalletStandardChain(network, "Privy Solana transaction");
659
- }
660
- if (this.chainId)
661
- return this.chainId;
662
- if (this.configuredNetwork) {
663
- return resolveSolanaWalletStandardChain(this.configuredNetwork, "Privy Solana transaction");
664
- }
665
- throw new Error("Privy Solana transaction requires config.chain as solana_devnet, solana_mainnet, or surfnet.");
666
- }
667
- /**
668
- * Internal sign transaction - serializes and signs via Privy
669
- * Returns the raw result from Privy (Uint8Array)
670
- * This mirrors exactly what runTransaction was doing
671
- */
672
- async signTransactionRaw(transaction, privyWallet, network) {
673
- var _a;
674
- // Serialize the transaction
675
- // Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
676
- const isLegacyTransaction = 'recentBlockhash' in transaction && !('message' in transaction && 'staticAccountKeys' in transaction.message);
677
- let serializedForSign;
678
- if (isLegacyTransaction) {
679
- serializedForSign = transaction.serialize({ requireAllSignatures: false, verifySignatures: false });
680
- }
681
- else {
682
- serializedForSign = transaction.serialize();
683
- }
684
- // Sign via Privy - returns { signedTransaction: Uint8Array }
685
- const result = await ((_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.signTransaction({
686
- transaction: serializedForSign,
687
- wallet: privyWallet,
688
- chain: this.getChainId(network)
689
- }));
690
- // Privy returns { signedTransaction: Uint8Array }, unwrap it
691
- const signedTx = (result === null || result === void 0 ? void 0 : result.signedTransaction) || result;
692
- return signedTx;
693
- }
694
- /**
695
- * Deserialize a signed transaction from Uint8Array to Transaction object
696
- */
697
- deserializeSignedTransaction(signedTxRaw) {
698
- // Handle different formats Privy might return
699
- let signedTx;
700
- if (signedTxRaw instanceof Uint8Array) {
701
- signedTx = signedTxRaw;
702
- }
703
- else if (Array.isArray(signedTxRaw)) {
704
- signedTx = new Uint8Array(signedTxRaw);
705
- }
706
- else if (signedTxRaw && typeof signedTxRaw === 'object' && 'signedTransaction' in signedTxRaw) {
707
- // Privy might wrap the result
708
- signedTx = signedTxRaw.signedTransaction instanceof Uint8Array
709
- ? signedTxRaw.signedTransaction
710
- : new Uint8Array(signedTxRaw.signedTransaction);
711
- }
712
- else if (bufferExports.Buffer.isBuffer(signedTxRaw)) {
713
- signedTx = new Uint8Array(signedTxRaw);
714
- }
715
- else {
716
- throw new Error(`Unexpected signed transaction format: ${typeof signedTxRaw}`);
717
- }
718
- // Try to deserialize as VersionedTransaction first, fall back to legacy Transaction
719
- try {
720
- return VersionedTransaction.deserialize(signedTx);
721
- }
722
- catch (_a) {
723
- return Transaction.from(signedTx);
724
- }
725
- }
726
- /**
727
- * Internal sign and submit - handles Surfnet vs non-Surfnet logic
728
- * This is the core submission logic used by both signAndSubmitTransaction and runTransaction
729
- *
730
- * For Surfnet: sign with signTransactionRaw, then sendRawTransaction
731
- * For non-Surfnet: use Privy's signAndSendTransaction (combined operation)
732
- */
733
- async signAndSubmitInternal(transaction, privyWallet, rpcUrl, connection, network) {
734
- var _a;
735
- const isSurfnet = rpcUrl === SURFNET_RPC_URL;
736
- let signature;
737
- if (isSurfnet) {
738
- // For Surfnet: sign with signTransactionRaw, then sendRawTransaction
739
- const signedTx = await this.signTransactionRaw(transaction, privyWallet, network);
740
- signature = await connection.sendRawTransaction(signedTx);
741
- }
742
- else {
743
- // For non-Surfnet: use Privy's combined signAndSendTransaction
744
- // Serialize the transaction for Privy API
745
- // Use duck typing instead of instanceof to handle multiple @solana/web3.js versions
746
- const isLegacyTransaction = 'recentBlockhash' in transaction && !('message' in transaction && 'staticAccountKeys' in transaction.message);
747
- let serializedTx;
748
- if (isLegacyTransaction) {
749
- serializedTx = transaction.serialize({ requireAllSignatures: false, verifySignatures: false });
750
- }
751
- else {
752
- serializedTx = transaction.serialize();
753
- }
754
- const result = await ((_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.useSendTransactionSolana.signAndSendTransaction({
755
- transaction: serializedTx,
756
- wallet: privyWallet,
757
- chain: this.getChainId(network)
758
- }));
759
- // Handle case where signature might be bytes instead of string
760
- const rawSig = result.signature;
761
- if (rawSig instanceof Uint8Array || Array.isArray(rawSig)) {
762
- signature = bs58.encode(rawSig instanceof Uint8Array ? rawSig : new Uint8Array(rawSig));
763
- }
764
- else {
765
- signature = rawSig;
766
- }
767
- }
768
- const txInfo = await confirmAndCheckTransaction(connection, signature);
769
- return { signature, txInfo };
770
- }
771
- async signMessage(message) {
772
- var _a, _b, _c;
773
- await this.ensureReady({ waitForWallets: true });
774
- const session = await getActiveSessionManager().getSession();
775
- let sessionAddress = session === null || session === void 0 ? void 0 : session.address;
776
- // If there's already a pending sign message, throw an error to prevent overlapping calls
777
- if (this.pendingSignMessage) {
778
- throw new Error("Oops... something went wrong. Please try again.");
779
- }
780
- // If no wallets connected yet, it may be an embedded wallet (email login) that hasn't
781
- // appeared in the wallets array due to React state timing. Wait for it before
782
- // falling back to connectWallet() which shows an unwanted wallet selection modal.
783
- if (!((_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.wallets) || this.privyMethods.wallets.length === 0) {
784
- if (((_b = this.privyMethods) === null || _b === void 0 ? void 0 : _b.authenticated) && sessionAddress) {
785
- const matchedWallet = await this.waitForMatchingWallet(sessionAddress);
786
- if (!matchedWallet) {
787
- return new Promise((resolve, reject) => {
788
- this.pendingSignMessage = {
789
- resolve,
790
- reject,
791
- message
792
- };
793
- this.privyMethods.connectWallet();
794
- // Set a timeout to reject the promise if connection takes too long
795
- setTimeout(() => {
796
- if (this.pendingSignMessage) {
797
- this.pendingSignMessage.reject(new Error("Wallet connection timed out"));
798
- this.pendingSignMessage = null;
799
- }
800
- }, 30000); // 30 seconds timeout
801
- });
802
- }
803
- }
804
- else {
805
- return new Promise((resolve, reject) => {
806
- this.pendingSignMessage = {
807
- resolve,
808
- reject,
809
- message
810
- };
811
- this.privyMethods.connectWallet();
812
- // Set a timeout to reject the promise if connection takes too long
813
- setTimeout(() => {
814
- if (this.pendingSignMessage) {
815
- this.pendingSignMessage.reject(new Error("Wallet connection timed out"));
816
- this.pendingSignMessage = null;
817
- }
818
- }, 30000); // 30 seconds timeout
819
- });
820
- }
821
- }
822
- let privyWallet = this.privyMethods.wallets.find((wallet) => wallet.address === sessionAddress);
823
- if (!privyWallet) {
824
- throw new Error('Active Privy wallets do not include the authenticated session wallet; please log in again.');
825
- }
826
- // Use the Privy signMessage hook for Solana wallets
827
- // The message needs to be passed as a Uint8Array
828
- const messageBytes = getPlatform().textEncode(message);
829
- const result = await ((_c = this.privyMethods) === null || _c === void 0 ? void 0 : _c.signMessage({
830
- message: messageBytes,
831
- wallet: privyWallet,
832
- }));
833
- // The signature is returned as a Uint8Array, encode it as base58 (standard Solana format)
834
- const signatureBytes = (result === null || result === void 0 ? void 0 : result.signature) || result;
835
- const bs58 = await import('./index.native-CcpKp_mg.mjs').then(function (n) { return n.J; });
836
- return bs58.default.encode(signatureBytes);
837
- }
838
- async restoreSession() {
839
- const session = await getActiveSessionManager().getSession();
840
- if (session) {
841
- return { provider: this, address: session.address };
842
- }
843
- else {
844
- return null;
845
- }
846
- }
847
- // NOTE: accessToken/idToken are accepted for backwards-compat with existing
848
- // call sites but are no longer used. The session is minted by having the Privy
849
- // Solana wallet SIGN the canonical SIWS challenge (the same wallet-signature
850
- // path Phantom/MWA/guest use), so the bounded /session issuer needs no
851
- // Privy-specific token verification — it arrives as a Solana signature and is
852
- // recorded server-side as authMethod 'phantom'.
853
- async createSession(address, _accessToken, _idToken) {
854
- var _a, _b, _c, _d;
855
- const privyWallet = (_b = (_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.wallets) === null || _b === void 0 ? void 0 : _b.find((w) => w.address === address);
856
- if (!privyWallet) {
857
- throw new Error('No Privy Solana wallet matching the requested session address is available.');
858
- }
859
- const nonce = await genAuthNonce();
860
- const message = await genSolanaMessage(address, nonce);
861
- const messageBytes = getPlatform().textEncode(message);
862
- // Low-level Privy sign hook — signs WITHOUT touching the session (the public
863
- // signMessage() bootstraps sessions, which would recurse here).
864
- const signResult = await ((_c = this.privyMethods) === null || _c === void 0 ? void 0 : _c.signMessage({
865
- message: messageBytes,
866
- wallet: privyWallet,
867
- }));
868
- const signatureBytes = (_d = signResult === null || signResult === void 0 ? void 0 : signResult.signature) !== null && _d !== void 0 ? _d : signResult;
869
- // base64-encoded detached signature (matches Phantom + guest providers).
870
- const signature = bufferExports.Buffer.from(signatureBytes).toString('base64');
871
- const createSessionResult = await createSessionWithSignature(address, message, signature);
872
- if (createSessionResult) {
873
- await getActiveSessionManager().storeSession(address, createSessionResult.accessToken, createSessionResult.idToken, createSessionResult.refreshToken);
874
- }
875
- }
876
- /**
877
- * Waits for a wallet matching the session address to appear in the wallets array.
878
- * This handles the timing gap where Privy's embedded wallet (created for email login users)
879
- * may not yet be in the useWallets() array when signTransaction is called immediately after login.
880
- */
881
- async waitForMatchingWallet(sessionAddress, timeoutMs = 10000) {
882
- var _a;
883
- const startTime = Date.now();
884
- while (Date.now() - startTime < timeoutMs) {
885
- const wallets = (_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.wallets;
886
- if (wallets && wallets.length > 0) {
887
- const wallet = wallets.find((w) => w.address === sessionAddress);
888
- if (wallet)
889
- return wallet;
890
- }
891
- await new Promise(resolve => setTimeout(resolve, 200));
892
- }
893
- return null;
894
- }
895
- async ensureReady({ waitForWallets = false, timeoutMs = 15000 } = {}) {
896
- const isReady = () => {
897
- var _a, _b;
898
- if (!((_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.ready))
899
- return false;
900
- if (waitForWallets && !((_b = this.privyMethods) === null || _b === void 0 ? void 0 : _b.walletReady))
901
- return false;
902
- return true;
903
- };
904
- if (isReady())
905
- return;
906
- return new Promise((resolve, reject) => {
907
- const startTime = Date.now();
908
- const check = () => {
909
- if (isReady()) {
910
- resolve();
911
- }
912
- else if (Date.now() - startTime > timeoutMs) {
913
- reject(new Error('Login provider failed to initialize. This can happen in incognito mode or when third-party cookies are blocked. Try using a regular browser window.'));
914
- }
915
- else {
916
- setTimeout(check, 100);
917
- }
918
- };
919
- check();
920
- });
921
- }
922
- }
923
- PrivyWalletProvider.instance = null;
924
-
925
- export { PrivyWalletProvider };
926
- //# sourceMappingURL=privy-wallet-provider-YR9Eisgl.mjs.map