@getpara/react-native-wallet 2.0.0-fc.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,21 @@
1
+ https://www.npmjs.com/package/@getpara/react-native-wallet
2
+
3
+ The `@getpara/react-native-wallet` package is Para's React Native SDK for building mobile wallet applications.
4
+
5
+ ###Key Features
6
+
7
+ - Native Passkey Authentication: Uses device biometrics and passkeys for secure wallet access
8
+ - Multi-blockchain Support: Works with EVM, Solana, and Cosmos networks
9
+ - Custom Storage: Supports MMKV for improved performance over AsyncStorage
10
+ - OAuth Integration: Supports social login with Google, Discord, and Farcaster
11
+ - Pregenerated Wallets: Create wallets before user authentication
12
+
13
+ ###Prerequisites
14
+
15
+ To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration.
16
+
17
+ Don't have an API key yet? Request access to the [Developer Portal](https://developer.getpara.com/) to create API keys, manage billing, teams, and more.
18
+
19
+ ###Learn more
20
+
21
+ For more information on Para’s React Native SDK visit the [Para Docs](https://docs.getpara.com/v2/react-native/setup/react-native)
package/dist/config.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Environment } from '@getpara/web-sdk';
2
- declare function getPortalBaseURL(env: Environment): "http://localhost:3003" | "https://app.sandbox.usecapsule.com" | "https://app.beta.usecapsule.com" | "https://app.usecapsule.com";
2
+ declare function getPortalBaseURL(env: Environment): "http://localhost:3003" | "https://app.sandbox.getpara.com" | "https://app.beta.getpara.com" | "https://app.getpara.com";
3
3
  declare function getBaseUrl(env: Environment): string;
4
4
  export declare function getBaseMPCNetworkWSUrl(env: Environment): string;
5
5
  export declare let userManagementServer: string;
package/dist/config.js CHANGED
@@ -5,11 +5,11 @@ function getPortalBaseURL(env) {
5
5
  case Environment.DEV:
6
6
  return 'http://localhost:3003';
7
7
  case Environment.SANDBOX:
8
- return 'https://app.sandbox.usecapsule.com';
8
+ return 'https://app.sandbox.getpara.com';
9
9
  case Environment.BETA:
10
- return 'https://app.beta.usecapsule.com';
10
+ return 'https://app.beta.getpara.com';
11
11
  case Environment.PROD:
12
- return 'https://app.usecapsule.com';
12
+ return 'https://app.getpara.com';
13
13
  default:
14
14
  throw new Error(`env: ${env} not supported`);
15
15
  }
@@ -11,13 +11,14 @@ export declare class ParaMobile extends ParaCore {
11
11
  private relyingPartyId;
12
12
  /**
13
13
  * Creates an instance of ParaMobile.
14
- * @param {Environment} env - The environment to use (DEV, SANDBOX, BETA, or PROD).
14
+ * @param {Environment} env - The environment to use (DEV, SANDBOX, BETA, or PROD). Optional if the apiKey contains an environment prefix (e.g., "prod_your_api_key"). Updated API keys can be found at https://developer.getpara.com.
15
15
  * @param {string} [apiKey] - The API key for authentication.
16
16
  * @param {string} [relyingPartyId] - The relying party ID for WebAuthn.
17
17
  * @param {ConstructorOpts} [opts] - Additional constructor options.
18
18
  */
19
- constructor(env: Environment, apiKey: string, relyingPartyId?: string, opts?: ConstructorOpts);
20
- protected ready(): Promise<void>;
19
+ constructor(env: Environment | undefined, apiKey: string, relyingPartyId?: string, opts?: ConstructorOpts);
20
+ constructor(apiKey: string, relyingPartyId?: string, opts?: ConstructorOpts);
21
+ ready(): Promise<void>;
21
22
  protected getPlatformUtils(): PlatformUtils;
22
23
  isPasskeySupported(): Promise<boolean>;
23
24
  /**
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { ParaCore, Environment, decryptPrivateKeyAndDecryptShare, encryptPrivateKey, getAsymmetricKeyPair, getDerivedPrivateKeyAndDecrypt, getPublicKeyHex, getSHA256HashHex, parseCredentialCreationRes, } from '@getpara/web-sdk';
11
11
  import { ReactNativeUtils } from './ReactNativeUtils.js';
12
12
  import { Passkey, } from 'react-native-passkey';
13
- import { PublicKeyStatus } from '@getpara/user-management-client';
13
+ import { AuthMethodStatus } from '@getpara/user-management-client';
14
14
  import { setEnv } from '../config.js';
15
15
  import base64url from 'base64url';
16
16
  import { webcrypto } from 'crypto';
@@ -24,14 +24,59 @@ const RS256_ALGORITHM = -257;
24
24
  * const para = new ParaMobile(Environment.BETA, "api_key");
25
25
  */
26
26
  export class ParaMobile extends ParaCore {
27
- /**
28
- * Creates an instance of ParaMobile.
29
- * @param {Environment} env - The environment to use (DEV, SANDBOX, BETA, or PROD).
30
- * @param {string} [apiKey] - The API key for authentication.
31
- * @param {string} [relyingPartyId] - The relying party ID for WebAuthn.
32
- * @param {ConstructorOpts} [opts] - Additional constructor options.
33
- */
34
- constructor(env, apiKey, relyingPartyId, opts) {
27
+ constructor(envOrApiKey, apiKeyOrRelyingPartyId, relyingPartyIdOrOpts, optsArg) {
28
+ let env, apiKey, relyingPartyId, opts;
29
+ // Helper function to check if value is a valid Environment
30
+ const isEnvironment = (value) => {
31
+ return Object.values(Environment).includes(value);
32
+ };
33
+ if (arguments.length === 1) {
34
+ // 1-parameter case: (apiKey)
35
+ env = undefined;
36
+ apiKey = envOrApiKey;
37
+ relyingPartyId = undefined;
38
+ opts = undefined;
39
+ }
40
+ else if (arguments.length === 2) {
41
+ if (isEnvironment(envOrApiKey)) {
42
+ // 2-parameter case: (env, apiKey)
43
+ env = envOrApiKey;
44
+ apiKey = apiKeyOrRelyingPartyId;
45
+ relyingPartyId = undefined;
46
+ opts = undefined;
47
+ }
48
+ else {
49
+ // 2-parameter case: (apiKey, relyingPartyId)
50
+ env = undefined;
51
+ apiKey = envOrApiKey;
52
+ relyingPartyId = apiKeyOrRelyingPartyId;
53
+ opts = undefined;
54
+ }
55
+ }
56
+ else if (arguments.length === 3) {
57
+ if (isEnvironment(envOrApiKey)) {
58
+ // 3-parameter case: (env, apiKey, relyingPartyId)
59
+ env = envOrApiKey;
60
+ apiKey = apiKeyOrRelyingPartyId;
61
+ relyingPartyId = relyingPartyIdOrOpts;
62
+ opts = undefined;
63
+ }
64
+ else {
65
+ // 3-parameter case: (apiKey, relyingPartyId, opts)
66
+ env = undefined;
67
+ apiKey = envOrApiKey;
68
+ relyingPartyId = apiKeyOrRelyingPartyId;
69
+ opts = relyingPartyIdOrOpts;
70
+ }
71
+ }
72
+ else {
73
+ // 4-parameter case: (env, apiKey, relyingPartyId, opts)
74
+ env = envOrApiKey;
75
+ apiKey = apiKeyOrRelyingPartyId;
76
+ relyingPartyId = relyingPartyIdOrOpts;
77
+ opts = optsArg;
78
+ }
79
+ env = ParaCore.resolveEnvironment(env, apiKey); // Ensure the environment is resolved before calling super
35
80
  super(env, apiKey, opts);
36
81
  this.isNativePasskey = true;
37
82
  setEnv(env);
@@ -135,7 +180,7 @@ export class ParaMobile extends ParaCore {
135
180
  sigDerivedPublicKey: publicKeyHex,
136
181
  cosePublicKey,
137
182
  clientDataJSON,
138
- status: PublicKeyStatus.COMPLETE,
183
+ status: AuthMethodStatus.COMPLETE,
139
184
  });
140
185
  yield this.ctx.client.uploadEncryptedWalletPrivateKey(userId, encryptedPrivateKeyHex, encryptionKeyHash, resultJson.id);
141
186
  });
@@ -48,4 +48,5 @@ export declare class ReactNativeUtils implements PlatformUtils {
48
48
  walletId: string;
49
49
  }>;
50
50
  ed25519Sign(ctx: Ctx, userId: string, walletId: string, share: string, base64Bytes: string, _sessionCookie: string): Promise<SignatureRes>;
51
+ initializeWorker(_ctx: Ctx): Promise<void>;
51
52
  }
@@ -168,4 +168,9 @@ export class ReactNativeUtils {
168
168
  return { signature: base64Sig };
169
169
  });
170
170
  }
171
+ initializeWorker(_ctx) {
172
+ return __awaiter(this, void 0, void 0, function* () {
173
+ return;
174
+ });
175
+ }
171
176
  }
package/dist/shim.d.ts CHANGED
@@ -1 +1,130 @@
1
- export {};
1
+ export function ensureParaCrypto(): {
2
+ baseCrypto: webcrypto.Crypto | {
3
+ getCiphers: () => string[];
4
+ getHashes: () => string[];
5
+ webcrypto: {
6
+ subtle: import("react-native-quick-crypto/lib/typescript/src/subtle").Subtle;
7
+ SubtleCrypto: typeof import("react-native-quick-crypto/lib/typescript/src/subtle").Subtle;
8
+ CryptoKey: typeof import("react-native-quick-crypto/lib/typescript/src/keys").CryptoKey;
9
+ };
10
+ randomFill<T extends import("react-native-quick-crypto/lib/typescript/src/Utils").ABV>(buffer: T, callback: (err: Error | null, buf: T) => void): void;
11
+ randomFill<T extends import("react-native-quick-crypto/lib/typescript/src/Utils").ABV>(buffer: T, offset: number, callback: (err: Error | null, buf: T) => void): void;
12
+ randomFill<T extends import("react-native-quick-crypto/lib/typescript/src/Utils").ABV>(buffer: T, offset: number, size: number, callback: (err: Error | null, buf: T) => void): void;
13
+ randomFillSync<T extends import("react-native-quick-crypto/lib/typescript/src/Utils").ABV>(buffer: T, offset?: number, size?: number): T;
14
+ randomBytes(size: number): Buffer;
15
+ randomBytes(size: number, callback: (err: Error | null, buf?: Buffer) => void): void;
16
+ randomInt(max: number, callback: (err: Error | null, value: number) => void): void;
17
+ randomInt(max: number): number;
18
+ randomInt(min: number, max: number, callback: (err: Error | null, value: number) => void): void;
19
+ randomInt(min: number, max: number): number;
20
+ getRandomValues(data: import("react-native-quick-crypto/lib/typescript/src/random").RandomTypedArrays): import("react-native-quick-crypto/lib/typescript/src/random").RandomTypedArrays;
21
+ randomUUID(): string;
22
+ rng: typeof import("react-native-quick-crypto/lib/typescript/src/random").randomBytes;
23
+ pseudoRandomBytes: typeof import("react-native-quick-crypto/lib/typescript/src/random").randomBytes;
24
+ prng: typeof import("react-native-quick-crypto/lib/typescript/src/random").randomBytes;
25
+ pbkdf2(password: import("react-native-quick-crypto/lib/typescript/src/Utils").BinaryLike, salt: import("react-native-quick-crypto/lib/typescript/src/Utils").BinaryLike, iterations: number, keylen: number, digest: string, callback: (err: Error | null, derivedKey?: Buffer) => void): void;
26
+ pbkdf2Sync(password: import("react-native-quick-crypto/lib/typescript/src/Utils").BinaryLike, salt: import("react-native-quick-crypto/lib/typescript/src/Utils").BinaryLike, iterations: number, keylen: number, digest?: string): ArrayBuffer;
27
+ pbkdf2DeriveBits(algorithm: import("react-native-quick-crypto/lib/typescript/src/keys").SubtleAlgorithm, baseKey: import("react-native-quick-crypto/lib/typescript/src/keys").CryptoKey, length: number): Promise<ArrayBuffer>;
28
+ createHmac: typeof import("react-native-quick-crypto/lib/typescript/src/Hmac").createHmac;
29
+ Hmac: typeof import("react-native-quick-crypto/lib/typescript/src/Hmac").createHmac;
30
+ Hash: typeof import("react-native-quick-crypto/lib/typescript/src/Hash").createHash;
31
+ createHash: typeof import("react-native-quick-crypto/lib/typescript/src/Hash").createHash;
32
+ createCipher: typeof import("react-native-quick-crypto/lib/typescript/src/Cipher").createCipher;
33
+ createCipheriv: typeof import("react-native-quick-crypto/lib/typescript/src/Cipher").createCipheriv;
34
+ createDecipher: typeof import("react-native-quick-crypto/lib/typescript/src/Cipher").createDecipher;
35
+ createDecipheriv: typeof import("react-native-quick-crypto/lib/typescript/src/Cipher").createDecipheriv;
36
+ createPublicKey: typeof import("react-native-quick-crypto/lib/typescript/src/keys").createPublicKey;
37
+ createPrivateKey: (key: import("react-native-quick-crypto/lib/typescript/src/Utils").BinaryLike | import("react-native-quick-crypto/lib/typescript/src/keys").EncodingOptions) => import("react-native-quick-crypto/lib/typescript/src/keys").PrivateKeyObject;
38
+ createSecretKey: typeof import("react-native-quick-crypto/lib/typescript/src/keys").createSecretKey;
39
+ publicEncrypt: (options: import("react-native-quick-crypto/lib/typescript/src/keys").EncodingOptions | import("react-native-quick-crypto/lib/typescript/src/Utils").BinaryLike, buffer: import("react-native-quick-crypto/lib/typescript/src/Utils").BinaryLike) => Buffer;
40
+ publicDecrypt: (options: import("react-native-quick-crypto/lib/typescript/src/keys").EncodingOptions | import("react-native-quick-crypto/lib/typescript/src/Utils").BinaryLike, buffer: import("react-native-quick-crypto/lib/typescript/src/Utils").BinaryLike) => Buffer;
41
+ privateDecrypt: (options: import("react-native-quick-crypto/lib/typescript/src/keys").EncodingOptions | import("react-native-quick-crypto/lib/typescript/src/Utils").BinaryLike, buffer: import("react-native-quick-crypto/lib/typescript/src/Utils").BinaryLike) => Buffer;
42
+ generateKey: (type: import("react-native-quick-crypto/lib/typescript/src/keys").SecretKeyType, options: import("react-native-quick-crypto/lib/typescript/src/keys").AesKeyGenParams, callback: import("react-native-quick-crypto/lib/typescript/src/keygen").KeyGenCallback) => void;
43
+ generateKeyPair: (type: import("react-native-quick-crypto/lib/typescript/src/keys").KeyPairType, options: import("react-native-quick-crypto/lib/typescript/src/Cipher").GenerateKeyPairOptions, callback: import("react-native-quick-crypto/lib/typescript/src/Cipher").GenerateKeyPairCallback) => void;
44
+ generateKeyPairSync: typeof import("react-native-quick-crypto/lib/typescript/src/Cipher").generateKeyPairSync;
45
+ generateKeySync: (type: import("react-native-quick-crypto/lib/typescript/src/keys").SecretKeyType, options: import("react-native-quick-crypto/lib/typescript/src/keys").AesKeyGenParams) => import("react-native-quick-crypto/lib/typescript/src/keys").SecretKeyObject;
46
+ createSign: typeof import("react-native-quick-crypto/lib/typescript/src/sig").createSign;
47
+ createVerify: typeof import("react-native-quick-crypto/lib/typescript/src/sig").createVerify;
48
+ subtle: import("react-native-quick-crypto/lib/typescript/src/subtle").Subtle;
49
+ constants: {
50
+ OPENSSL_VERSION_NUMBER: number;
51
+ SSL_OP_ALL: number;
52
+ SSL_OP_ALLOW_NO_DHE_KEX: number;
53
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: number;
54
+ SSL_OP_CIPHER_SERVER_PREFERENCE: number;
55
+ SSL_OP_CISCO_ANYCONNECT: number;
56
+ SSL_OP_COOKIE_EXCHANGE: number;
57
+ SSL_OP_CRYPTOPRO_TLSEXT_BUG: number;
58
+ SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: number;
59
+ SSL_OP_EPHEMERAL_RSA: number;
60
+ SSL_OP_LEGACY_SERVER_CONNECT: number;
61
+ SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER: number;
62
+ SSL_OP_MICROSOFT_SESS_ID_BUG: number;
63
+ SSL_OP_MSIE_SSLV2_RSA_PADDING: number;
64
+ SSL_OP_NETSCAPE_CA_DN_BUG: number;
65
+ SSL_OP_NETSCAPE_CHALLENGE_BUG: number;
66
+ SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG: number;
67
+ SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG: number;
68
+ SSL_OP_NO_COMPRESSION: number;
69
+ SSL_OP_NO_ENCRYPT_THEN_MAC: number;
70
+ SSL_OP_NO_QUERY_MTU: number;
71
+ SSL_OP_NO_RENEGOTIATION: number;
72
+ SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: number;
73
+ SSL_OP_NO_SSLv2: number;
74
+ SSL_OP_NO_SSLv3: number;
75
+ SSL_OP_NO_TICKET: number;
76
+ SSL_OP_NO_TLSv1: number;
77
+ SSL_OP_NO_TLSv1_1: number;
78
+ SSL_OP_NO_TLSv1_2: number;
79
+ SSL_OP_NO_TLSv1_3: number;
80
+ SSL_OP_PKCS1_CHECK_1: number;
81
+ SSL_OP_PKCS1_CHECK_2: number;
82
+ SSL_OP_PRIORITIZE_CHACHA: number;
83
+ SSL_OP_SINGLE_DH_USE: number;
84
+ SSL_OP_SINGLE_ECDH_USE: number;
85
+ SSL_OP_SSLEAY_080_CLIENT_DH_BUG: number;
86
+ SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG: number;
87
+ SSL_OP_TLS_BLOCK_PADDING_BUG: number;
88
+ SSL_OP_TLS_D5_BUG: number;
89
+ SSL_OP_TLS_ROLLBACK_BUG: number;
90
+ ENGINE_METHOD_RSA: number;
91
+ ENGINE_METHOD_DSA: number;
92
+ ENGINE_METHOD_DH: number;
93
+ ENGINE_METHOD_RAND: number;
94
+ ENGINE_METHOD_EC: number;
95
+ ENGINE_METHOD_CIPHERS: number;
96
+ ENGINE_METHOD_DIGESTS: number;
97
+ ENGINE_METHOD_PKEY_METHS: number;
98
+ ENGINE_METHOD_PKEY_ASN1_METHS: number;
99
+ ENGINE_METHOD_ALL: number;
100
+ ENGINE_METHOD_NONE: number;
101
+ DH_CHECK_P_NOT_SAFE_PRIME: number;
102
+ DH_CHECK_P_NOT_PRIME: number;
103
+ DH_UNABLE_TO_CHECK_GENERATOR: number;
104
+ DH_NOT_SUITABLE_GENERATOR: number;
105
+ ALPN_ENABLED: number;
106
+ RSA_PKCS1_PADDING: number;
107
+ RSA_SSLV23_PADDING: number;
108
+ RSA_NO_PADDING: number;
109
+ RSA_PKCS1_OAEP_PADDING: number;
110
+ RSA_X931_PADDING: number;
111
+ RSA_PKCS1_PSS_PADDING: number;
112
+ RSA_PSS_SALTLEN_DIGEST: number;
113
+ RSA_PSS_SALTLEN_MAX_SIGN: number;
114
+ RSA_PSS_SALTLEN_AUTO: number;
115
+ defaultCoreCipherList: string;
116
+ TLS1_VERSION: number;
117
+ TLS1_1_VERSION: number;
118
+ TLS1_2_VERSION: number;
119
+ TLS1_3_VERSION: number;
120
+ POINT_CONVERSION_COMPRESSED: number;
121
+ POINT_CONVERSION_UNCOMPRESSED: number;
122
+ POINT_CONVERSION_HYBRID: number;
123
+ };
124
+ };
125
+ peculiarCrypto: PeculiarCrypto;
126
+ };
127
+ export function ensureCreateECDH(): any;
128
+ import { webcrypto } from 'crypto';
129
+ import { Buffer } from '@craftzdog/react-native-buffer';
130
+ import { Crypto as PeculiarCrypto } from '@peculiar/webcrypto';
package/dist/shim.js CHANGED
@@ -1,6 +1,7 @@
1
- import crypto from 'react-native-quick-crypto';
1
+ import quickCrypto from 'react-native-quick-crypto';
2
2
  import { webcrypto } from 'crypto';
3
- import { Crypto } from '@peculiar/webcrypto';
3
+ import { Crypto as PeculiarCrypto } from '@peculiar/webcrypto';
4
+ import { ec as EllipticEC } from 'elliptic';
4
5
  import Forge from 'node-forge';
5
6
  import modPow from 'react-native-modpow';
6
7
  import { atob, btoa } from 'react-native-quick-base64';
@@ -8,7 +9,37 @@ import { Buffer } from '@craftzdog/react-native-buffer';
8
9
  import process from 'process';
9
10
  import 'react-native-url-polyfill/auto';
10
11
  import { TextEncoder, TextDecoder } from 'text-encoding';
11
- import structuredClone from '@ungap/structured-clone';
12
+ let cachedStructuredCloneImpl;
13
+ const resolveStructuredClone = () => {
14
+ if (typeof cachedStructuredCloneImpl !== 'undefined') {
15
+ return cachedStructuredCloneImpl;
16
+ }
17
+ if (typeof globalThis.structuredClone === 'function') {
18
+ cachedStructuredCloneImpl = globalThis.structuredClone.bind(globalThis);
19
+ return cachedStructuredCloneImpl;
20
+ }
21
+ const isHermes = typeof globalThis.HermesInternal === 'object' && globalThis.HermesInternal !== null;
22
+ if (isHermes) {
23
+ // Hermes crashes when @ungap/structured-clone rewires Reflect helpers.
24
+ cachedStructuredCloneImpl = null;
25
+ return cachedStructuredCloneImpl;
26
+ }
27
+ try {
28
+ const maybePolyfill = require('@ungap/structured-clone');
29
+ const polyfill = (maybePolyfill && maybePolyfill.default) || maybePolyfill;
30
+ if (typeof polyfill === 'function') {
31
+ cachedStructuredCloneImpl = polyfill;
32
+ return cachedStructuredCloneImpl;
33
+ }
34
+ }
35
+ catch (err) {
36
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
37
+ console.warn('[Para React Native shim] Failed to load structuredClone polyfill', err);
38
+ }
39
+ }
40
+ cachedStructuredCloneImpl = null;
41
+ return cachedStructuredCloneImpl;
42
+ };
12
43
  const setupProcessPolyfill = () => {
13
44
  if (typeof globalThis.process === 'undefined') {
14
45
  globalThis.process = process;
@@ -33,16 +64,191 @@ const setupBase64Polyfills = () => {
33
64
  throw new Error('Base64 polyfills failed to initialize');
34
65
  }
35
66
  };
36
- const setupCryptoPolyfills = () => {
37
- const peculiarCrypto = new Crypto();
38
- if (typeof globalThis.crypto === 'undefined') {
39
- globalThis.crypto = crypto;
67
+ const curveAliases = {
68
+ 'P-256': 'p256',
69
+ 'p-256': 'p256',
70
+ 'prime256v1': 'p256',
71
+ 'secp256r1': 'p256',
72
+ 'secp256k1': 'secp256k1',
73
+ };
74
+ const getBufferImpl = () => {
75
+ const BufferImpl = globalThis.Buffer;
76
+ if (!BufferImpl) {
77
+ // prettier-ignore
78
+ throw new Error("[Para React Native shim] Buffer global missing. Please import '@getpara/react-native-wallet/shim' before using cryptography helpers.");
79
+ }
80
+ return BufferImpl;
81
+ };
82
+ const bufferFrom = (data, encoding = 'binary') => {
83
+ const BufferImpl = getBufferImpl();
84
+ if (BufferImpl.isBuffer && BufferImpl.isBuffer(data)) {
85
+ return data;
40
86
  }
41
- if (!globalThis.crypto.subtle) {
42
- globalThis.crypto.subtle = peculiarCrypto.subtle;
43
- globalThis.crypto.getRandomValues = peculiarCrypto.getRandomValues.bind(peculiarCrypto);
87
+ if (typeof data === 'string') {
88
+ switch (encoding) {
89
+ case 'hex':
90
+ return BufferImpl.from(data, 'hex');
91
+ case 'base64':
92
+ return BufferImpl.from(data, 'base64');
93
+ default:
94
+ return BufferImpl.from(data, 'binary');
95
+ }
96
+ }
97
+ return BufferImpl.from(data);
98
+ };
99
+ const bufferTo = (buf, encoding = 'binary') => {
100
+ const BufferImpl = getBufferImpl();
101
+ switch (encoding) {
102
+ case 'hex':
103
+ return BufferImpl.from(buf).toString('hex');
104
+ case 'base64':
105
+ return BufferImpl.from(buf).toString('base64');
106
+ case 'binary':
107
+ default:
108
+ return BufferImpl.from(buf);
109
+ }
110
+ };
111
+ let cachedCreateECDH = null;
112
+ const ensureCreateECDH = () => {
113
+ if (!cachedCreateECDH) {
114
+ cachedCreateECDH = curveName => {
115
+ const BufferImpl = getBufferImpl();
116
+ const normalizedCurve = curveAliases[curveName] || curveName;
117
+ if (!normalizedCurve && typeof __DEV__ !== 'undefined' && __DEV__) {
118
+ console.warn('[Para React Native shim] Unknown curve alias', curveName);
119
+ }
120
+ const ec = new EllipticEC(normalizedCurve);
121
+ const secretByteLength = (() => {
122
+ const order = (ec && ec.curve && ec.curve.n) || (ec && ec.n);
123
+ if (!order) {
124
+ return null;
125
+ }
126
+ if (typeof order.byteLength === 'function') {
127
+ return order.byteLength();
128
+ }
129
+ if (typeof order.bitLength === 'function') {
130
+ return Math.ceil(order.bitLength() / 8);
131
+ }
132
+ return null;
133
+ })();
134
+ let keyPair = ec.genKeyPair();
135
+ return {
136
+ generateKeys: (encoding = 'binary', format = 'uncompressed') => {
137
+ keyPair = ec.genKeyPair();
138
+ const compressed = format === 'compressed' || format === 'comp';
139
+ const publicKey = keyPair.getPublic(compressed, 'array');
140
+ return bufferTo(BufferImpl.from(publicKey), encoding || 'binary');
141
+ },
142
+ computeSecret: (publicKey, inputEncoding = 'binary', outputEncoding = 'binary') => {
143
+ const publicKeyBuf = bufferFrom(publicKey, inputEncoding || 'binary');
144
+ const derived = keyPair.derive(ec.keyFromPublic(publicKeyBuf).getPublic());
145
+ const padded = secretByteLength ? derived.toArray('be', secretByteLength) : derived.toArray('be');
146
+ // Match Node's ECDH API: shared secret is always curve-size bytes.
147
+ const secret = BufferImpl.from(padded);
148
+ return bufferTo(secret, outputEncoding || 'binary');
149
+ },
150
+ getPrivateKey: (encoding = 'binary') => {
151
+ return bufferTo(keyPair.getPrivate().toArrayLike(BufferImpl, 'be', 32), encoding || 'binary');
152
+ },
153
+ getPublicKey: (encoding = 'binary', format = 'uncompressed') => {
154
+ const compressed = format === 'compressed' || format === 'comp';
155
+ const publicKey = keyPair.getPublic(compressed, 'array');
156
+ return bufferTo(BufferImpl.from(publicKey), encoding || 'binary');
157
+ },
158
+ setPrivateKey: (privateKey, encoding = 'binary') => {
159
+ keyPair = ec.keyFromPrivate(bufferFrom(privateKey, encoding));
160
+ },
161
+ setPublicKey: (publicKey, encoding = 'binary') => {
162
+ keyPair = ec.keyFromPublic(bufferFrom(publicKey, encoding));
163
+ },
164
+ };
165
+ };
166
+ }
167
+ const possibleTargets = [];
168
+ const globalCrypto = globalThis.crypto;
169
+ if (globalCrypto) {
170
+ possibleTargets.push(globalCrypto, globalCrypto.default);
171
+ }
172
+ possibleTargets.push(quickCrypto, quickCrypto && quickCrypto.default);
173
+ try {
174
+ const nodeCrypto = require('crypto');
175
+ possibleTargets.push(nodeCrypto, nodeCrypto && nodeCrypto.default);
176
+ // eslint-disable-next-line no-unused-vars
177
+ }
178
+ catch (_err) {
179
+ // The Node crypto module is not available in React Native; ignore failures.
180
+ }
181
+ const patchedTargets = new Set(possibleTargets.filter(Boolean));
182
+ let patched = false;
183
+ patchedTargets.forEach(target => {
184
+ if (target && typeof target === 'object' && target.createECDH !== cachedCreateECDH) {
185
+ try {
186
+ target.createECDH = cachedCreateECDH;
187
+ patched = true;
188
+ }
189
+ catch (err) {
190
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
191
+ console.warn('[Para React Native shim] Failed to patch createECDH', err);
192
+ }
193
+ }
194
+ }
195
+ });
196
+ if (patched && typeof __DEV__ !== 'undefined' && __DEV__) {
197
+ console.info('[Para React Native shim] createECDH patched function');
198
+ }
199
+ return cachedCreateECDH;
200
+ };
201
+ const syncWindowLikeGlobals = cryptoObj => {
202
+ const maybeWindow = globalThis.window;
203
+ if (maybeWindow && typeof maybeWindow === 'object' && maybeWindow.crypto !== cryptoObj) {
204
+ maybeWindow.crypto = cryptoObj;
205
+ }
206
+ const maybeSelf = globalThis.self;
207
+ if (maybeSelf && typeof maybeSelf === 'object' && maybeSelf.crypto !== cryptoObj) {
208
+ maybeSelf.crypto = cryptoObj;
209
+ }
210
+ };
211
+ const ensureParaCrypto = () => {
212
+ getBufferImpl();
213
+ const baseCrypto = (typeof globalThis.crypto === 'object' && globalThis.crypto) || quickCrypto;
214
+ if (!baseCrypto || typeof baseCrypto !== 'object') {
215
+ throw new Error('[Para React Native shim] No crypto object found to polyfill');
216
+ }
217
+ if (typeof baseCrypto.default === 'undefined') {
218
+ baseCrypto.default = baseCrypto;
219
+ }
220
+ const peculiarCrypto = new PeculiarCrypto();
221
+ baseCrypto.subtle = peculiarCrypto.subtle;
222
+ baseCrypto.getRandomValues = peculiarCrypto.getRandomValues.bind(peculiarCrypto);
223
+ if (baseCrypto.default && baseCrypto.default !== baseCrypto) {
224
+ baseCrypto.default.subtle = baseCrypto.subtle;
225
+ baseCrypto.default.getRandomValues = baseCrypto.getRandomValues;
226
+ }
227
+ globalThis.crypto = baseCrypto;
228
+ syncWindowLikeGlobals(baseCrypto);
229
+ try {
230
+ const brorand = require('brorand');
231
+ if (brorand && brorand.Rand) {
232
+ brorand.Rand.prototype._rand = function _rand(n) {
233
+ const arr = new Uint8Array(n);
234
+ baseCrypto.getRandomValues(arr);
235
+ return arr;
236
+ };
237
+ }
238
+ }
239
+ catch (err) {
240
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
241
+ console.warn('[Para React Native shim] Failed to patch brorand RNG', err);
242
+ }
243
+ }
244
+ ensureCreateECDH();
245
+ return { baseCrypto, peculiarCrypto };
246
+ };
247
+ const setupCryptoPolyfills = () => {
248
+ const { peculiarCrypto } = ensureParaCrypto();
249
+ if (webcrypto && typeof webcrypto === 'object') {
250
+ webcrypto.getRandomValues = peculiarCrypto.getRandomValues.bind(peculiarCrypto);
44
251
  }
45
- webcrypto.getRandomValues = peculiarCrypto.getRandomValues.bind(peculiarCrypto);
46
252
  if (typeof Forge === 'undefined') {
47
253
  throw new Error('node-forge not loaded');
48
254
  }
@@ -62,14 +268,78 @@ const setupTextEncodingPolyfills = () => {
62
268
  globalThis.TextEncoder = TextEncoder;
63
269
  globalThis.TextDecoder = TextDecoder;
64
270
  };
271
+ const setupWindowLocationPolyfill = () => {
272
+ const FALLBACK_HOST = 'para.mobile';
273
+ const FALLBACK_ORIGIN = `https://${FALLBACK_HOST}`;
274
+ const globalScope = globalThis;
275
+ if (!globalScope.window || typeof globalScope.window !== 'object') {
276
+ globalScope.window = globalScope;
277
+ }
278
+ const win = globalScope.window;
279
+ const existingLocation = (win && win.location) || globalScope.location;
280
+ const hasOrigin = existingLocation &&
281
+ typeof existingLocation === 'object' &&
282
+ typeof existingLocation.origin === 'string' &&
283
+ typeof existingLocation.host === 'string';
284
+ if (hasOrigin) {
285
+ if (!globalScope.location) {
286
+ globalScope.location = existingLocation;
287
+ }
288
+ return;
289
+ }
290
+ const fallbackLocation = {
291
+ href: `${FALLBACK_ORIGIN}/`,
292
+ origin: FALLBACK_ORIGIN,
293
+ protocol: 'https:',
294
+ host: FALLBACK_HOST,
295
+ hostname: FALLBACK_HOST,
296
+ port: '',
297
+ pathname: '/',
298
+ };
299
+ const patchedLocation = typeof existingLocation === 'object' && existingLocation !== null
300
+ ? Object.assign({}, fallbackLocation, existingLocation)
301
+ : fallbackLocation;
302
+ win.location = patchedLocation;
303
+ globalScope.location = patchedLocation;
304
+ };
65
305
  const setupStructuredClonePolyfill = () => {
66
- if (typeof globalThis.structuredClone === 'undefined') {
67
- globalThis.structuredClone = structuredClone;
306
+ if (typeof globalThis.structuredClone === 'function') {
307
+ return;
68
308
  }
309
+ const structuredCloneImpl = resolveStructuredClone();
310
+ const safeStructuredClone = (value, options) => {
311
+ if (typeof structuredCloneImpl === 'function') {
312
+ try {
313
+ return structuredCloneImpl(value, options);
314
+ }
315
+ catch (err) {
316
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
317
+ console.warn('[Para React Native shim] structuredClone polyfill failed, falling back to JSON clone', err);
318
+ }
319
+ }
320
+ }
321
+ try {
322
+ return JSON.parse(JSON.stringify(value));
323
+ }
324
+ catch (_err) {
325
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
326
+ console.warn('[Para React Native shim] JSON clone failed, returning original value', _err);
327
+ }
328
+ return value;
329
+ }
330
+ };
331
+ Object.defineProperty(globalThis, 'structuredClone', {
332
+ value: safeStructuredClone,
333
+ configurable: true,
334
+ enumerable: false,
335
+ writable: true,
336
+ });
69
337
  };
70
338
  setupProcessPolyfill();
71
339
  setupBufferPolyfill();
72
340
  setupBase64Polyfills();
341
+ setupWindowLocationPolyfill();
73
342
  setupCryptoPolyfills();
74
343
  setupTextEncodingPolyfills();
75
344
  setupStructuredClonePolyfill();
345
+ export { ensureParaCrypto, ensureCreateECDH };
package/package.json CHANGED
@@ -1,15 +1,14 @@
1
1
  {
2
2
  "name": "@getpara/react-native-wallet",
3
3
  "description": "Para Wallet for React Native",
4
- "version": "2.0.0-fc.3",
4
+ "version": "2.1.0",
5
5
  "author": "Para Team <hello@getpara.com> (https://getpara.com)",
6
6
  "dependencies": {
7
- "@getpara/core-sdk": "2.0.0-fc.3",
8
- "@getpara/user-management-client": "2.0.0-fc.3",
9
- "@getpara/web-sdk": "2.0.0-fc.3",
7
+ "@getpara/core-sdk": "2.1.0",
8
+ "@getpara/user-management-client": "2.1.0",
9
+ "@getpara/web-sdk": "2.1.0",
10
10
  "@peculiar/webcrypto": "^1.5.0",
11
11
  "@ungap/structured-clone": "1.3.0",
12
- "node-forge": "1.3.1",
13
12
  "react-native-url-polyfill": "2.0.0",
14
13
  "text-encoding": "0.7.0"
15
14
  },
@@ -93,5 +92,5 @@
93
92
  ]
94
93
  }
95
94
  },
96
- "gitHead": "5ba068331384bed874c0da0d3d0e1fed6bf03ff9"
95
+ "gitHead": "3ae7f836324a3a2a8a57156e16304aeaf0d37b42"
97
96
  }
@@ -2,6 +2,7 @@ require "json"
2
2
 
3
3
  package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
4
  folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
5
+ new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
5
6
 
6
7
  Pod::Spec.new do |s|
7
8
  s.name = "para-react-native-wallet"
@@ -19,8 +20,9 @@ Pod::Spec.new do |s|
19
20
 
20
21
  s.dependency "React-Core"
21
22
 
22
- # Don't install the dependencies when we run `pod install` in the old architecture.
23
- if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
23
+ if respond_to?(:install_modules_dependencies, true)
24
+ install_modules_dependencies(s)
25
+ elsif new_arch_enabled
24
26
  s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
25
27
  s.pod_target_xcconfig = {
26
28
  "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
@@ -33,4 +35,4 @@ Pod::Spec.new do |s|
33
35
  s.dependency "RCTTypeSafety"
34
36
  s.dependency "ReactCommon/turbomodule/core"
35
37
  end
36
- end
38
+ end
package/src/config.ts CHANGED
@@ -6,11 +6,11 @@ function getPortalBaseURL(env: Environment) {
6
6
  case Environment.DEV:
7
7
  return 'http://localhost:3003';
8
8
  case Environment.SANDBOX:
9
- return 'https://app.sandbox.usecapsule.com';
9
+ return 'https://app.sandbox.getpara.com';
10
10
  case Environment.BETA:
11
- return 'https://app.beta.usecapsule.com';
11
+ return 'https://app.beta.getpara.com';
12
12
  case Environment.PROD:
13
- return 'https://app.usecapsule.com';
13
+ return 'https://app.getpara.com';
14
14
  default:
15
15
  throw new Error(`env: ${env} not supported`);
16
16
  }
@@ -22,7 +22,7 @@ import {
22
22
  PasskeyGetRequest,
23
23
  PasskeyGetResult,
24
24
  } from 'react-native-passkey';
25
- import { CurrentWalletIds, PublicKeyStatus, TWalletScheme } from '@getpara/user-management-client';
25
+ import { CurrentWalletIds, AuthMethodStatus, TWalletScheme } from '@getpara/user-management-client';
26
26
  import { setEnv } from '../config.js';
27
27
  import base64url from 'base64url';
28
28
  import { webcrypto } from 'crypto';
@@ -43,12 +43,70 @@ export class ParaMobile extends ParaCore {
43
43
  private relyingPartyId: string;
44
44
  /**
45
45
  * Creates an instance of ParaMobile.
46
- * @param {Environment} env - The environment to use (DEV, SANDBOX, BETA, or PROD).
46
+ * @param {Environment} env - The environment to use (DEV, SANDBOX, BETA, or PROD). Optional if the apiKey contains an environment prefix (e.g., "prod_your_api_key"). Updated API keys can be found at https://developer.getpara.com.
47
47
  * @param {string} [apiKey] - The API key for authentication.
48
48
  * @param {string} [relyingPartyId] - The relying party ID for WebAuthn.
49
49
  * @param {ConstructorOpts} [opts] - Additional constructor options.
50
50
  */
51
- constructor(env: Environment, apiKey: string, relyingPartyId?: string, opts?: ConstructorOpts) {
51
+ constructor(env: Environment | undefined, apiKey: string, relyingPartyId?: string, opts?: ConstructorOpts);
52
+ constructor(apiKey: string, relyingPartyId?: string, opts?: ConstructorOpts);
53
+ constructor(
54
+ envOrApiKey: Environment | undefined | string,
55
+ apiKeyOrRelyingPartyId?: string,
56
+ relyingPartyIdOrOpts?: string | ConstructorOpts,
57
+ optsArg?: ConstructorOpts,
58
+ ) {
59
+ let env: Environment | undefined, apiKey: string, relyingPartyId: string | undefined, opts: ConstructorOpts | undefined;
60
+
61
+ // Helper function to check if value is a valid Environment
62
+ const isEnvironment = (value: any): value is Environment => {
63
+ return Object.values(Environment).includes(value);
64
+ };
65
+
66
+ if (arguments.length === 1) {
67
+ // 1-parameter case: (apiKey)
68
+ env = undefined;
69
+ apiKey = envOrApiKey as string;
70
+ relyingPartyId = undefined;
71
+ opts = undefined;
72
+ } else if (arguments.length === 2) {
73
+ if (isEnvironment(envOrApiKey)) {
74
+ // 2-parameter case: (env, apiKey)
75
+ env = envOrApiKey;
76
+ apiKey = apiKeyOrRelyingPartyId as string;
77
+ relyingPartyId = undefined;
78
+ opts = undefined;
79
+ } else {
80
+ // 2-parameter case: (apiKey, relyingPartyId)
81
+ env = undefined;
82
+ apiKey = envOrApiKey as string;
83
+ relyingPartyId = apiKeyOrRelyingPartyId;
84
+ opts = undefined;
85
+ }
86
+ } else if (arguments.length === 3) {
87
+ if (isEnvironment(envOrApiKey)) {
88
+ // 3-parameter case: (env, apiKey, relyingPartyId)
89
+ env = envOrApiKey;
90
+ apiKey = apiKeyOrRelyingPartyId as string;
91
+ relyingPartyId = relyingPartyIdOrOpts as string;
92
+ opts = undefined;
93
+ } else {
94
+ // 3-parameter case: (apiKey, relyingPartyId, opts)
95
+ env = undefined;
96
+ apiKey = envOrApiKey as string;
97
+ relyingPartyId = apiKeyOrRelyingPartyId;
98
+ opts = relyingPartyIdOrOpts as ConstructorOpts;
99
+ }
100
+ } else {
101
+ // 4-parameter case: (env, apiKey, relyingPartyId, opts)
102
+ env = envOrApiKey as Environment | undefined;
103
+ apiKey = apiKeyOrRelyingPartyId as string;
104
+ relyingPartyId = relyingPartyIdOrOpts as string | undefined;
105
+ opts = optsArg;
106
+ }
107
+
108
+ env = ParaCore.resolveEnvironment(env, apiKey); // Ensure the environment is resolved before calling super
109
+
52
110
  super(env, apiKey, opts);
53
111
 
54
112
  setEnv(env);
@@ -72,7 +130,7 @@ export class ParaMobile extends ParaCore {
72
130
  }
73
131
  }
74
132
 
75
- protected async ready() {
133
+ async ready() {
76
134
  this.isReady = true;
77
135
  }
78
136
 
@@ -159,7 +217,7 @@ export class ParaMobile extends ParaCore {
159
217
  sigDerivedPublicKey: publicKeyHex,
160
218
  cosePublicKey,
161
219
  clientDataJSON,
162
- status: PublicKeyStatus.COMPLETE,
220
+ status: AuthMethodStatus.COMPLETE,
163
221
  });
164
222
 
165
223
  await this.ctx.client.uploadEncryptedWalletPrivateKey(userId, encryptedPrivateKeyHex, encryptionKeyHash, resultJson.id);
@@ -262,4 +262,8 @@ export class ReactNativeUtils implements PlatformUtils {
262
262
  const base64Sig = await ParaSignerModule.ed25519Sign(protocolId, share, base64Bytes);
263
263
  return { signature: base64Sig };
264
264
  }
265
+
266
+ async initializeWorker(_ctx: Ctx): Promise<void> {
267
+ return;
268
+ }
265
269
  }
package/src/shim.js CHANGED
@@ -1,6 +1,7 @@
1
- import crypto from 'react-native-quick-crypto';
1
+ import quickCrypto from 'react-native-quick-crypto';
2
2
  import { webcrypto } from 'crypto';
3
- import { Crypto } from '@peculiar/webcrypto';
3
+ import { Crypto as PeculiarCrypto } from '@peculiar/webcrypto';
4
+ import { ec as EllipticEC } from 'elliptic';
4
5
  import Forge from 'node-forge';
5
6
  import modPow from 'react-native-modpow';
6
7
  import { atob, btoa } from 'react-native-quick-base64';
@@ -8,7 +9,42 @@ import { Buffer } from '@craftzdog/react-native-buffer';
8
9
  import process from 'process';
9
10
  import 'react-native-url-polyfill/auto';
10
11
  import { TextEncoder, TextDecoder } from 'text-encoding';
11
- import structuredClone from '@ungap/structured-clone';
12
+
13
+ let cachedStructuredCloneImpl;
14
+
15
+ const resolveStructuredClone = () => {
16
+ if (typeof cachedStructuredCloneImpl !== 'undefined') {
17
+ return cachedStructuredCloneImpl;
18
+ }
19
+
20
+ if (typeof globalThis.structuredClone === 'function') {
21
+ cachedStructuredCloneImpl = globalThis.structuredClone.bind(globalThis);
22
+ return cachedStructuredCloneImpl;
23
+ }
24
+
25
+ const isHermes = typeof globalThis.HermesInternal === 'object' && globalThis.HermesInternal !== null;
26
+ if (isHermes) {
27
+ // Hermes crashes when @ungap/structured-clone rewires Reflect helpers.
28
+ cachedStructuredCloneImpl = null;
29
+ return cachedStructuredCloneImpl;
30
+ }
31
+
32
+ try {
33
+ const maybePolyfill = require('@ungap/structured-clone');
34
+ const polyfill = (maybePolyfill && maybePolyfill.default) || maybePolyfill;
35
+ if (typeof polyfill === 'function') {
36
+ cachedStructuredCloneImpl = polyfill;
37
+ return cachedStructuredCloneImpl;
38
+ }
39
+ } catch (err) {
40
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
41
+ console.warn('[Para React Native shim] Failed to load structuredClone polyfill', err);
42
+ }
43
+ }
44
+
45
+ cachedStructuredCloneImpl = null;
46
+ return cachedStructuredCloneImpl;
47
+ };
12
48
 
13
49
  const setupProcessPolyfill = () => {
14
50
  if (typeof globalThis.process === 'undefined') {
@@ -37,16 +73,218 @@ const setupBase64Polyfills = () => {
37
73
  }
38
74
  };
39
75
 
40
- const setupCryptoPolyfills = () => {
41
- const peculiarCrypto = new Crypto();
42
- if (typeof globalThis.crypto === 'undefined') {
43
- globalThis.crypto = crypto;
76
+ const curveAliases = {
77
+ 'P-256': 'p256',
78
+ 'p-256': 'p256',
79
+ 'prime256v1': 'p256',
80
+ 'secp256r1': 'p256',
81
+ 'secp256k1': 'secp256k1',
82
+ };
83
+
84
+ const getBufferImpl = () => {
85
+ const BufferImpl = globalThis.Buffer;
86
+ if (!BufferImpl) {
87
+ // prettier-ignore
88
+ throw new Error(
89
+ "[Para React Native shim] Buffer global missing. Please import '@getpara/react-native-wallet/shim' before using cryptography helpers."
90
+ );
91
+ }
92
+ return BufferImpl;
93
+ };
94
+
95
+ const bufferFrom = (data, encoding = 'binary') => {
96
+ const BufferImpl = getBufferImpl();
97
+ if (BufferImpl.isBuffer && BufferImpl.isBuffer(data)) {
98
+ return data;
99
+ }
100
+
101
+ if (typeof data === 'string') {
102
+ switch (encoding) {
103
+ case 'hex':
104
+ return BufferImpl.from(data, 'hex');
105
+ case 'base64':
106
+ return BufferImpl.from(data, 'base64');
107
+ default:
108
+ return BufferImpl.from(data, 'binary');
109
+ }
110
+ }
111
+
112
+ return BufferImpl.from(data);
113
+ };
114
+
115
+ const bufferTo = (buf, encoding = 'binary') => {
116
+ const BufferImpl = getBufferImpl();
117
+ switch (encoding) {
118
+ case 'hex':
119
+ return BufferImpl.from(buf).toString('hex');
120
+ case 'base64':
121
+ return BufferImpl.from(buf).toString('base64');
122
+ case 'binary':
123
+ default:
124
+ return BufferImpl.from(buf);
125
+ }
126
+ };
127
+
128
+ let cachedCreateECDH = null;
129
+
130
+ const ensureCreateECDH = () => {
131
+ if (!cachedCreateECDH) {
132
+ cachedCreateECDH = curveName => {
133
+ const BufferImpl = getBufferImpl();
134
+ const normalizedCurve = curveAliases[curveName] || curveName;
135
+ if (!normalizedCurve && typeof __DEV__ !== 'undefined' && __DEV__) {
136
+ console.warn('[Para React Native shim] Unknown curve alias', curveName);
137
+ }
138
+
139
+ const ec = new EllipticEC(normalizedCurve);
140
+ const secretByteLength = (() => {
141
+ const order = (ec && ec.curve && ec.curve.n) || (ec && ec.n);
142
+ if (!order) {
143
+ return null;
144
+ }
145
+ if (typeof order.byteLength === 'function') {
146
+ return order.byteLength();
147
+ }
148
+ if (typeof order.bitLength === 'function') {
149
+ return Math.ceil(order.bitLength() / 8);
150
+ }
151
+ return null;
152
+ })();
153
+ let keyPair = ec.genKeyPair();
154
+
155
+ return {
156
+ generateKeys: (encoding = 'binary', format = 'uncompressed') => {
157
+ keyPair = ec.genKeyPair();
158
+ const compressed = format === 'compressed' || format === 'comp';
159
+ const publicKey = keyPair.getPublic(compressed, 'array');
160
+ return bufferTo(BufferImpl.from(publicKey), encoding || 'binary');
161
+ },
162
+ computeSecret: (publicKey, inputEncoding = 'binary', outputEncoding = 'binary') => {
163
+ const publicKeyBuf = bufferFrom(publicKey, inputEncoding || 'binary');
164
+ const derived = keyPair.derive(ec.keyFromPublic(publicKeyBuf).getPublic());
165
+ const padded = secretByteLength ? derived.toArray('be', secretByteLength) : derived.toArray('be');
166
+ // Match Node's ECDH API: shared secret is always curve-size bytes.
167
+ const secret = BufferImpl.from(padded);
168
+ return bufferTo(secret, outputEncoding || 'binary');
169
+ },
170
+ getPrivateKey: (encoding = 'binary') => {
171
+ return bufferTo(keyPair.getPrivate().toArrayLike(BufferImpl, 'be', 32), encoding || 'binary');
172
+ },
173
+ getPublicKey: (encoding = 'binary', format = 'uncompressed') => {
174
+ const compressed = format === 'compressed' || format === 'comp';
175
+ const publicKey = keyPair.getPublic(compressed, 'array');
176
+ return bufferTo(BufferImpl.from(publicKey), encoding || 'binary');
177
+ },
178
+ setPrivateKey: (privateKey, encoding = 'binary') => {
179
+ keyPair = ec.keyFromPrivate(bufferFrom(privateKey, encoding));
180
+ },
181
+ setPublicKey: (publicKey, encoding = 'binary') => {
182
+ keyPair = ec.keyFromPublic(bufferFrom(publicKey, encoding));
183
+ },
184
+ };
185
+ };
186
+ }
187
+
188
+ const possibleTargets = [];
189
+ const globalCrypto = globalThis.crypto;
190
+ if (globalCrypto) {
191
+ possibleTargets.push(globalCrypto, globalCrypto.default);
192
+ }
193
+ possibleTargets.push(quickCrypto, quickCrypto && quickCrypto.default);
194
+
195
+ try {
196
+ const nodeCrypto = require('crypto');
197
+ possibleTargets.push(nodeCrypto, nodeCrypto && nodeCrypto.default);
198
+ // eslint-disable-next-line no-unused-vars
199
+ } catch (_err) {
200
+ // The Node crypto module is not available in React Native; ignore failures.
201
+ }
202
+
203
+ const patchedTargets = new Set(possibleTargets.filter(Boolean));
204
+ let patched = false;
205
+
206
+ patchedTargets.forEach(target => {
207
+ if (target && typeof target === 'object' && target.createECDH !== cachedCreateECDH) {
208
+ try {
209
+ target.createECDH = cachedCreateECDH;
210
+ patched = true;
211
+ } catch (err) {
212
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
213
+ console.warn('[Para React Native shim] Failed to patch createECDH', err);
214
+ }
215
+ }
216
+ }
217
+ });
218
+
219
+ if (patched && typeof __DEV__ !== 'undefined' && __DEV__) {
220
+ console.info('[Para React Native shim] createECDH patched function');
221
+ }
222
+
223
+ return cachedCreateECDH;
224
+ };
225
+
226
+ const syncWindowLikeGlobals = cryptoObj => {
227
+ const maybeWindow = globalThis.window;
228
+ if (maybeWindow && typeof maybeWindow === 'object' && maybeWindow.crypto !== cryptoObj) {
229
+ maybeWindow.crypto = cryptoObj;
230
+ }
231
+
232
+ const maybeSelf = globalThis.self;
233
+ if (maybeSelf && typeof maybeSelf === 'object' && maybeSelf.crypto !== cryptoObj) {
234
+ maybeSelf.crypto = cryptoObj;
235
+ }
236
+ };
237
+
238
+ const ensureParaCrypto = () => {
239
+ getBufferImpl();
240
+ const baseCrypto = (typeof globalThis.crypto === 'object' && globalThis.crypto) || quickCrypto;
241
+
242
+ if (!baseCrypto || typeof baseCrypto !== 'object') {
243
+ throw new Error('[Para React Native shim] No crypto object found to polyfill');
244
+ }
245
+
246
+ if (typeof baseCrypto.default === 'undefined') {
247
+ baseCrypto.default = baseCrypto;
44
248
  }
45
- if (!globalThis.crypto.subtle) {
46
- globalThis.crypto.subtle = peculiarCrypto.subtle;
47
- globalThis.crypto.getRandomValues = peculiarCrypto.getRandomValues.bind(peculiarCrypto);
249
+
250
+ const peculiarCrypto = new PeculiarCrypto();
251
+
252
+ baseCrypto.subtle = peculiarCrypto.subtle;
253
+ baseCrypto.getRandomValues = peculiarCrypto.getRandomValues.bind(peculiarCrypto);
254
+
255
+ if (baseCrypto.default && baseCrypto.default !== baseCrypto) {
256
+ baseCrypto.default.subtle = baseCrypto.subtle;
257
+ baseCrypto.default.getRandomValues = baseCrypto.getRandomValues;
258
+ }
259
+
260
+ globalThis.crypto = baseCrypto;
261
+ syncWindowLikeGlobals(baseCrypto);
262
+ try {
263
+ const brorand = require('brorand');
264
+ if (brorand && brorand.Rand) {
265
+ brorand.Rand.prototype._rand = function _rand(n) {
266
+ const arr = new Uint8Array(n);
267
+ baseCrypto.getRandomValues(arr);
268
+ return arr;
269
+ };
270
+ }
271
+ } catch (err) {
272
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
273
+ console.warn('[Para React Native shim] Failed to patch brorand RNG', err);
274
+ }
48
275
  }
49
- webcrypto.getRandomValues = peculiarCrypto.getRandomValues.bind(peculiarCrypto);
276
+ ensureCreateECDH();
277
+
278
+ return { baseCrypto, peculiarCrypto };
279
+ };
280
+
281
+ const setupCryptoPolyfills = () => {
282
+ const { peculiarCrypto } = ensureParaCrypto();
283
+
284
+ if (webcrypto && typeof webcrypto === 'object') {
285
+ webcrypto.getRandomValues = peculiarCrypto.getRandomValues.bind(peculiarCrypto);
286
+ }
287
+
50
288
  if (typeof Forge === 'undefined') {
51
289
  throw new Error('node-forge not loaded');
52
290
  }
@@ -68,15 +306,89 @@ const setupTextEncodingPolyfills = () => {
68
306
  globalThis.TextDecoder = TextDecoder;
69
307
  };
70
308
 
309
+ const setupWindowLocationPolyfill = () => {
310
+ const FALLBACK_HOST = 'para.mobile';
311
+ const FALLBACK_ORIGIN = `https://${FALLBACK_HOST}`;
312
+
313
+ const globalScope = globalThis;
314
+ if (!globalScope.window || typeof globalScope.window !== 'object') {
315
+ globalScope.window = globalScope;
316
+ }
317
+
318
+ const win = globalScope.window;
319
+ const existingLocation = (win && win.location) || globalScope.location;
320
+ const hasOrigin =
321
+ existingLocation &&
322
+ typeof existingLocation === 'object' &&
323
+ typeof existingLocation.origin === 'string' &&
324
+ typeof existingLocation.host === 'string';
325
+
326
+ if (hasOrigin) {
327
+ if (!globalScope.location) {
328
+ globalScope.location = existingLocation;
329
+ }
330
+ return;
331
+ }
332
+
333
+ const fallbackLocation = {
334
+ href: `${FALLBACK_ORIGIN}/`,
335
+ origin: FALLBACK_ORIGIN,
336
+ protocol: 'https:',
337
+ host: FALLBACK_HOST,
338
+ hostname: FALLBACK_HOST,
339
+ port: '',
340
+ pathname: '/',
341
+ };
342
+
343
+ const patchedLocation =
344
+ typeof existingLocation === 'object' && existingLocation !== null
345
+ ? Object.assign({}, fallbackLocation, existingLocation)
346
+ : fallbackLocation;
347
+
348
+ win.location = patchedLocation;
349
+ globalScope.location = patchedLocation;
350
+ };
351
+
71
352
  const setupStructuredClonePolyfill = () => {
72
- if (typeof globalThis.structuredClone === 'undefined') {
73
- globalThis.structuredClone = structuredClone;
353
+ if (typeof globalThis.structuredClone === 'function') {
354
+ return;
74
355
  }
356
+
357
+ const structuredCloneImpl = resolveStructuredClone();
358
+ const safeStructuredClone = (value, options) => {
359
+ if (typeof structuredCloneImpl === 'function') {
360
+ try {
361
+ return structuredCloneImpl(value, options);
362
+ } catch (err) {
363
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
364
+ console.warn('[Para React Native shim] structuredClone polyfill failed, falling back to JSON clone', err);
365
+ }
366
+ }
367
+ }
368
+ try {
369
+ return JSON.parse(JSON.stringify(value));
370
+ } catch (_err) {
371
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
372
+ console.warn('[Para React Native shim] JSON clone failed, returning original value', _err);
373
+ }
374
+ return value;
375
+ }
376
+ };
377
+
378
+ Object.defineProperty(globalThis, 'structuredClone', {
379
+ value: safeStructuredClone,
380
+ configurable: true,
381
+ enumerable: false,
382
+ writable: true,
383
+ });
75
384
  };
76
385
 
77
386
  setupProcessPolyfill();
78
387
  setupBufferPolyfill();
79
388
  setupBase64Polyfills();
389
+ setupWindowLocationPolyfill();
80
390
  setupCryptoPolyfills();
81
391
  setupTextEncodingPolyfills();
82
392
  setupStructuredClonePolyfill();
393
+
394
+ export { ensureParaCrypto, ensureCreateECDH };