@phantom/embedded-provider-core 1.0.2 → 1.0.4

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/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/embedded-provider.ts
2
2
  import { base64urlEncode, stringToBase64url } from "@phantom/base64url";
3
- import { AddressType, PhantomClient, SpendingLimitError } from "@phantom/client";
3
+ import { AddressType, PhantomClient, SpendingLimitError, isAuthenticationError } from "@phantom/client";
4
4
  import {
5
5
  parseSignMessageResponse,
6
6
  parseTransactionResponse,
@@ -24,21 +24,19 @@ var EmbeddedSolanaChain = class {
24
24
  constructor(provider) {
25
25
  this.provider = provider;
26
26
  this.currentNetworkId = NetworkId.SOLANA_MAINNET;
27
- this._connected = false;
28
27
  this._publicKey = null;
29
28
  this.eventEmitter = new EventEmitter();
30
29
  this.setupEventListeners();
31
30
  this.syncInitialState();
32
31
  }
33
- // Wallet adapter compliant properties
34
- get connected() {
35
- return this._connected;
36
- }
37
32
  get publicKey() {
38
33
  return this._publicKey;
39
34
  }
35
+ get isConnected() {
36
+ return this._publicKey !== null;
37
+ }
40
38
  ensureConnected() {
41
- if (!this.provider.isConnected()) {
39
+ if (!this.isConnected) {
42
40
  throw new Error("Solana chain not available. Ensure SDK is connected.");
43
41
  }
44
42
  }
@@ -96,7 +94,7 @@ var EmbeddedSolanaChain = class {
96
94
  const solanaAddr = addresses.find((a) => a.addressType === "Solana");
97
95
  if (!solanaAddr)
98
96
  throw new Error("No Solana address found");
99
- this.updateConnectionState(true, solanaAddr.address);
97
+ this._publicKey = solanaAddr.address;
100
98
  return Promise.resolve({ publicKey: solanaAddr.address });
101
99
  }
102
100
  async disconnect() {
@@ -106,41 +104,25 @@ var EmbeddedSolanaChain = class {
106
104
  this.currentNetworkId = network === "mainnet" ? NetworkId.SOLANA_MAINNET : NetworkId.SOLANA_DEVNET;
107
105
  return Promise.resolve();
108
106
  }
109
- getPublicKey() {
110
- if (!this.provider.isConnected())
111
- return Promise.resolve(null);
112
- const addresses = this.provider.getAddresses();
113
- const solanaAddr = addresses.find((a) => a.addressType === "Solana");
114
- return Promise.resolve(solanaAddr?.address || null);
115
- }
116
- isConnected() {
117
- return this._connected && this.provider.isConnected();
118
- }
119
107
  setupEventListeners() {
120
108
  this.provider.on("connect", (data) => {
121
109
  const solanaAddress = data.addresses?.find((addr) => addr.addressType === "Solana");
122
110
  if (solanaAddress) {
123
- this.updateConnectionState(true, solanaAddress.address);
111
+ this._publicKey = solanaAddress.address;
124
112
  this.eventEmitter.emit("connect", solanaAddress.address);
125
113
  }
126
114
  });
127
115
  this.provider.on("disconnect", () => {
128
- this.updateConnectionState(false, null);
116
+ this._publicKey = null;
129
117
  this.eventEmitter.emit("disconnect");
130
118
  });
131
119
  }
132
120
  syncInitialState() {
133
- if (this.provider.isConnected()) {
134
- const addresses = this.provider.getAddresses();
135
- const solanaAddress = addresses.find((a) => a.addressType === "Solana");
136
- if (solanaAddress) {
137
- this.updateConnectionState(true, solanaAddress.address);
138
- }
139
- }
140
- }
141
- updateConnectionState(connected, publicKey) {
142
- this._connected = connected;
143
- this._publicKey = publicKey;
121
+ if (!this.provider.isConnected())
122
+ return;
123
+ const addresses = this.provider.getAddresses();
124
+ const solanaAddr = addresses.find((a) => a.addressType === "Solana");
125
+ this._publicKey = solanaAddr?.address || null;
144
126
  }
145
127
  // Event methods for interface compliance
146
128
  on(event, listener) {
@@ -158,16 +140,11 @@ var EmbeddedEthereumChain = class {
158
140
  constructor(provider) {
159
141
  this.provider = provider;
160
142
  this.currentNetworkId = NetworkId2.ETHEREUM_MAINNET;
161
- this._connected = false;
162
143
  this._accounts = [];
163
144
  this.eventEmitter = new EventEmitter2();
164
145
  this.setupEventListeners();
165
146
  this.syncInitialState();
166
147
  }
167
- // EIP-1193 compliant properties
168
- get connected() {
169
- return this._connected;
170
- }
171
148
  get chainId() {
172
149
  const chainId = networkIdToChainId(this.currentNetworkId) || 1;
173
150
  return `0x${chainId.toString(16)}`;
@@ -191,7 +168,7 @@ var EmbeddedEthereumChain = class {
191
168
  }
192
169
  const addresses = this.provider.getAddresses();
193
170
  const ethAddresses = addresses.filter((a) => a.addressType === "Ethereum").map((a) => a.address);
194
- this.updateConnectionState(true, ethAddresses);
171
+ this.updateConnectionState(ethAddresses);
195
172
  return Promise.resolve(ethAddresses);
196
173
  }
197
174
  async disconnect() {
@@ -257,19 +234,19 @@ var EmbeddedEthereumChain = class {
257
234
  return this.request({ method: "eth_accounts" });
258
235
  }
259
236
  isConnected() {
260
- return this._connected && this.provider.isConnected();
237
+ return this.provider.isConnected() && this._accounts.length > 0;
261
238
  }
262
239
  setupEventListeners() {
263
240
  this.provider.on("connect", (data) => {
264
241
  const ethAddresses = data.addresses?.filter((addr) => addr.addressType === "Ethereum")?.map((addr) => addr.address) || [];
265
242
  if (ethAddresses.length > 0) {
266
- this.updateConnectionState(true, ethAddresses);
243
+ this.updateConnectionState(ethAddresses);
267
244
  this.eventEmitter.emit("connect", { chainId: this.chainId });
268
245
  this.eventEmitter.emit("accountsChanged", ethAddresses);
269
246
  }
270
247
  });
271
248
  this.provider.on("disconnect", () => {
272
- this.updateConnectionState(false, []);
249
+ this.updateConnectionState([]);
273
250
  this.eventEmitter.emit("disconnect", { code: 4900, message: "Provider disconnected" });
274
251
  this.eventEmitter.emit("accountsChanged", []);
275
252
  });
@@ -279,12 +256,11 @@ var EmbeddedEthereumChain = class {
279
256
  const addresses = this.provider.getAddresses();
280
257
  const ethAddresses = addresses.filter((a) => a.addressType === "Ethereum").map((a) => a.address);
281
258
  if (ethAddresses.length > 0) {
282
- this.updateConnectionState(true, ethAddresses);
259
+ this.updateConnectionState(ethAddresses);
283
260
  }
284
261
  }
285
262
  }
286
- updateConnectionState(connected, accounts) {
287
- this._connected = connected;
263
+ updateConnectionState(accounts) {
288
264
  this._accounts = accounts;
289
265
  }
290
266
  async handleEmbeddedRequest(args) {
@@ -512,7 +488,8 @@ var EmbeddedProvider = class {
512
488
  });
513
489
  if (session.status !== "completed") {
514
490
  const urlSessionId = this.urlParamsAccessor.getParam("session_id");
515
- if (session.status === "pending" && !urlSessionId) {
491
+ const urlCode = this.urlParamsAccessor.getParam("code");
492
+ if (session.status === "pending" && !urlSessionId && !urlCode) {
516
493
  this.logger.warn("EMBEDDED_PROVIDER", "Session mismatch detected - pending session without redirect context", {
517
494
  sessionId: session.sessionId,
518
495
  status: session.status
@@ -585,7 +562,7 @@ var EmbeddedProvider = class {
585
562
  }
586
563
  this.logger.log("EMBEDDED_PROVIDER", "No completed session found, checking for redirect resume");
587
564
  if (this.authProvider.resumeAuthFromRedirect) {
588
- const authResult = this.authProvider.resumeAuthFromRedirect(session.authProvider);
565
+ const authResult = await this.authProvider.resumeAuthFromRedirect(session.authProvider);
589
566
  if (authResult) {
590
567
  this.logger.info("EMBEDDED_PROVIDER", "Resuming from redirect", {
591
568
  walletId: authResult.walletId,
@@ -748,7 +725,7 @@ var EmbeddedProvider = class {
748
725
  authenticatorName: `auth-${shortPubKey}`,
749
726
  authenticatorKind: "keypair",
750
727
  publicKey: base64urlPublicKey,
751
- algorithm: "Ed25519",
728
+ algorithm: this.stamper.algorithm,
752
729
  expiresInMs
753
730
  }
754
731
  ]
@@ -864,6 +841,21 @@ var EmbeddedProvider = class {
864
841
  });
865
842
  }
866
843
  }
844
+ /**
845
+ * Handles errors from signing operations.
846
+ * Disconnects the user if the server returns a 401/403 (revoked or expired authenticator).
847
+ */
848
+ async handleSigningError(error) {
849
+ if (isAuthenticationError(error)) {
850
+ this.logger.warn("EMBEDDED_PROVIDER", "Authenticator rejected by server (401/403), disconnecting", { error });
851
+ await this.disconnect(false);
852
+ throw new Error("Authenticator revoked");
853
+ }
854
+ if (error instanceof SpendingLimitError) {
855
+ this.emit("spending_limit_reached", { error });
856
+ }
857
+ throw error;
858
+ }
867
859
  async signMessage(params) {
868
860
  if (!this.client || !this.walletId) {
869
861
  throw new Error("Not connected");
@@ -880,7 +872,7 @@ var EmbeddedProvider = class {
880
872
  message: params.message,
881
873
  networkId: params.networkId,
882
874
  derivationIndex
883
- });
875
+ }).catch((error) => this.handleSigningError(error));
884
876
  this.logger.info("EMBEDDED_PROVIDER", "Message signed successfully", {
885
877
  walletId: this.walletId,
886
878
  message: params.message
@@ -913,7 +905,7 @@ var EmbeddedProvider = class {
913
905
  message: base64UrlMessage,
914
906
  networkId: params.networkId,
915
907
  derivationIndex
916
- });
908
+ }).catch((error) => this.handleSigningError(error));
917
909
  this.logger.info("EMBEDDED_PROVIDER", "Message signed successfully", {
918
910
  walletId: this.walletId,
919
911
  message: params.message
@@ -936,7 +928,7 @@ var EmbeddedProvider = class {
936
928
  typedData: params.typedData,
937
929
  networkId: params.networkId,
938
930
  derivationIndex
939
- });
931
+ }).catch((error) => this.handleSigningError(error));
940
932
  this.logger.info("EMBEDDED_PROVIDER", "Typed data signed successfully", {
941
933
  walletId: this.walletId
942
934
  });
@@ -973,7 +965,7 @@ var EmbeddedProvider = class {
973
965
  networkId: params.networkId,
974
966
  derivationIndex,
975
967
  account
976
- });
968
+ }).catch((error) => this.handleSigningError(error));
977
969
  this.logger.info("EMBEDDED_PROVIDER", "Transaction signed successfully", {
978
970
  walletId: this.walletId,
979
971
  networkId: params.networkId,
@@ -1006,21 +998,13 @@ var EmbeddedProvider = class {
1006
998
  if (!account) {
1007
999
  throw new Error(`No address found for network ${params.networkId}`);
1008
1000
  }
1009
- let rawResponse;
1010
- try {
1011
- rawResponse = await this.client.signAndSendTransaction({
1012
- walletId: this.walletId,
1013
- transaction: transactionPayload,
1014
- networkId: params.networkId,
1015
- derivationIndex,
1016
- account
1017
- });
1018
- } catch (error) {
1019
- if (error instanceof SpendingLimitError) {
1020
- this.emit("spending_limit_reached", { error });
1021
- }
1022
- throw error;
1023
- }
1001
+ const rawResponse = await this.client.signAndSendTransaction({
1002
+ walletId: this.walletId,
1003
+ transaction: transactionPayload,
1004
+ networkId: params.networkId,
1005
+ derivationIndex,
1006
+ account
1007
+ }).catch((error) => this.handleSigningError(error));
1024
1008
  this.logger.info("EMBEDDED_PROVIDER", "Transaction signed and sent successfully", {
1025
1009
  walletId: this.walletId,
1026
1010
  networkId: params.networkId,
@@ -1204,8 +1188,9 @@ var EmbeddedProvider = class {
1204
1188
  // OAuth session management - defaults to allowing refresh unless user explicitly logged out
1205
1189
  clearPreviousSession: shouldClearPreviousSession,
1206
1190
  // true only after logout
1207
- allowRefresh: !shouldClearPreviousSession
1191
+ allowRefresh: !shouldClearPreviousSession,
1208
1192
  // false only after logout
1193
+ algorithm: this.stamper.algorithm
1209
1194
  });
1210
1195
  if (authResult && "walletId" in authResult) {
1211
1196
  this.logger.info("EMBEDDED_PROVIDER", "Authentication completed after redirect", {
@@ -1218,6 +1203,7 @@ var EmbeddedProvider = class {
1218
1203
  tempSession.authProvider = authResult.provider || tempSession.authProvider;
1219
1204
  tempSession.accountDerivationIndex = authResult.accountDerivationIndex;
1220
1205
  tempSession.authUserId = authResult.authUserId;
1206
+ tempSession.bearerToken = authResult.bearerToken;
1221
1207
  tempSession.status = "completed";
1222
1208
  tempSession.lastUsed = Date.now();
1223
1209
  if (authResult.expiresInMs > 0) {
@@ -1247,6 +1233,7 @@ var EmbeddedProvider = class {
1247
1233
  session.organizationId = authResult.organizationId;
1248
1234
  session.accountDerivationIndex = authResult.accountDerivationIndex;
1249
1235
  session.authUserId = authResult.authUserId;
1236
+ session.bearerToken = authResult.bearerToken;
1250
1237
  session.status = "completed";
1251
1238
  session.lastUsed = Date.now();
1252
1239
  if (authResult.expiresInMs > 0) {
@@ -1316,7 +1303,8 @@ var EmbeddedProvider = class {
1316
1303
  organizationId: session.organizationId,
1317
1304
  headers: {
1318
1305
  ...this.platform.analyticsHeaders || {},
1319
- ...session.authUserId ? { "x-auth-user-id": session.authUserId } : {}
1306
+ ...session.authUserId ? { "x-auth-user-id": session.authUserId } : {},
1307
+ ...session.bearerToken ? { Authorization: session.bearerToken } : {}
1320
1308
  }
1321
1309
  },
1322
1310
  this.stamper