@phantom/react-native-sdk 0.1.0 → 0.1.1

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.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode } from 'react';
3
3
  import * as _phantom_embedded_provider_core from '@phantom/embedded-provider-core';
4
- import { EmbeddedProviderConfig, AuthOptions, EmbeddedProvider, WalletAddress, ConnectResult, SignMessageParams, SignAndSendTransactionParams, SignedTransaction } from '@phantom/embedded-provider-core';
5
- export { ConnectResult, SignAndSendTransactionParams, SignMessageParams, SignedTransaction, WalletAddress } from '@phantom/embedded-provider-core';
6
- export { AddressType, NetworkId } from '@phantom/client';
4
+ import { EmbeddedProviderConfig, AuthOptions, EmbeddedProvider, WalletAddress, ConnectResult, SignMessageParams, SignMessageResult, SignAndSendTransactionParams, SignedTransaction } from '@phantom/embedded-provider-core';
5
+ export { ConnectResult, SignAndSendTransactionParams, SignMessageParams, SignMessageResult, SignedTransaction, WalletAddress } from '@phantom/embedded-provider-core';
6
+ export { AddressType } from '@phantom/client';
7
+ export { NetworkId } from '@phantom/constants';
7
8
 
8
9
  interface PhantomProviderConfig extends Omit<EmbeddedProviderConfig, "authOptions"> {
9
10
  /** Custom URL scheme for your app (e.g., "myapp") */
@@ -66,7 +67,7 @@ declare function useAccounts(): {
66
67
  };
67
68
 
68
69
  declare function useSignMessage(): {
69
- signMessage: (params: SignMessageParams) => Promise<string>;
70
+ signMessage: (params: SignMessageParams) => Promise<SignMessageResult>;
70
71
  isSigning: boolean;
71
72
  error: Error | null;
72
73
  };
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  AddressType: () => import_client.AddressType,
34
- NetworkId: () => import_client.NetworkId,
34
+ NetworkId: () => import_constants.NetworkId,
35
35
  PhantomProvider: () => PhantomProvider,
36
36
  useAccounts: () => useAccounts,
37
37
  useConnect: () => useConnect,
@@ -96,22 +96,52 @@ var ExpoSecureStorage = class {
96
96
 
97
97
  // src/providers/embedded/auth.ts
98
98
  var WebBrowser = __toESM(require("expo-web-browser"));
99
+ var DEFAULT_AUTH_URL = "https://auth.phantom.app";
99
100
  var ExpoAuthProvider = class {
100
101
  async authenticate(options) {
101
102
  if ("jwtToken" in options) {
102
103
  return;
103
104
  }
104
- const { authUrl, redirectUrl } = options;
105
- if (!authUrl || !redirectUrl) {
106
- throw new Error("authUrl and redirectUrl are required for web browser authentication");
105
+ const phantomOptions = options;
106
+ const { authUrl, redirectUrl, organizationId, parentOrganizationId, sessionId, provider, customAuthData } = phantomOptions;
107
+ if (!redirectUrl) {
108
+ throw new Error("redirectUrl is required for web browser authentication");
109
+ }
110
+ if (!organizationId || !sessionId) {
111
+ throw new Error("organizationId and sessionId are required for authentication");
107
112
  }
108
113
  try {
114
+ const baseUrl = authUrl || DEFAULT_AUTH_URL;
115
+ const params = new URLSearchParams({
116
+ organization_id: organizationId,
117
+ parent_organization_id: parentOrganizationId,
118
+ redirect_uri: redirectUrl,
119
+ session_id: sessionId,
120
+ clear_previous_session: "true"
121
+ });
122
+ if (provider) {
123
+ console.log("[ExpoAuthProvider] Provider specified, will skip selection", { provider });
124
+ params.append("provider", provider);
125
+ } else {
126
+ console.log("[ExpoAuthProvider] No provider specified, defaulting to Google");
127
+ params.append("provider", "google");
128
+ }
129
+ if (customAuthData) {
130
+ console.log("[ExpoAuthProvider] Adding custom auth data");
131
+ params.append("authData", JSON.stringify(customAuthData));
132
+ }
133
+ const fullAuthUrl = `${baseUrl}?${params.toString()}`;
109
134
  console.log("[ExpoAuthProvider] Starting authentication", {
110
- authUrl: authUrl.substring(0, 50) + "...",
111
- redirectUrl
135
+ baseUrl,
136
+ redirectUrl,
137
+ organizationId,
138
+ parentOrganizationId,
139
+ sessionId,
140
+ provider,
141
+ hasCustomData: !!customAuthData
112
142
  });
113
143
  await WebBrowser.warmUpAsync();
114
- const result = await WebBrowser.openAuthSessionAsync(authUrl, redirectUrl, {
144
+ const result = await WebBrowser.openAuthSessionAsync(fullAuthUrl, redirectUrl, {
115
145
  // Use system browser on iOS for ASWebAuthenticationSession
116
146
  preferEphemeralSession: false
117
147
  });
@@ -121,19 +151,14 @@ var ExpoAuthProvider = class {
121
151
  });
122
152
  if (result.type === "success" && result.url) {
123
153
  const url = new URL(result.url);
124
- const walletId = url.searchParams.get("walletId");
125
- const provider = url.searchParams.get("provider");
154
+ const walletId = url.searchParams.get("wallet_id");
155
+ const provider2 = url.searchParams.get("provider");
126
156
  if (!walletId) {
127
157
  throw new Error("Authentication failed: no walletId in redirect URL");
128
158
  }
129
- const userInfo = {};
130
- url.searchParams.forEach((value, key) => {
131
- userInfo[key] = value;
132
- });
133
159
  return {
134
160
  walletId,
135
- provider: provider || void 0,
136
- userInfo
161
+ provider: provider2 || void 0
137
162
  };
138
163
  } else if (result.type === "cancel") {
139
164
  throw new Error("User cancelled authentication");
@@ -247,7 +272,136 @@ var ExpoLogger = class {
247
272
  }
248
273
  };
249
274
 
275
+ // src/providers/embedded/stamper.ts
276
+ var SecureStore2 = __toESM(require("expo-secure-store"));
277
+ var import_api_key_stamper = require("@phantom/api-key-stamper");
278
+ var import_crypto = require("@phantom/crypto");
279
+ var import_base64url = require("@phantom/base64url");
280
+ var import_sdk_types = require("@phantom/sdk-types");
281
+ var ReactNativeStamper = class {
282
+ constructor(config = {}) {
283
+ this.keyInfo = null;
284
+ this.algorithm = import_sdk_types.Algorithm.ed25519;
285
+ this.keyPrefix = config.keyPrefix || "phantom-rn-stamper";
286
+ this.organizationId = config.organizationId || "default";
287
+ }
288
+ /**
289
+ * Initialize the stamper and generate/load cryptographic keys
290
+ */
291
+ async init() {
292
+ const storedSecretKey = await this.getStoredSecretKey();
293
+ if (storedSecretKey) {
294
+ const keyInfo2 = await this.getStoredKeyInfo();
295
+ if (keyInfo2) {
296
+ this.keyInfo = keyInfo2;
297
+ return keyInfo2;
298
+ }
299
+ }
300
+ const keyInfo = await this.generateAndStoreKeyPair();
301
+ this.keyInfo = keyInfo;
302
+ return keyInfo;
303
+ }
304
+ /**
305
+ * Get the current key information
306
+ */
307
+ getKeyInfo() {
308
+ return this.keyInfo;
309
+ }
310
+ /**
311
+ * Generate and store a new key pair, replacing any existing keys
312
+ */
313
+ async resetKeyPair() {
314
+ await this.clear();
315
+ const keyInfo = await this.generateAndStoreKeyPair();
316
+ this.keyInfo = keyInfo;
317
+ return keyInfo;
318
+ }
319
+ /**
320
+ * Create X-Phantom-Stamp header value using stored secret key
321
+ * @param data - Data to sign (Buffer)
322
+ * @returns Complete X-Phantom-Stamp header value
323
+ */
324
+ async stamp({
325
+ data
326
+ }) {
327
+ if (!this.keyInfo) {
328
+ throw new Error("Stamper not initialized. Call init() first.");
329
+ }
330
+ const storedSecretKey = await this.getStoredSecretKey();
331
+ if (!storedSecretKey) {
332
+ throw new Error("Secret key not found in secure storage");
333
+ }
334
+ const apiKeyStamper = new import_api_key_stamper.ApiKeyStamper({ apiSecretKey: storedSecretKey });
335
+ return await apiKeyStamper.stamp({ data });
336
+ }
337
+ /**
338
+ * Clear all stored keys from SecureStore
339
+ */
340
+ async clear() {
341
+ const infoKey = this.getInfoKey();
342
+ const secretKey = this.getSecretKey();
343
+ try {
344
+ await SecureStore2.deleteItemAsync(infoKey);
345
+ } catch (error) {
346
+ }
347
+ try {
348
+ await SecureStore2.deleteItemAsync(secretKey);
349
+ } catch (error) {
350
+ }
351
+ this.keyInfo = null;
352
+ }
353
+ async generateAndStoreKeyPair() {
354
+ const keypair = (0, import_crypto.generateKeyPair)();
355
+ const keyId = this.createKeyId(keypair.publicKey);
356
+ const keyInfo = {
357
+ keyId,
358
+ publicKey: keypair.publicKey
359
+ };
360
+ await this.storeKeyPair(keypair.secretKey, keyInfo);
361
+ return keyInfo;
362
+ }
363
+ createKeyId(publicKey) {
364
+ return (0, import_base64url.base64urlEncode)(new TextEncoder().encode(publicKey)).substring(0, 16);
365
+ }
366
+ async storeKeyPair(secretKey, keyInfo) {
367
+ const infoKey = this.getInfoKey();
368
+ const secretKeyName = this.getSecretKey();
369
+ await SecureStore2.setItemAsync(infoKey, JSON.stringify(keyInfo), {
370
+ requireAuthentication: false
371
+ });
372
+ await SecureStore2.setItemAsync(secretKeyName, secretKey, {
373
+ requireAuthentication: false
374
+ });
375
+ }
376
+ async getStoredKeyInfo() {
377
+ try {
378
+ const infoKey = this.getInfoKey();
379
+ const storedInfo = await SecureStore2.getItemAsync(infoKey);
380
+ if (storedInfo) {
381
+ return JSON.parse(storedInfo);
382
+ }
383
+ } catch (error) {
384
+ }
385
+ return null;
386
+ }
387
+ async getStoredSecretKey() {
388
+ try {
389
+ const secretKeyName = this.getSecretKey();
390
+ return await SecureStore2.getItemAsync(secretKeyName);
391
+ } catch (error) {
392
+ return null;
393
+ }
394
+ }
395
+ getInfoKey() {
396
+ return `${this.keyPrefix}-${this.organizationId}-info`;
397
+ }
398
+ getSecretKey() {
399
+ return `${this.keyPrefix}-${this.organizationId}-secret`;
400
+ }
401
+ };
402
+
250
403
  // src/PhantomProvider.tsx
404
+ var import_react_native2 = require("react-native");
251
405
  var import_jsx_runtime = require("react/jsx-runtime");
252
406
  var PhantomContext = (0, import_react.createContext)(void 0);
253
407
  function PhantomProvider({ children, config }) {
@@ -268,10 +422,16 @@ function PhantomProvider({ children, config }) {
268
422
  const authProvider = new ExpoAuthProvider();
269
423
  const urlParamsAccessor = new ExpoURLParamsAccessor();
270
424
  const logger = new ExpoLogger(config.debug);
425
+ const stamper = new ReactNativeStamper({
426
+ keyPrefix: `phantom-rn-${config.organizationId}`,
427
+ organizationId: config.organizationId
428
+ });
271
429
  const platform = {
272
430
  storage,
273
431
  authProvider,
274
- urlParamsAccessor
432
+ urlParamsAccessor,
433
+ stamper,
434
+ name: `${import_react_native2.Platform.OS}-${import_react_native2.Platform.Version}`
275
435
  };
276
436
  return new import_embedded_provider_core.EmbeddedProvider(embeddedConfig, platform, logger);
277
437
  }, [config]);
@@ -465,6 +625,7 @@ function useSignAndSendTransaction() {
465
625
 
466
626
  // src/index.ts
467
627
  var import_client = require("@phantom/client");
628
+ var import_constants = require("@phantom/constants");
468
629
  // Annotate the CommonJS export names for ESM import in node:
469
630
  0 && (module.exports = {
470
631
  AddressType,
package/dist/index.mjs CHANGED
@@ -52,22 +52,52 @@ var ExpoSecureStorage = class {
52
52
 
53
53
  // src/providers/embedded/auth.ts
54
54
  import * as WebBrowser from "expo-web-browser";
55
+ var DEFAULT_AUTH_URL = "https://auth.phantom.app";
55
56
  var ExpoAuthProvider = class {
56
57
  async authenticate(options) {
57
58
  if ("jwtToken" in options) {
58
59
  return;
59
60
  }
60
- const { authUrl, redirectUrl } = options;
61
- if (!authUrl || !redirectUrl) {
62
- throw new Error("authUrl and redirectUrl are required for web browser authentication");
61
+ const phantomOptions = options;
62
+ const { authUrl, redirectUrl, organizationId, parentOrganizationId, sessionId, provider, customAuthData } = phantomOptions;
63
+ if (!redirectUrl) {
64
+ throw new Error("redirectUrl is required for web browser authentication");
65
+ }
66
+ if (!organizationId || !sessionId) {
67
+ throw new Error("organizationId and sessionId are required for authentication");
63
68
  }
64
69
  try {
70
+ const baseUrl = authUrl || DEFAULT_AUTH_URL;
71
+ const params = new URLSearchParams({
72
+ organization_id: organizationId,
73
+ parent_organization_id: parentOrganizationId,
74
+ redirect_uri: redirectUrl,
75
+ session_id: sessionId,
76
+ clear_previous_session: "true"
77
+ });
78
+ if (provider) {
79
+ console.log("[ExpoAuthProvider] Provider specified, will skip selection", { provider });
80
+ params.append("provider", provider);
81
+ } else {
82
+ console.log("[ExpoAuthProvider] No provider specified, defaulting to Google");
83
+ params.append("provider", "google");
84
+ }
85
+ if (customAuthData) {
86
+ console.log("[ExpoAuthProvider] Adding custom auth data");
87
+ params.append("authData", JSON.stringify(customAuthData));
88
+ }
89
+ const fullAuthUrl = `${baseUrl}?${params.toString()}`;
65
90
  console.log("[ExpoAuthProvider] Starting authentication", {
66
- authUrl: authUrl.substring(0, 50) + "...",
67
- redirectUrl
91
+ baseUrl,
92
+ redirectUrl,
93
+ organizationId,
94
+ parentOrganizationId,
95
+ sessionId,
96
+ provider,
97
+ hasCustomData: !!customAuthData
68
98
  });
69
99
  await WebBrowser.warmUpAsync();
70
- const result = await WebBrowser.openAuthSessionAsync(authUrl, redirectUrl, {
100
+ const result = await WebBrowser.openAuthSessionAsync(fullAuthUrl, redirectUrl, {
71
101
  // Use system browser on iOS for ASWebAuthenticationSession
72
102
  preferEphemeralSession: false
73
103
  });
@@ -77,19 +107,14 @@ var ExpoAuthProvider = class {
77
107
  });
78
108
  if (result.type === "success" && result.url) {
79
109
  const url = new URL(result.url);
80
- const walletId = url.searchParams.get("walletId");
81
- const provider = url.searchParams.get("provider");
110
+ const walletId = url.searchParams.get("wallet_id");
111
+ const provider2 = url.searchParams.get("provider");
82
112
  if (!walletId) {
83
113
  throw new Error("Authentication failed: no walletId in redirect URL");
84
114
  }
85
- const userInfo = {};
86
- url.searchParams.forEach((value, key) => {
87
- userInfo[key] = value;
88
- });
89
115
  return {
90
116
  walletId,
91
- provider: provider || void 0,
92
- userInfo
117
+ provider: provider2 || void 0
93
118
  };
94
119
  } else if (result.type === "cancel") {
95
120
  throw new Error("User cancelled authentication");
@@ -203,7 +228,136 @@ var ExpoLogger = class {
203
228
  }
204
229
  };
205
230
 
231
+ // src/providers/embedded/stamper.ts
232
+ import * as SecureStore2 from "expo-secure-store";
233
+ import { ApiKeyStamper } from "@phantom/api-key-stamper";
234
+ import { generateKeyPair } from "@phantom/crypto";
235
+ import { base64urlEncode } from "@phantom/base64url";
236
+ import { Algorithm } from "@phantom/sdk-types";
237
+ var ReactNativeStamper = class {
238
+ constructor(config = {}) {
239
+ this.keyInfo = null;
240
+ this.algorithm = Algorithm.ed25519;
241
+ this.keyPrefix = config.keyPrefix || "phantom-rn-stamper";
242
+ this.organizationId = config.organizationId || "default";
243
+ }
244
+ /**
245
+ * Initialize the stamper and generate/load cryptographic keys
246
+ */
247
+ async init() {
248
+ const storedSecretKey = await this.getStoredSecretKey();
249
+ if (storedSecretKey) {
250
+ const keyInfo2 = await this.getStoredKeyInfo();
251
+ if (keyInfo2) {
252
+ this.keyInfo = keyInfo2;
253
+ return keyInfo2;
254
+ }
255
+ }
256
+ const keyInfo = await this.generateAndStoreKeyPair();
257
+ this.keyInfo = keyInfo;
258
+ return keyInfo;
259
+ }
260
+ /**
261
+ * Get the current key information
262
+ */
263
+ getKeyInfo() {
264
+ return this.keyInfo;
265
+ }
266
+ /**
267
+ * Generate and store a new key pair, replacing any existing keys
268
+ */
269
+ async resetKeyPair() {
270
+ await this.clear();
271
+ const keyInfo = await this.generateAndStoreKeyPair();
272
+ this.keyInfo = keyInfo;
273
+ return keyInfo;
274
+ }
275
+ /**
276
+ * Create X-Phantom-Stamp header value using stored secret key
277
+ * @param data - Data to sign (Buffer)
278
+ * @returns Complete X-Phantom-Stamp header value
279
+ */
280
+ async stamp({
281
+ data
282
+ }) {
283
+ if (!this.keyInfo) {
284
+ throw new Error("Stamper not initialized. Call init() first.");
285
+ }
286
+ const storedSecretKey = await this.getStoredSecretKey();
287
+ if (!storedSecretKey) {
288
+ throw new Error("Secret key not found in secure storage");
289
+ }
290
+ const apiKeyStamper = new ApiKeyStamper({ apiSecretKey: storedSecretKey });
291
+ return await apiKeyStamper.stamp({ data });
292
+ }
293
+ /**
294
+ * Clear all stored keys from SecureStore
295
+ */
296
+ async clear() {
297
+ const infoKey = this.getInfoKey();
298
+ const secretKey = this.getSecretKey();
299
+ try {
300
+ await SecureStore2.deleteItemAsync(infoKey);
301
+ } catch (error) {
302
+ }
303
+ try {
304
+ await SecureStore2.deleteItemAsync(secretKey);
305
+ } catch (error) {
306
+ }
307
+ this.keyInfo = null;
308
+ }
309
+ async generateAndStoreKeyPair() {
310
+ const keypair = generateKeyPair();
311
+ const keyId = this.createKeyId(keypair.publicKey);
312
+ const keyInfo = {
313
+ keyId,
314
+ publicKey: keypair.publicKey
315
+ };
316
+ await this.storeKeyPair(keypair.secretKey, keyInfo);
317
+ return keyInfo;
318
+ }
319
+ createKeyId(publicKey) {
320
+ return base64urlEncode(new TextEncoder().encode(publicKey)).substring(0, 16);
321
+ }
322
+ async storeKeyPair(secretKey, keyInfo) {
323
+ const infoKey = this.getInfoKey();
324
+ const secretKeyName = this.getSecretKey();
325
+ await SecureStore2.setItemAsync(infoKey, JSON.stringify(keyInfo), {
326
+ requireAuthentication: false
327
+ });
328
+ await SecureStore2.setItemAsync(secretKeyName, secretKey, {
329
+ requireAuthentication: false
330
+ });
331
+ }
332
+ async getStoredKeyInfo() {
333
+ try {
334
+ const infoKey = this.getInfoKey();
335
+ const storedInfo = await SecureStore2.getItemAsync(infoKey);
336
+ if (storedInfo) {
337
+ return JSON.parse(storedInfo);
338
+ }
339
+ } catch (error) {
340
+ }
341
+ return null;
342
+ }
343
+ async getStoredSecretKey() {
344
+ try {
345
+ const secretKeyName = this.getSecretKey();
346
+ return await SecureStore2.getItemAsync(secretKeyName);
347
+ } catch (error) {
348
+ return null;
349
+ }
350
+ }
351
+ getInfoKey() {
352
+ return `${this.keyPrefix}-${this.organizationId}-info`;
353
+ }
354
+ getSecretKey() {
355
+ return `${this.keyPrefix}-${this.organizationId}-secret`;
356
+ }
357
+ };
358
+
206
359
  // src/PhantomProvider.tsx
360
+ import { Platform } from "react-native";
207
361
  import { jsx } from "react/jsx-runtime";
208
362
  var PhantomContext = createContext(void 0);
209
363
  function PhantomProvider({ children, config }) {
@@ -224,10 +378,16 @@ function PhantomProvider({ children, config }) {
224
378
  const authProvider = new ExpoAuthProvider();
225
379
  const urlParamsAccessor = new ExpoURLParamsAccessor();
226
380
  const logger = new ExpoLogger(config.debug);
381
+ const stamper = new ReactNativeStamper({
382
+ keyPrefix: `phantom-rn-${config.organizationId}`,
383
+ organizationId: config.organizationId
384
+ });
227
385
  const platform = {
228
386
  storage,
229
387
  authProvider,
230
- urlParamsAccessor
388
+ urlParamsAccessor,
389
+ stamper,
390
+ name: `${Platform.OS}-${Platform.Version}`
231
391
  };
232
392
  return new EmbeddedProvider(embeddedConfig, platform, logger);
233
393
  }, [config]);
@@ -420,7 +580,8 @@ function useSignAndSendTransaction() {
420
580
  }
421
581
 
422
582
  // src/index.ts
423
- import { AddressType, NetworkId } from "@phantom/client";
583
+ import { AddressType } from "@phantom/client";
584
+ import { NetworkId } from "@phantom/constants";
424
585
  export {
425
586
  AddressType,
426
587
  NetworkId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phantom/react-native-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Phantom Wallet SDK for React Native and Expo applications",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -45,8 +45,16 @@
45
45
  "directory": "packages/react-native-sdk"
46
46
  },
47
47
  "dependencies": {
48
- "@phantom/client": "^0.1.3",
49
- "@phantom/embedded-provider-core": "^0.1.1"
48
+ "@phantom/api-key-stamper": "^0.1.2",
49
+ "@phantom/base64url": "^0.1.0",
50
+ "@phantom/client": "^0.1.5",
51
+ "@phantom/constants": "^0.0.2",
52
+ "@phantom/crypto": "^0.1.1",
53
+ "@phantom/embedded-provider-core": "^0.1.2",
54
+ "@phantom/sdk-types": "^0.1.1",
55
+ "@types/bs58": "^5.0.0",
56
+ "bs58": "^6.0.0",
57
+ "buffer": "^6.0.3"
50
58
  },
51
59
  "peerDependencies": {
52
60
  "expo": ">=53.0.0",