@phantom/browser-sdk 0.3.4 → 0.3.5
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/README.md +48 -2
- package/dist/index.d.ts +20 -12
- package/dist/index.js +328 -43
- package/dist/index.mjs +328 -43
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -41,8 +41,7 @@ const sdk = new BrowserSDK({
|
|
|
41
41
|
organizationId: "your-org-id",
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
const {
|
|
45
|
-
console.log("Wallet ID:", walletId);
|
|
44
|
+
const { addresses } = await sdk.connect();
|
|
46
45
|
console.log("Addresses:", addresses);
|
|
47
46
|
|
|
48
47
|
const result = await sdk.signAndSendTransaction({
|
|
@@ -89,6 +88,7 @@ const sdk = new BrowserSDK({
|
|
|
89
88
|
},
|
|
90
89
|
appName: "My DApp", // optional, for branding
|
|
91
90
|
appLogo: "https://myapp.com/logo.png", // optional, for branding
|
|
91
|
+
autoConnect: true, // optional, auto-connect to existing session (default: true for embedded)
|
|
92
92
|
debug: {
|
|
93
93
|
enabled: true, // optional, enable debug logging
|
|
94
94
|
level: "info", // optional, debug level
|
|
@@ -162,6 +162,43 @@ const sdk = new BrowserSDK({
|
|
|
162
162
|
- **@solana/web3.js**: Better ecosystem compatibility, wider community support
|
|
163
163
|
- **@solana/kit**: Better TypeScript support, modern architecture, smaller bundle size
|
|
164
164
|
|
|
165
|
+
### Auto-Connect Feature
|
|
166
|
+
|
|
167
|
+
The SDK can automatically reconnect to existing sessions when instantiated, providing a seamless user experience.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const sdk = new BrowserSDK({
|
|
171
|
+
providerType: "embedded",
|
|
172
|
+
// ... other config
|
|
173
|
+
autoConnect: true, // Default: true for embedded, false for injected
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// SDK will automatically check for existing valid session and connect in background
|
|
177
|
+
// No need to call connect() if user already has a session
|
|
178
|
+
|
|
179
|
+
// Check if already connected
|
|
180
|
+
if (sdk.isConnected()) {
|
|
181
|
+
console.log("Already connected!");
|
|
182
|
+
const addresses = await sdk.getAddresses();
|
|
183
|
+
} else {
|
|
184
|
+
// First time or session expired, need to connect manually
|
|
185
|
+
await sdk.connect();
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Disabling Auto-Connect:**
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const sdk = new BrowserSDK({
|
|
193
|
+
providerType: "embedded",
|
|
194
|
+
// ... other config
|
|
195
|
+
autoConnect: false, // Disable auto-connect
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Now you must manually call connect() every time
|
|
199
|
+
await sdk.connect();
|
|
200
|
+
```
|
|
201
|
+
|
|
165
202
|
## API Reference
|
|
166
203
|
|
|
167
204
|
### Constructor
|
|
@@ -188,6 +225,7 @@ interface BrowserSDKConfig {
|
|
|
188
225
|
};
|
|
189
226
|
embeddedWalletType?: "app-wallet" | "user-wallet"; // Wallet type
|
|
190
227
|
solanaProvider?: "web3js" | "kit"; // Solana library choice (default: 'web3js')
|
|
228
|
+
autoConnect?: boolean; // Enable auto-connect to existing sessions (default: true)
|
|
191
229
|
|
|
192
230
|
// Debug options
|
|
193
231
|
debug?: {
|
|
@@ -316,6 +354,14 @@ Disconnect from wallet and clear session.
|
|
|
316
354
|
await sdk.disconnect();
|
|
317
355
|
```
|
|
318
356
|
|
|
357
|
+
#### autoConnect()
|
|
358
|
+
|
|
359
|
+
Attempt auto-connection using existing session. Should be called after setting up event listeners to avoid race conditions. Only works with embedded providers.
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
await sdk.autoConnect();
|
|
363
|
+
```
|
|
364
|
+
|
|
319
365
|
## Transaction Examples
|
|
320
366
|
|
|
321
367
|
### Solana Transactions
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { AddressType } from '@phantom/client';
|
|
3
|
-
import { AuthOptions, ConnectResult, SignMessageParams, SignMessageResult, SignAndSendTransactionParams, SignedTransaction, WalletAddress } from '@phantom/embedded-provider-core';
|
|
1
|
+
import { EmbeddedProviderConfig, AuthOptions, ConnectResult, SignMessageParams, SignMessageResult, SignAndSendTransactionParams, SignedTransaction, WalletAddress, EmbeddedProviderEvent, EventCallback } from '@phantom/embedded-provider-core';
|
|
4
2
|
export { AuthOptions, ConnectResult, SignAndSendTransactionParams, SignMessageParams, SignMessageResult, SignedTransaction, WalletAddress } from '@phantom/embedded-provider-core';
|
|
5
3
|
export { NetworkId } from '@phantom/constants';
|
|
4
|
+
export { AddressType } from '@phantom/client';
|
|
6
5
|
|
|
7
6
|
declare enum DebugLevel {
|
|
8
7
|
ERROR = 0,
|
|
@@ -48,19 +47,12 @@ declare const DebugCategory: {
|
|
|
48
47
|
readonly SESSION: "Session";
|
|
49
48
|
};
|
|
50
49
|
|
|
51
|
-
interface BrowserSDKConfig {
|
|
50
|
+
interface BrowserSDKConfig extends Partial<EmbeddedProviderConfig> {
|
|
52
51
|
providerType: "injected" | "embedded" | (string & Record<never, never>);
|
|
53
|
-
appName?: string;
|
|
54
|
-
appLogo?: string;
|
|
55
|
-
addressTypes?: AddressType[];
|
|
56
52
|
apiBaseUrl?: string;
|
|
57
53
|
organizationId?: string;
|
|
58
|
-
authOptions?: {
|
|
59
|
-
authUrl?: string;
|
|
60
|
-
redirectUrl?: string;
|
|
61
|
-
};
|
|
62
54
|
embeddedWalletType?: "app-wallet" | "user-wallet" | (string & Record<never, never>);
|
|
63
|
-
|
|
55
|
+
autoConnect?: boolean;
|
|
64
56
|
debug?: {
|
|
65
57
|
enabled?: boolean;
|
|
66
58
|
level?: DebugLevel;
|
|
@@ -137,6 +129,22 @@ declare class BrowserSDK {
|
|
|
137
129
|
* Get the current wallet ID (for embedded wallets)
|
|
138
130
|
*/
|
|
139
131
|
getWalletId(): string | null;
|
|
132
|
+
/**
|
|
133
|
+
* Add event listener for provider events (connect, connect_start, connect_error, disconnect, error)
|
|
134
|
+
* Works with both embedded and injected providers
|
|
135
|
+
*/
|
|
136
|
+
on(event: EmbeddedProviderEvent, callback: EventCallback): void;
|
|
137
|
+
/**
|
|
138
|
+
* Remove event listener for provider events
|
|
139
|
+
* Works with both embedded and injected providers
|
|
140
|
+
*/
|
|
141
|
+
off(event: EmbeddedProviderEvent, callback: EventCallback): void;
|
|
142
|
+
/**
|
|
143
|
+
* Attempt auto-connection using existing session
|
|
144
|
+
* Should be called after setting up event listeners
|
|
145
|
+
* Only works with embedded providers
|
|
146
|
+
*/
|
|
147
|
+
autoConnect(): Promise<void>;
|
|
140
148
|
}
|
|
141
149
|
|
|
142
150
|
/**
|
package/dist/index.js
CHANGED
|
@@ -133,6 +133,10 @@ var InjectedProvider = class {
|
|
|
133
133
|
constructor(config) {
|
|
134
134
|
this.connected = false;
|
|
135
135
|
this.addresses = [];
|
|
136
|
+
// Event management
|
|
137
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
138
|
+
this.browserInjectedCleanupFunctions = [];
|
|
139
|
+
this.eventsInitialized = false;
|
|
136
140
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Initializing InjectedProvider", { config });
|
|
137
141
|
this.addressTypes = config.addressTypes || [import_client.AddressType.solana, import_client.AddressType.ethereum];
|
|
138
142
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Address types configured", { addressTypes: this.addressTypes });
|
|
@@ -157,70 +161,108 @@ var InjectedProvider = class {
|
|
|
157
161
|
authOptionsIgnored: !!authOptions
|
|
158
162
|
// Note: authOptions are ignored for injected provider
|
|
159
163
|
});
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
164
|
+
this.emit("connect_start", {
|
|
165
|
+
source: "manual-connect",
|
|
166
|
+
providerType: "injected"
|
|
167
|
+
});
|
|
168
|
+
try {
|
|
169
|
+
if (!this.phantom.extension.isInstalled()) {
|
|
170
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Phantom wallet extension not found");
|
|
171
|
+
const error = new Error("Phantom wallet not found");
|
|
172
|
+
this.emit("connect_error", {
|
|
173
|
+
error: error.message,
|
|
174
|
+
source: "manual-connect"
|
|
175
|
+
});
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Phantom extension detected");
|
|
179
|
+
const connectedAddresses = [];
|
|
180
|
+
if (this.addressTypes.includes(import_client.AddressType.solana)) {
|
|
181
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Solana connection");
|
|
182
|
+
try {
|
|
183
|
+
const publicKey = await this.phantom.solana.connect();
|
|
184
|
+
if (publicKey) {
|
|
185
|
+
connectedAddresses.push({
|
|
186
|
+
addressType: import_client.AddressType.solana,
|
|
187
|
+
address: publicKey
|
|
188
|
+
});
|
|
189
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Solana connected successfully", { address: publicKey });
|
|
190
|
+
}
|
|
191
|
+
} catch (err) {
|
|
192
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana", { error: err });
|
|
176
193
|
}
|
|
177
|
-
} catch (err) {
|
|
178
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana", { error: err });
|
|
179
194
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
195
|
+
if (this.addressTypes.includes(import_client.AddressType.ethereum)) {
|
|
196
|
+
try {
|
|
197
|
+
const accounts = await this.phantom.ethereum.connect();
|
|
198
|
+
if (accounts && accounts.length > 0) {
|
|
199
|
+
connectedAddresses.push(
|
|
200
|
+
...accounts.map((address) => ({
|
|
201
|
+
addressType: import_client.AddressType.ethereum,
|
|
202
|
+
address
|
|
203
|
+
}))
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum", { error: err });
|
|
191
208
|
}
|
|
192
|
-
} catch (err) {
|
|
193
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum", { error: err });
|
|
194
209
|
}
|
|
210
|
+
if (connectedAddresses.length === 0) {
|
|
211
|
+
const error = new Error("Failed to connect to any supported wallet provider");
|
|
212
|
+
this.emit("connect_error", {
|
|
213
|
+
error: error.message,
|
|
214
|
+
source: "manual-connect"
|
|
215
|
+
});
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
this.addresses = connectedAddresses;
|
|
219
|
+
this.connected = true;
|
|
220
|
+
const result = {
|
|
221
|
+
addresses: this.addresses,
|
|
222
|
+
status: "completed"
|
|
223
|
+
// walletId is not applicable for injected providers
|
|
224
|
+
};
|
|
225
|
+
this.emit("connect", {
|
|
226
|
+
addresses: this.addresses,
|
|
227
|
+
source: "manual-connect"
|
|
228
|
+
});
|
|
229
|
+
return result;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
if (error instanceof Error && !error.message.includes("Phantom wallet not found") && !error.message.includes("Failed to connect to any supported wallet provider")) {
|
|
232
|
+
this.emit("connect_error", {
|
|
233
|
+
error: error.message,
|
|
234
|
+
source: "manual-connect"
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
throw error;
|
|
195
238
|
}
|
|
196
|
-
if (connectedAddresses.length === 0) {
|
|
197
|
-
throw new Error("Failed to connect to any supported wallet provider");
|
|
198
|
-
}
|
|
199
|
-
this.addresses = connectedAddresses;
|
|
200
|
-
this.connected = true;
|
|
201
|
-
return {
|
|
202
|
-
addresses: this.addresses,
|
|
203
|
-
status: "completed"
|
|
204
|
-
// walletId is not applicable for injected providers
|
|
205
|
-
};
|
|
206
239
|
}
|
|
207
240
|
async disconnect() {
|
|
241
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Starting injected provider disconnect");
|
|
208
242
|
if (this.addressTypes.includes(import_client.AddressType.solana)) {
|
|
209
243
|
try {
|
|
210
244
|
await this.phantom.solana.disconnect();
|
|
245
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana disconnected successfully");
|
|
211
246
|
} catch (err) {
|
|
212
|
-
|
|
247
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to disconnect Solana", { error: err });
|
|
213
248
|
}
|
|
214
249
|
}
|
|
215
250
|
if (this.addressTypes.includes(import_client.AddressType.ethereum)) {
|
|
216
251
|
try {
|
|
217
252
|
await this.phantom.ethereum.disconnect();
|
|
253
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnected successfully");
|
|
218
254
|
} catch (err) {
|
|
219
|
-
|
|
255
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to disconnect Ethereum", { error: err });
|
|
220
256
|
}
|
|
221
257
|
}
|
|
258
|
+
this.browserInjectedCleanupFunctions.forEach((cleanup) => cleanup());
|
|
259
|
+
this.browserInjectedCleanupFunctions = [];
|
|
222
260
|
this.connected = false;
|
|
223
261
|
this.addresses = [];
|
|
262
|
+
this.emit("disconnect", {
|
|
263
|
+
source: "manual-disconnect"
|
|
264
|
+
});
|
|
265
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Injected provider disconnected successfully");
|
|
224
266
|
}
|
|
225
267
|
async signMessage(params) {
|
|
226
268
|
if (!this.connected) {
|
|
@@ -293,6 +335,139 @@ var InjectedProvider = class {
|
|
|
293
335
|
isConnected() {
|
|
294
336
|
return this.connected;
|
|
295
337
|
}
|
|
338
|
+
// Event management methods - implementing unified event interface
|
|
339
|
+
on(event, callback) {
|
|
340
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Adding event listener", { event });
|
|
341
|
+
if (!this.eventsInitialized) {
|
|
342
|
+
this.setupBrowserInjectedEvents();
|
|
343
|
+
this.eventsInitialized = true;
|
|
344
|
+
}
|
|
345
|
+
if (!this.eventListeners.has(event)) {
|
|
346
|
+
this.eventListeners.set(event, /* @__PURE__ */ new Set());
|
|
347
|
+
}
|
|
348
|
+
this.eventListeners.get(event).add(callback);
|
|
349
|
+
}
|
|
350
|
+
off(event, callback) {
|
|
351
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Removing event listener", { event });
|
|
352
|
+
if (this.eventListeners.has(event)) {
|
|
353
|
+
this.eventListeners.get(event).delete(callback);
|
|
354
|
+
if (this.eventListeners.get(event).size === 0) {
|
|
355
|
+
this.eventListeners.delete(event);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
emit(event, data) {
|
|
360
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Emitting event", {
|
|
361
|
+
event,
|
|
362
|
+
listenerCount: this.eventListeners.get(event)?.size || 0,
|
|
363
|
+
data
|
|
364
|
+
});
|
|
365
|
+
const listeners = this.eventListeners.get(event);
|
|
366
|
+
if (listeners && listeners.size > 0) {
|
|
367
|
+
listeners.forEach((callback) => {
|
|
368
|
+
try {
|
|
369
|
+
callback(data);
|
|
370
|
+
} catch (error) {
|
|
371
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Event callback error", { event, error });
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
setupBrowserInjectedEvents() {
|
|
377
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up browser-injected-sdk event listeners");
|
|
378
|
+
if (this.addressTypes.includes(import_client.AddressType.solana) && this.phantom.solana) {
|
|
379
|
+
this.setupSolanaEvents();
|
|
380
|
+
}
|
|
381
|
+
if (this.addressTypes.includes(import_client.AddressType.ethereum) && this.phantom.ethereum) {
|
|
382
|
+
this.setupEthereumEvents();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
setupSolanaEvents() {
|
|
386
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Solana event listeners");
|
|
387
|
+
const solanaConnectCleanup = this.phantom.solana.addEventListener("connect", (publicKey) => {
|
|
388
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana connect event received", { publicKey });
|
|
389
|
+
const solanaAddress = { addressType: import_client.AddressType.solana, address: publicKey };
|
|
390
|
+
if (!this.addresses.find((addr) => addr.addressType === import_client.AddressType.solana)) {
|
|
391
|
+
this.addresses.push(solanaAddress);
|
|
392
|
+
}
|
|
393
|
+
this.connected = true;
|
|
394
|
+
this.emit("connect", {
|
|
395
|
+
addresses: this.addresses,
|
|
396
|
+
source: "injected-extension"
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
const solanaDisconnectCleanup = this.phantom.solana.addEventListener("disconnect", () => {
|
|
400
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana disconnect event received");
|
|
401
|
+
this.addresses = this.addresses.filter((addr) => addr.addressType !== import_client.AddressType.solana);
|
|
402
|
+
this.connected = this.addresses.length > 0;
|
|
403
|
+
this.emit("disconnect", {
|
|
404
|
+
source: "injected-extension"
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
const solanaAccountChangedCleanup = this.phantom.solana.addEventListener("accountChanged", (publicKey) => {
|
|
408
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana account changed event received", { publicKey });
|
|
409
|
+
const solanaIndex = this.addresses.findIndex((addr) => addr.addressType === import_client.AddressType.solana);
|
|
410
|
+
if (solanaIndex >= 0) {
|
|
411
|
+
this.addresses[solanaIndex] = { addressType: import_client.AddressType.solana, address: publicKey };
|
|
412
|
+
} else {
|
|
413
|
+
this.addresses.push({ addressType: import_client.AddressType.solana, address: publicKey });
|
|
414
|
+
}
|
|
415
|
+
this.emit("connect", {
|
|
416
|
+
addresses: this.addresses,
|
|
417
|
+
source: "injected-extension-account-change"
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
this.browserInjectedCleanupFunctions.push(
|
|
421
|
+
solanaConnectCleanup,
|
|
422
|
+
solanaDisconnectCleanup,
|
|
423
|
+
solanaAccountChangedCleanup
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
setupEthereumEvents() {
|
|
427
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Ethereum event listeners");
|
|
428
|
+
const ethConnectCleanup = this.phantom.ethereum.addEventListener("connect", (accounts) => {
|
|
429
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum connect event received", { accounts });
|
|
430
|
+
this.addresses = this.addresses.filter((addr) => addr.addressType !== import_client.AddressType.ethereum);
|
|
431
|
+
if (accounts && accounts.length > 0) {
|
|
432
|
+
this.addresses.push(...accounts.map((address) => ({
|
|
433
|
+
addressType: import_client.AddressType.ethereum,
|
|
434
|
+
address
|
|
435
|
+
})));
|
|
436
|
+
}
|
|
437
|
+
this.connected = this.addresses.length > 0;
|
|
438
|
+
this.emit("connect", {
|
|
439
|
+
addresses: this.addresses,
|
|
440
|
+
source: "injected-extension"
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
const ethDisconnectCleanup = this.phantom.ethereum.addEventListener("disconnect", () => {
|
|
444
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnect event received");
|
|
445
|
+
this.addresses = this.addresses.filter((addr) => addr.addressType !== import_client.AddressType.ethereum);
|
|
446
|
+
this.connected = this.addresses.length > 0;
|
|
447
|
+
this.emit("disconnect", {
|
|
448
|
+
source: "injected-extension"
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
const ethAccountChangedCleanup = this.phantom.ethereum.addEventListener("accountChanged", (accounts) => {
|
|
452
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum account changed event received", { accounts });
|
|
453
|
+
this.addresses = this.addresses.filter((addr) => addr.addressType !== import_client.AddressType.ethereum);
|
|
454
|
+
if (accounts && accounts.length > 0) {
|
|
455
|
+
this.addresses.push(...accounts.map((address) => ({
|
|
456
|
+
addressType: import_client.AddressType.ethereum,
|
|
457
|
+
address
|
|
458
|
+
})));
|
|
459
|
+
}
|
|
460
|
+
this.emit("connect", {
|
|
461
|
+
addresses: this.addresses,
|
|
462
|
+
source: "injected-extension-account-change"
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
this.browserInjectedCleanupFunctions.push(
|
|
466
|
+
ethConnectCleanup,
|
|
467
|
+
ethDisconnectCleanup,
|
|
468
|
+
ethAccountChangedCleanup
|
|
469
|
+
);
|
|
470
|
+
}
|
|
296
471
|
};
|
|
297
472
|
|
|
298
473
|
// src/providers/embedded/index.ts
|
|
@@ -662,11 +837,15 @@ var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvi
|
|
|
662
837
|
|
|
663
838
|
// src/ProviderManager.ts
|
|
664
839
|
var ProviderManager = class {
|
|
840
|
+
// Track which providers have forwarding set up
|
|
665
841
|
constructor(config) {
|
|
666
842
|
this.providers = /* @__PURE__ */ new Map();
|
|
667
843
|
this.currentProvider = null;
|
|
668
844
|
this.currentProviderKey = null;
|
|
669
845
|
this.walletId = null;
|
|
846
|
+
// Event management for forwarding provider events
|
|
847
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
848
|
+
this.providerForwardingSetup = /* @__PURE__ */ new WeakSet();
|
|
670
849
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Initializing ProviderManager", { config });
|
|
671
850
|
this.config = config;
|
|
672
851
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Setting default provider");
|
|
@@ -691,6 +870,7 @@ var ProviderManager = class {
|
|
|
691
870
|
this.currentProvider = this.providers.get(key);
|
|
692
871
|
this.currentProviderKey = key;
|
|
693
872
|
this.walletId = null;
|
|
873
|
+
this.ensureProviderEventForwarding();
|
|
694
874
|
return this.currentProvider;
|
|
695
875
|
}
|
|
696
876
|
/**
|
|
@@ -785,6 +965,79 @@ var ProviderManager = class {
|
|
|
785
965
|
getWalletId() {
|
|
786
966
|
return this.walletId;
|
|
787
967
|
}
|
|
968
|
+
/**
|
|
969
|
+
* Add event listener - stores callback and ensures current provider forwards events to ProviderManager
|
|
970
|
+
*/
|
|
971
|
+
on(event, callback) {
|
|
972
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Adding event listener", { event });
|
|
973
|
+
if (!this.eventListeners.has(event)) {
|
|
974
|
+
this.eventListeners.set(event, /* @__PURE__ */ new Set());
|
|
975
|
+
}
|
|
976
|
+
this.eventListeners.get(event).add(callback);
|
|
977
|
+
this.ensureProviderEventForwarding();
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Remove event listener
|
|
981
|
+
*/
|
|
982
|
+
off(event, callback) {
|
|
983
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Removing event listener", { event });
|
|
984
|
+
if (this.eventListeners.has(event)) {
|
|
985
|
+
this.eventListeners.get(event).delete(callback);
|
|
986
|
+
if (this.eventListeners.get(event).size === 0) {
|
|
987
|
+
this.eventListeners.delete(event);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Emit event to all registered callbacks
|
|
993
|
+
*/
|
|
994
|
+
emit(event, data) {
|
|
995
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Emitting event to stored callbacks", {
|
|
996
|
+
event,
|
|
997
|
+
listenerCount: this.eventListeners.get(event)?.size || 0,
|
|
998
|
+
data
|
|
999
|
+
});
|
|
1000
|
+
const listeners = this.eventListeners.get(event);
|
|
1001
|
+
if (listeners && listeners.size > 0) {
|
|
1002
|
+
listeners.forEach((callback) => {
|
|
1003
|
+
try {
|
|
1004
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Calling stored callback for event", { event });
|
|
1005
|
+
callback(data);
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
debug.error(DebugCategory.PROVIDER_MANAGER, "Event callback error", { event, error });
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
} else {
|
|
1011
|
+
debug.warn(DebugCategory.PROVIDER_MANAGER, "No stored callbacks for event", { event });
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Ensure current provider forwards its events to this ProviderManager
|
|
1016
|
+
* Only sets up forwarding once per provider instance to avoid accumulation
|
|
1017
|
+
*/
|
|
1018
|
+
ensureProviderEventForwarding() {
|
|
1019
|
+
if (!this.currentProvider || !("on" in this.currentProvider)) {
|
|
1020
|
+
debug.warn(DebugCategory.PROVIDER_MANAGER, "Current provider does not support events", {
|
|
1021
|
+
providerType: this.getCurrentProviderInfo()?.type
|
|
1022
|
+
});
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
if (this.providerForwardingSetup.has(this.currentProvider)) {
|
|
1026
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Event forwarding already set up for current provider");
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Setting up event forwarding from current provider");
|
|
1030
|
+
const eventsToForward = ["connect_start", "connect", "connect_error", "disconnect", "error"];
|
|
1031
|
+
for (const event of eventsToForward) {
|
|
1032
|
+
const forwardingCallback = (data) => {
|
|
1033
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Forwarding event from provider", { event, data });
|
|
1034
|
+
this.emit(event, data);
|
|
1035
|
+
};
|
|
1036
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Attaching forwarding callback for event", { event });
|
|
1037
|
+
this.currentProvider.on(event, forwardingCallback);
|
|
1038
|
+
}
|
|
1039
|
+
this.providerForwardingSetup.add(this.currentProvider);
|
|
1040
|
+
}
|
|
788
1041
|
/**
|
|
789
1042
|
* Set default provider based on initial config
|
|
790
1043
|
*/
|
|
@@ -1038,6 +1291,38 @@ var BrowserSDK = class {
|
|
|
1038
1291
|
getWalletId() {
|
|
1039
1292
|
return this.providerManager.getWalletId();
|
|
1040
1293
|
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Add event listener for provider events (connect, connect_start, connect_error, disconnect, error)
|
|
1296
|
+
* Works with both embedded and injected providers
|
|
1297
|
+
*/
|
|
1298
|
+
on(event, callback) {
|
|
1299
|
+
debug.log(DebugCategory.BROWSER_SDK, "Adding event listener", { event });
|
|
1300
|
+
this.providerManager.on(event, callback);
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Remove event listener for provider events
|
|
1304
|
+
* Works with both embedded and injected providers
|
|
1305
|
+
*/
|
|
1306
|
+
off(event, callback) {
|
|
1307
|
+
debug.log(DebugCategory.BROWSER_SDK, "Removing event listener", { event });
|
|
1308
|
+
this.providerManager.off(event, callback);
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Attempt auto-connection using existing session
|
|
1312
|
+
* Should be called after setting up event listeners
|
|
1313
|
+
* Only works with embedded providers
|
|
1314
|
+
*/
|
|
1315
|
+
async autoConnect() {
|
|
1316
|
+
debug.log(DebugCategory.BROWSER_SDK, "Attempting auto-connect");
|
|
1317
|
+
const currentProvider = this.providerManager.getCurrentProvider();
|
|
1318
|
+
if (currentProvider && "autoConnect" in currentProvider) {
|
|
1319
|
+
await currentProvider.autoConnect();
|
|
1320
|
+
} else {
|
|
1321
|
+
debug.warn(DebugCategory.BROWSER_SDK, "Current provider does not support auto-connect", {
|
|
1322
|
+
providerType: this.getCurrentProviderInfo()?.type
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1041
1326
|
};
|
|
1042
1327
|
|
|
1043
1328
|
// src/index.ts
|
package/dist/index.mjs
CHANGED
|
@@ -86,6 +86,10 @@ var InjectedProvider = class {
|
|
|
86
86
|
constructor(config) {
|
|
87
87
|
this.connected = false;
|
|
88
88
|
this.addresses = [];
|
|
89
|
+
// Event management
|
|
90
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
91
|
+
this.browserInjectedCleanupFunctions = [];
|
|
92
|
+
this.eventsInitialized = false;
|
|
89
93
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Initializing InjectedProvider", { config });
|
|
90
94
|
this.addressTypes = config.addressTypes || [AddressType.solana, AddressType.ethereum];
|
|
91
95
|
debug.log(DebugCategory.INJECTED_PROVIDER, "Address types configured", { addressTypes: this.addressTypes });
|
|
@@ -110,70 +114,108 @@ var InjectedProvider = class {
|
|
|
110
114
|
authOptionsIgnored: !!authOptions
|
|
111
115
|
// Note: authOptions are ignored for injected provider
|
|
112
116
|
});
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
117
|
+
this.emit("connect_start", {
|
|
118
|
+
source: "manual-connect",
|
|
119
|
+
providerType: "injected"
|
|
120
|
+
});
|
|
121
|
+
try {
|
|
122
|
+
if (!this.phantom.extension.isInstalled()) {
|
|
123
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Phantom wallet extension not found");
|
|
124
|
+
const error = new Error("Phantom wallet not found");
|
|
125
|
+
this.emit("connect_error", {
|
|
126
|
+
error: error.message,
|
|
127
|
+
source: "manual-connect"
|
|
128
|
+
});
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Phantom extension detected");
|
|
132
|
+
const connectedAddresses = [];
|
|
133
|
+
if (this.addressTypes.includes(AddressType.solana)) {
|
|
134
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Attempting Solana connection");
|
|
135
|
+
try {
|
|
136
|
+
const publicKey = await this.phantom.solana.connect();
|
|
137
|
+
if (publicKey) {
|
|
138
|
+
connectedAddresses.push({
|
|
139
|
+
addressType: AddressType.solana,
|
|
140
|
+
address: publicKey
|
|
141
|
+
});
|
|
142
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Solana connected successfully", { address: publicKey });
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana", { error: err });
|
|
129
146
|
}
|
|
130
|
-
} catch (err) {
|
|
131
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana", { error: err });
|
|
132
147
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
148
|
+
if (this.addressTypes.includes(AddressType.ethereum)) {
|
|
149
|
+
try {
|
|
150
|
+
const accounts = await this.phantom.ethereum.connect();
|
|
151
|
+
if (accounts && accounts.length > 0) {
|
|
152
|
+
connectedAddresses.push(
|
|
153
|
+
...accounts.map((address) => ({
|
|
154
|
+
addressType: AddressType.ethereum,
|
|
155
|
+
address
|
|
156
|
+
}))
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
} catch (err) {
|
|
160
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum", { error: err });
|
|
144
161
|
}
|
|
145
|
-
} catch (err) {
|
|
146
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum", { error: err });
|
|
147
162
|
}
|
|
163
|
+
if (connectedAddresses.length === 0) {
|
|
164
|
+
const error = new Error("Failed to connect to any supported wallet provider");
|
|
165
|
+
this.emit("connect_error", {
|
|
166
|
+
error: error.message,
|
|
167
|
+
source: "manual-connect"
|
|
168
|
+
});
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
this.addresses = connectedAddresses;
|
|
172
|
+
this.connected = true;
|
|
173
|
+
const result = {
|
|
174
|
+
addresses: this.addresses,
|
|
175
|
+
status: "completed"
|
|
176
|
+
// walletId is not applicable for injected providers
|
|
177
|
+
};
|
|
178
|
+
this.emit("connect", {
|
|
179
|
+
addresses: this.addresses,
|
|
180
|
+
source: "manual-connect"
|
|
181
|
+
});
|
|
182
|
+
return result;
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (error instanceof Error && !error.message.includes("Phantom wallet not found") && !error.message.includes("Failed to connect to any supported wallet provider")) {
|
|
185
|
+
this.emit("connect_error", {
|
|
186
|
+
error: error.message,
|
|
187
|
+
source: "manual-connect"
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
throw error;
|
|
148
191
|
}
|
|
149
|
-
if (connectedAddresses.length === 0) {
|
|
150
|
-
throw new Error("Failed to connect to any supported wallet provider");
|
|
151
|
-
}
|
|
152
|
-
this.addresses = connectedAddresses;
|
|
153
|
-
this.connected = true;
|
|
154
|
-
return {
|
|
155
|
-
addresses: this.addresses,
|
|
156
|
-
status: "completed"
|
|
157
|
-
// walletId is not applicable for injected providers
|
|
158
|
-
};
|
|
159
192
|
}
|
|
160
193
|
async disconnect() {
|
|
194
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Starting injected provider disconnect");
|
|
161
195
|
if (this.addressTypes.includes(AddressType.solana)) {
|
|
162
196
|
try {
|
|
163
197
|
await this.phantom.solana.disconnect();
|
|
198
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana disconnected successfully");
|
|
164
199
|
} catch (err) {
|
|
165
|
-
|
|
200
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to disconnect Solana", { error: err });
|
|
166
201
|
}
|
|
167
202
|
}
|
|
168
203
|
if (this.addressTypes.includes(AddressType.ethereum)) {
|
|
169
204
|
try {
|
|
170
205
|
await this.phantom.ethereum.disconnect();
|
|
206
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnected successfully");
|
|
171
207
|
} catch (err) {
|
|
172
|
-
|
|
208
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to disconnect Ethereum", { error: err });
|
|
173
209
|
}
|
|
174
210
|
}
|
|
211
|
+
this.browserInjectedCleanupFunctions.forEach((cleanup) => cleanup());
|
|
212
|
+
this.browserInjectedCleanupFunctions = [];
|
|
175
213
|
this.connected = false;
|
|
176
214
|
this.addresses = [];
|
|
215
|
+
this.emit("disconnect", {
|
|
216
|
+
source: "manual-disconnect"
|
|
217
|
+
});
|
|
218
|
+
debug.info(DebugCategory.INJECTED_PROVIDER, "Injected provider disconnected successfully");
|
|
177
219
|
}
|
|
178
220
|
async signMessage(params) {
|
|
179
221
|
if (!this.connected) {
|
|
@@ -246,6 +288,139 @@ var InjectedProvider = class {
|
|
|
246
288
|
isConnected() {
|
|
247
289
|
return this.connected;
|
|
248
290
|
}
|
|
291
|
+
// Event management methods - implementing unified event interface
|
|
292
|
+
on(event, callback) {
|
|
293
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Adding event listener", { event });
|
|
294
|
+
if (!this.eventsInitialized) {
|
|
295
|
+
this.setupBrowserInjectedEvents();
|
|
296
|
+
this.eventsInitialized = true;
|
|
297
|
+
}
|
|
298
|
+
if (!this.eventListeners.has(event)) {
|
|
299
|
+
this.eventListeners.set(event, /* @__PURE__ */ new Set());
|
|
300
|
+
}
|
|
301
|
+
this.eventListeners.get(event).add(callback);
|
|
302
|
+
}
|
|
303
|
+
off(event, callback) {
|
|
304
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Removing event listener", { event });
|
|
305
|
+
if (this.eventListeners.has(event)) {
|
|
306
|
+
this.eventListeners.get(event).delete(callback);
|
|
307
|
+
if (this.eventListeners.get(event).size === 0) {
|
|
308
|
+
this.eventListeners.delete(event);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
emit(event, data) {
|
|
313
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Emitting event", {
|
|
314
|
+
event,
|
|
315
|
+
listenerCount: this.eventListeners.get(event)?.size || 0,
|
|
316
|
+
data
|
|
317
|
+
});
|
|
318
|
+
const listeners = this.eventListeners.get(event);
|
|
319
|
+
if (listeners && listeners.size > 0) {
|
|
320
|
+
listeners.forEach((callback) => {
|
|
321
|
+
try {
|
|
322
|
+
callback(data);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
debug.error(DebugCategory.INJECTED_PROVIDER, "Event callback error", { event, error });
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
setupBrowserInjectedEvents() {
|
|
330
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up browser-injected-sdk event listeners");
|
|
331
|
+
if (this.addressTypes.includes(AddressType.solana) && this.phantom.solana) {
|
|
332
|
+
this.setupSolanaEvents();
|
|
333
|
+
}
|
|
334
|
+
if (this.addressTypes.includes(AddressType.ethereum) && this.phantom.ethereum) {
|
|
335
|
+
this.setupEthereumEvents();
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
setupSolanaEvents() {
|
|
339
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Solana event listeners");
|
|
340
|
+
const solanaConnectCleanup = this.phantom.solana.addEventListener("connect", (publicKey) => {
|
|
341
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana connect event received", { publicKey });
|
|
342
|
+
const solanaAddress = { addressType: AddressType.solana, address: publicKey };
|
|
343
|
+
if (!this.addresses.find((addr) => addr.addressType === AddressType.solana)) {
|
|
344
|
+
this.addresses.push(solanaAddress);
|
|
345
|
+
}
|
|
346
|
+
this.connected = true;
|
|
347
|
+
this.emit("connect", {
|
|
348
|
+
addresses: this.addresses,
|
|
349
|
+
source: "injected-extension"
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
const solanaDisconnectCleanup = this.phantom.solana.addEventListener("disconnect", () => {
|
|
353
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana disconnect event received");
|
|
354
|
+
this.addresses = this.addresses.filter((addr) => addr.addressType !== AddressType.solana);
|
|
355
|
+
this.connected = this.addresses.length > 0;
|
|
356
|
+
this.emit("disconnect", {
|
|
357
|
+
source: "injected-extension"
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
const solanaAccountChangedCleanup = this.phantom.solana.addEventListener("accountChanged", (publicKey) => {
|
|
361
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Solana account changed event received", { publicKey });
|
|
362
|
+
const solanaIndex = this.addresses.findIndex((addr) => addr.addressType === AddressType.solana);
|
|
363
|
+
if (solanaIndex >= 0) {
|
|
364
|
+
this.addresses[solanaIndex] = { addressType: AddressType.solana, address: publicKey };
|
|
365
|
+
} else {
|
|
366
|
+
this.addresses.push({ addressType: AddressType.solana, address: publicKey });
|
|
367
|
+
}
|
|
368
|
+
this.emit("connect", {
|
|
369
|
+
addresses: this.addresses,
|
|
370
|
+
source: "injected-extension-account-change"
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
this.browserInjectedCleanupFunctions.push(
|
|
374
|
+
solanaConnectCleanup,
|
|
375
|
+
solanaDisconnectCleanup,
|
|
376
|
+
solanaAccountChangedCleanup
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
setupEthereumEvents() {
|
|
380
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Setting up Ethereum event listeners");
|
|
381
|
+
const ethConnectCleanup = this.phantom.ethereum.addEventListener("connect", (accounts) => {
|
|
382
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum connect event received", { accounts });
|
|
383
|
+
this.addresses = this.addresses.filter((addr) => addr.addressType !== AddressType.ethereum);
|
|
384
|
+
if (accounts && accounts.length > 0) {
|
|
385
|
+
this.addresses.push(...accounts.map((address) => ({
|
|
386
|
+
addressType: AddressType.ethereum,
|
|
387
|
+
address
|
|
388
|
+
})));
|
|
389
|
+
}
|
|
390
|
+
this.connected = this.addresses.length > 0;
|
|
391
|
+
this.emit("connect", {
|
|
392
|
+
addresses: this.addresses,
|
|
393
|
+
source: "injected-extension"
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
const ethDisconnectCleanup = this.phantom.ethereum.addEventListener("disconnect", () => {
|
|
397
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum disconnect event received");
|
|
398
|
+
this.addresses = this.addresses.filter((addr) => addr.addressType !== AddressType.ethereum);
|
|
399
|
+
this.connected = this.addresses.length > 0;
|
|
400
|
+
this.emit("disconnect", {
|
|
401
|
+
source: "injected-extension"
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
const ethAccountChangedCleanup = this.phantom.ethereum.addEventListener("accountChanged", (accounts) => {
|
|
405
|
+
debug.log(DebugCategory.INJECTED_PROVIDER, "Ethereum account changed event received", { accounts });
|
|
406
|
+
this.addresses = this.addresses.filter((addr) => addr.addressType !== AddressType.ethereum);
|
|
407
|
+
if (accounts && accounts.length > 0) {
|
|
408
|
+
this.addresses.push(...accounts.map((address) => ({
|
|
409
|
+
addressType: AddressType.ethereum,
|
|
410
|
+
address
|
|
411
|
+
})));
|
|
412
|
+
}
|
|
413
|
+
this.emit("connect", {
|
|
414
|
+
addresses: this.addresses,
|
|
415
|
+
source: "injected-extension-account-change"
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
this.browserInjectedCleanupFunctions.push(
|
|
419
|
+
ethConnectCleanup,
|
|
420
|
+
ethDisconnectCleanup,
|
|
421
|
+
ethAccountChangedCleanup
|
|
422
|
+
);
|
|
423
|
+
}
|
|
249
424
|
};
|
|
250
425
|
|
|
251
426
|
// src/providers/embedded/index.ts
|
|
@@ -615,11 +790,15 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
|
615
790
|
|
|
616
791
|
// src/ProviderManager.ts
|
|
617
792
|
var ProviderManager = class {
|
|
793
|
+
// Track which providers have forwarding set up
|
|
618
794
|
constructor(config) {
|
|
619
795
|
this.providers = /* @__PURE__ */ new Map();
|
|
620
796
|
this.currentProvider = null;
|
|
621
797
|
this.currentProviderKey = null;
|
|
622
798
|
this.walletId = null;
|
|
799
|
+
// Event management for forwarding provider events
|
|
800
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
801
|
+
this.providerForwardingSetup = /* @__PURE__ */ new WeakSet();
|
|
623
802
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Initializing ProviderManager", { config });
|
|
624
803
|
this.config = config;
|
|
625
804
|
debug.log(DebugCategory.PROVIDER_MANAGER, "Setting default provider");
|
|
@@ -644,6 +823,7 @@ var ProviderManager = class {
|
|
|
644
823
|
this.currentProvider = this.providers.get(key);
|
|
645
824
|
this.currentProviderKey = key;
|
|
646
825
|
this.walletId = null;
|
|
826
|
+
this.ensureProviderEventForwarding();
|
|
647
827
|
return this.currentProvider;
|
|
648
828
|
}
|
|
649
829
|
/**
|
|
@@ -738,6 +918,79 @@ var ProviderManager = class {
|
|
|
738
918
|
getWalletId() {
|
|
739
919
|
return this.walletId;
|
|
740
920
|
}
|
|
921
|
+
/**
|
|
922
|
+
* Add event listener - stores callback and ensures current provider forwards events to ProviderManager
|
|
923
|
+
*/
|
|
924
|
+
on(event, callback) {
|
|
925
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Adding event listener", { event });
|
|
926
|
+
if (!this.eventListeners.has(event)) {
|
|
927
|
+
this.eventListeners.set(event, /* @__PURE__ */ new Set());
|
|
928
|
+
}
|
|
929
|
+
this.eventListeners.get(event).add(callback);
|
|
930
|
+
this.ensureProviderEventForwarding();
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Remove event listener
|
|
934
|
+
*/
|
|
935
|
+
off(event, callback) {
|
|
936
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Removing event listener", { event });
|
|
937
|
+
if (this.eventListeners.has(event)) {
|
|
938
|
+
this.eventListeners.get(event).delete(callback);
|
|
939
|
+
if (this.eventListeners.get(event).size === 0) {
|
|
940
|
+
this.eventListeners.delete(event);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Emit event to all registered callbacks
|
|
946
|
+
*/
|
|
947
|
+
emit(event, data) {
|
|
948
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Emitting event to stored callbacks", {
|
|
949
|
+
event,
|
|
950
|
+
listenerCount: this.eventListeners.get(event)?.size || 0,
|
|
951
|
+
data
|
|
952
|
+
});
|
|
953
|
+
const listeners = this.eventListeners.get(event);
|
|
954
|
+
if (listeners && listeners.size > 0) {
|
|
955
|
+
listeners.forEach((callback) => {
|
|
956
|
+
try {
|
|
957
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Calling stored callback for event", { event });
|
|
958
|
+
callback(data);
|
|
959
|
+
} catch (error) {
|
|
960
|
+
debug.error(DebugCategory.PROVIDER_MANAGER, "Event callback error", { event, error });
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
} else {
|
|
964
|
+
debug.warn(DebugCategory.PROVIDER_MANAGER, "No stored callbacks for event", { event });
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Ensure current provider forwards its events to this ProviderManager
|
|
969
|
+
* Only sets up forwarding once per provider instance to avoid accumulation
|
|
970
|
+
*/
|
|
971
|
+
ensureProviderEventForwarding() {
|
|
972
|
+
if (!this.currentProvider || !("on" in this.currentProvider)) {
|
|
973
|
+
debug.warn(DebugCategory.PROVIDER_MANAGER, "Current provider does not support events", {
|
|
974
|
+
providerType: this.getCurrentProviderInfo()?.type
|
|
975
|
+
});
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
if (this.providerForwardingSetup.has(this.currentProvider)) {
|
|
979
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Event forwarding already set up for current provider");
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Setting up event forwarding from current provider");
|
|
983
|
+
const eventsToForward = ["connect_start", "connect", "connect_error", "disconnect", "error"];
|
|
984
|
+
for (const event of eventsToForward) {
|
|
985
|
+
const forwardingCallback = (data) => {
|
|
986
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Forwarding event from provider", { event, data });
|
|
987
|
+
this.emit(event, data);
|
|
988
|
+
};
|
|
989
|
+
debug.log(DebugCategory.PROVIDER_MANAGER, "Attaching forwarding callback for event", { event });
|
|
990
|
+
this.currentProvider.on(event, forwardingCallback);
|
|
991
|
+
}
|
|
992
|
+
this.providerForwardingSetup.add(this.currentProvider);
|
|
993
|
+
}
|
|
741
994
|
/**
|
|
742
995
|
* Set default provider based on initial config
|
|
743
996
|
*/
|
|
@@ -991,6 +1244,38 @@ var BrowserSDK = class {
|
|
|
991
1244
|
getWalletId() {
|
|
992
1245
|
return this.providerManager.getWalletId();
|
|
993
1246
|
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Add event listener for provider events (connect, connect_start, connect_error, disconnect, error)
|
|
1249
|
+
* Works with both embedded and injected providers
|
|
1250
|
+
*/
|
|
1251
|
+
on(event, callback) {
|
|
1252
|
+
debug.log(DebugCategory.BROWSER_SDK, "Adding event listener", { event });
|
|
1253
|
+
this.providerManager.on(event, callback);
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Remove event listener for provider events
|
|
1257
|
+
* Works with both embedded and injected providers
|
|
1258
|
+
*/
|
|
1259
|
+
off(event, callback) {
|
|
1260
|
+
debug.log(DebugCategory.BROWSER_SDK, "Removing event listener", { event });
|
|
1261
|
+
this.providerManager.off(event, callback);
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Attempt auto-connection using existing session
|
|
1265
|
+
* Should be called after setting up event listeners
|
|
1266
|
+
* Only works with embedded providers
|
|
1267
|
+
*/
|
|
1268
|
+
async autoConnect() {
|
|
1269
|
+
debug.log(DebugCategory.BROWSER_SDK, "Attempting auto-connect");
|
|
1270
|
+
const currentProvider = this.providerManager.getCurrentProvider();
|
|
1271
|
+
if (currentProvider && "autoConnect" in currentProvider) {
|
|
1272
|
+
await currentProvider.autoConnect();
|
|
1273
|
+
} else {
|
|
1274
|
+
debug.warn(DebugCategory.BROWSER_SDK, "Current provider does not support auto-connect", {
|
|
1275
|
+
providerType: this.getCurrentProviderInfo()?.type
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
994
1279
|
};
|
|
995
1280
|
|
|
996
1281
|
// src/index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phantom/browser-sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Browser SDK for Phantom Wallet with unified interface",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@phantom/browser-injected-sdk": "^0.0.9",
|
|
33
33
|
"@phantom/client": "^0.1.8",
|
|
34
34
|
"@phantom/constants": "^0.0.3",
|
|
35
|
-
"@phantom/embedded-provider-core": "^0.1.
|
|
35
|
+
"@phantom/embedded-provider-core": "^0.1.6",
|
|
36
36
|
"@phantom/indexed-db-stamper": "^0.1.4",
|
|
37
37
|
"@phantom/parsers": "^0.0.7",
|
|
38
38
|
"axios": "^1.10.0",
|