@naculus/connect-native 0.4.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/dist/index.js ADDED
@@ -0,0 +1,331 @@
1
+ // src/codec.ts
2
+ var B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
3
+ function base58Encode(bytes) {
4
+ let zeros = 0;
5
+ while (zeros < bytes.length && bytes[zeros] === 0) zeros++;
6
+ let value = 0n;
7
+ for (const byte of bytes) value = value * 256n + BigInt(byte);
8
+ let out = "";
9
+ while (value > 0n) {
10
+ out = B58[Number(value % 58n)] + out;
11
+ value /= 58n;
12
+ }
13
+ return "1".repeat(zeros) + out;
14
+ }
15
+ function base64Decode(value) {
16
+ return Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
17
+ }
18
+
19
+ // src/coinbase.ts
20
+ function coinbaseMobileWallet(provider, info = {}) {
21
+ return {
22
+ info: {
23
+ uuid: info.uuid ?? "coinbase-mwp",
24
+ name: info.name ?? "Coinbase Wallet",
25
+ icon: info.icon ?? "data:image/svg+xml;base64,",
26
+ rdns: info.rdns ?? "com.coinbase.wallet"
27
+ },
28
+ provider
29
+ };
30
+ }
31
+
32
+ // src/keystore.ts
33
+ import {
34
+ KeyStoreStorageAdapter
35
+ } from "@naculus/wallet-engine";
36
+ function keystoreWalletStorage(options) {
37
+ const { secureStore, asyncStorage } = options;
38
+ const name = options.name ?? "naculus_wallet";
39
+ const keyName = `${name}.key`;
40
+ const recordName = `${name}.sealed`;
41
+ const access = secureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY !== void 0 ? { keychainAccessible: secureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY } : void 0;
42
+ return new KeyStoreStorageAdapter({
43
+ slot: name,
44
+ keyStore: {
45
+ load: () => secureStore.getItemAsync(keyName, access),
46
+ save: (value) => secureStore.setItemAsync(keyName, value, access),
47
+ delete: () => secureStore.deleteItemAsync(keyName)
48
+ },
49
+ blobStore: {
50
+ read: () => asyncStorage.getItem(recordName),
51
+ write: (value) => asyncStorage.setItem(recordName, value),
52
+ remove: () => asyncStorage.removeItem(recordName)
53
+ }
54
+ });
55
+ }
56
+
57
+ // src/mwa.ts
58
+ import { parseSolanaTransaction, WalletError } from "@naculus/connect-core";
59
+ var SIGNATURE_LENGTH = 64;
60
+ function kitTransactionFromWire(wire) {
61
+ const tx = parseSolanaTransaction(wire);
62
+ const signatures = {};
63
+ for (let i = 0; i < tx.numRequiredSignatures; i++) {
64
+ const signature = tx.signatures[i];
65
+ signatures[tx.accountKeys[i]] = signature.some((b) => b !== 0) ? signature : null;
66
+ }
67
+ return { messageBytes: tx.message, signatures };
68
+ }
69
+ function wireFromKitTransaction(tx) {
70
+ const count = Object.keys(tx.signatures).length;
71
+ if (count === 0 || count > 127) {
72
+ throw new WalletError("invalid_input", "Unexpected signature count.");
73
+ }
74
+ const unsigned = new Uint8Array(1 + 64 * count + tx.messageBytes.length);
75
+ unsigned[0] = count;
76
+ unsigned.set(tx.messageBytes, 1 + 64 * count);
77
+ const parsed = parseSolanaTransaction(unsigned);
78
+ const out = unsigned.slice();
79
+ parsed.accountKeys.slice(0, parsed.numRequiredSignatures).forEach((k, i) => {
80
+ const signature = tx.signatures[k];
81
+ if (!signature) return;
82
+ if (signature.length !== 64) {
83
+ throw new WalletError(
84
+ "signature_rejected",
85
+ "The wallet returned a malformed signature."
86
+ );
87
+ }
88
+ out.set(signature, 1 + 64 * i);
89
+ });
90
+ return out;
91
+ }
92
+ function createMobileWalletAdapterWallet(options) {
93
+ const { transact, identity, chain } = options;
94
+ let authToken;
95
+ let account = null;
96
+ const listeners = /* @__PURE__ */ new Set();
97
+ const authorize = async (wallet) => {
98
+ const result = await wallet.authorize({
99
+ identity,
100
+ chain,
101
+ ...authToken ? { auth_token: authToken } : {}
102
+ });
103
+ authToken = result.auth_token;
104
+ const first = result.accounts[0];
105
+ if (!first) {
106
+ throw new WalletError(
107
+ "wallet_unavailable",
108
+ "The wallet returned no account."
109
+ );
110
+ }
111
+ const publicKey = base64Decode(first.address);
112
+ if (publicKey.length !== 32) {
113
+ throw new WalletError(
114
+ "wallet_unavailable",
115
+ "The wallet returned a malformed account."
116
+ );
117
+ }
118
+ const previous = account?.address;
119
+ account = {
120
+ address: base58Encode(publicKey),
121
+ base64: first.address,
122
+ publicKey
123
+ };
124
+ if (previous !== void 0 && previous !== account.address) {
125
+ for (const listener of listeners) {
126
+ listener({ accounts: [standardAccount()] });
127
+ }
128
+ }
129
+ return account;
130
+ };
131
+ const standardAccount = () => {
132
+ if (!account) {
133
+ throw new WalletError("session_expired", "Connect the wallet first.");
134
+ }
135
+ return {
136
+ address: account.address,
137
+ publicKey: account.publicKey,
138
+ chains: [chain],
139
+ features: [
140
+ "solana:signMessage",
141
+ "solana:signTransaction",
142
+ "solana:signAndSendTransaction"
143
+ ]
144
+ };
145
+ };
146
+ return {
147
+ version: "1.0.0",
148
+ name: options.name ?? "Mobile Wallet Adapter",
149
+ icon: options.icon ?? "data:image/svg+xml;base64,",
150
+ chains: [chain],
151
+ get accounts() {
152
+ return account ? [standardAccount()] : [];
153
+ },
154
+ features: {
155
+ "standard:connect": {
156
+ version: "1.0.0",
157
+ connect: async () => {
158
+ await transact((wallet) => authorize(wallet));
159
+ for (const listener of listeners)
160
+ listener({ accounts: [standardAccount()] });
161
+ return { accounts: [standardAccount()] };
162
+ }
163
+ },
164
+ "standard:disconnect": {
165
+ version: "1.0.0",
166
+ disconnect: async () => {
167
+ const token = authToken;
168
+ authToken = void 0;
169
+ account = null;
170
+ for (const listener of listeners) listener({ accounts: [] });
171
+ if (token) {
172
+ await transact(
173
+ (wallet) => wallet.deauthorize({ auth_token: token })
174
+ ).catch(() => {
175
+ });
176
+ }
177
+ }
178
+ },
179
+ "standard:events": {
180
+ version: "1.0.0",
181
+ on: (_event, listener) => {
182
+ listeners.add(listener);
183
+ return () => listeners.delete(listener);
184
+ }
185
+ },
186
+ "solana:signMessage": {
187
+ version: "1.0.0",
188
+ signMessage: async (message) => transact(async (wallet) => {
189
+ const { base64 } = await authorize(wallet);
190
+ const [signed] = await wallet.signMessages({
191
+ addresses: [base64],
192
+ payloads: [message]
193
+ });
194
+ if (!signed || signed.length < SIGNATURE_LENGTH) {
195
+ throw new WalletError(
196
+ "signature_rejected",
197
+ "No signature returned."
198
+ );
199
+ }
200
+ return { signature: signed.slice(-SIGNATURE_LENGTH) };
201
+ })
202
+ },
203
+ "solana:signTransaction": {
204
+ version: "1.0.0",
205
+ signTransaction: async (transaction) => transact(async (wallet) => {
206
+ await authorize(wallet);
207
+ const [signed] = await wallet.signTransactions({
208
+ transactions: [kitTransactionFromWire(transaction)]
209
+ });
210
+ if (!signed) {
211
+ throw new WalletError(
212
+ "signature_rejected",
213
+ "No transaction returned."
214
+ );
215
+ }
216
+ return { signedTransaction: wireFromKitTransaction(signed) };
217
+ })
218
+ },
219
+ "solana:signAndSendTransaction": {
220
+ version: "1.0.0",
221
+ signAndSendTransaction: async (transaction) => transact(async (wallet) => {
222
+ await authorize(wallet);
223
+ const [signature] = await wallet.signAndSendTransactions({
224
+ transactions: [kitTransactionFromWire(transaction)]
225
+ });
226
+ if (!signature) {
227
+ throw new WalletError("tx_failed", "No signature returned.");
228
+ }
229
+ return { signature: base58Encode(signature) };
230
+ })
231
+ }
232
+ }
233
+ };
234
+ }
235
+
236
+ // src/platform.ts
237
+ import { setPlatformOverride } from "@naculus/connect-core";
238
+ function setupNaculusNative(os) {
239
+ if (os === "ios") setPlatformOverride("native-ios");
240
+ else if (os === "android") setPlatformOverride("native-android");
241
+ else setPlatformOverride(null);
242
+ }
243
+
244
+ // src/storage.ts
245
+ function asyncStorageAdapter(storage, prefix = "naculus:") {
246
+ const k = (key) => `${prefix}${key}`;
247
+ return {
248
+ async get(key) {
249
+ const raw = await storage.getItem(k(key));
250
+ if (raw === null) return null;
251
+ try {
252
+ return JSON.parse(raw);
253
+ } catch {
254
+ return null;
255
+ }
256
+ },
257
+ async set(key, value) {
258
+ await storage.setItem(k(key), JSON.stringify(value));
259
+ },
260
+ async remove(key) {
261
+ await storage.removeItem(k(key));
262
+ },
263
+ async clear() {
264
+ const keys = (await storage.getAllKeys()).filter(
265
+ (key) => key.startsWith(prefix)
266
+ );
267
+ if (keys.length > 0) await storage.multiRemove(keys);
268
+ },
269
+ async has(key) {
270
+ return await storage.getItem(k(key)) !== null;
271
+ },
272
+ isAvailable() {
273
+ return true;
274
+ }
275
+ };
276
+ }
277
+ function asyncStorageSessionStorage(storage, key = "naculus_web3_session") {
278
+ return {
279
+ async load() {
280
+ const raw = await storage.getItem(key);
281
+ if (raw === null) return null;
282
+ try {
283
+ return JSON.parse(raw);
284
+ } catch {
285
+ await storage.removeItem(key);
286
+ return null;
287
+ }
288
+ },
289
+ async save(session) {
290
+ await storage.setItem(key, JSON.stringify(session));
291
+ },
292
+ async clear() {
293
+ await storage.removeItem(key);
294
+ }
295
+ };
296
+ }
297
+
298
+ // src/walletconnect.ts
299
+ function nativeWalletConnect(linking) {
300
+ return {
301
+ openUrl: async (url) => {
302
+ await linking.openURL(url);
303
+ }
304
+ };
305
+ }
306
+ async function installedWallets(linking, wallets) {
307
+ const checks = await Promise.all(
308
+ wallets.map(async (wallet) => {
309
+ try {
310
+ return await linking.canOpenURL(wallet.deepLink);
311
+ } catch {
312
+ return false;
313
+ }
314
+ })
315
+ );
316
+ return wallets.filter((_, i) => checks[i]);
317
+ }
318
+ export {
319
+ asyncStorageAdapter,
320
+ asyncStorageSessionStorage,
321
+ base58Encode,
322
+ coinbaseMobileWallet,
323
+ createMobileWalletAdapterWallet,
324
+ installedWallets,
325
+ keystoreWalletStorage,
326
+ kitTransactionFromWire,
327
+ nativeWalletConnect,
328
+ setupNaculusNative,
329
+ wireFromKitTransaction
330
+ };
331
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/codec.ts","../src/coinbase.ts","../src/keystore.ts","../src/mwa.ts","../src/platform.ts","../src/storage.ts","../src/walletconnect.ts"],"sourcesContent":["const B58 = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n\n/** base58 (Bitcoin alphabet), as Solana addresses and signatures use. */\nexport function base58Encode(bytes: Uint8Array): string {\n let zeros = 0;\n while (zeros < bytes.length && bytes[zeros] === 0) zeros++;\n let value = 0n;\n for (const byte of bytes) value = value * 256n + BigInt(byte);\n let out = \"\";\n while (value > 0n) {\n out = B58[Number(value % 58n)] + out;\n value /= 58n;\n }\n return \"1\".repeat(zeros) + out;\n}\n\nexport function base64Encode(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary);\n}\n\nexport function base64Decode(value: string): Uint8Array {\n return Uint8Array.from(atob(value), (c) => c.charCodeAt(0));\n}\n","import type { InjectedProviderRegistration } from \"@naculus/connect-appkit-react\";\n\n/**\n * Coinbase Wallet on React Native through Mobile Wallet Protocol. The app\n * builds the provider — `new EIP1193Provider({ metadata, wallet:\n * Wallets.CoinbaseSmartWallet })` from `@mobile-wallet-protocol/client` —\n * and this registers it as an injected wallet (pass in\n * `config.injectedProviders`); connect with `connectInjected(\"coinbase-mwp\")`.\n */\nexport function coinbaseMobileWallet(\n provider: unknown,\n info: Partial<InjectedProviderRegistration[\"info\"]> = {},\n): InjectedProviderRegistration {\n return {\n info: {\n uuid: info.uuid ?? \"coinbase-mwp\",\n name: info.name ?? \"Coinbase Wallet\",\n icon: info.icon ?? \"data:image/svg+xml;base64,\",\n rdns: info.rdns ?? \"com.coinbase.wallet\",\n },\n provider,\n };\n}\n","import {\n KeyStoreStorageAdapter,\n type StorageAdapter,\n} from \"@naculus/wallet-engine\";\nimport type { AsyncStorageLike, SecureStoreLike } from \"./types\";\n\n/**\n * The embedded wallet's storage on a phone: wallet-engine's\n * `KeyStoreStorageAdapter` with its key in iOS Keychain / Android Keystore\n * (`expo-secure-store`, this device only, when unlocked) and the sealed record\n * in AsyncStorage. Sealing happens inside wallet-engine; this only moves two\n * opaque strings. Pass as `embeddedConfig.storage`.\n *\n * Isolation is weaker than the web's worker: React Native has no Web Worker,\n * so signing runs on the JS thread. The Keystore makes the sealed record\n * useless off the device and keeps the key out of AsyncStorage. Needs\n * `crypto.getRandomValues` (`react-native-get-random-values`, which\n * `@walletconnect/react-native-compat` installs).\n */\nexport interface KeystoreWalletStorageOptions {\n secureStore: SecureStoreLike;\n asyncStorage: AsyncStorageLike;\n /** Names both the Keystore entry and the AsyncStorage record. */\n name?: string;\n}\n\nexport function keystoreWalletStorage(\n options: KeystoreWalletStorageOptions,\n): StorageAdapter {\n const { secureStore, asyncStorage } = options;\n const name = options.name ?? \"naculus_wallet\";\n const keyName = `${name}.key`;\n const recordName = `${name}.sealed`;\n const access =\n secureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY !== undefined\n ? { keychainAccessible: secureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY }\n : undefined;\n return new KeyStoreStorageAdapter({\n slot: name,\n keyStore: {\n load: () => secureStore.getItemAsync(keyName, access),\n save: (value) => secureStore.setItemAsync(keyName, value, access),\n delete: () => secureStore.deleteItemAsync(keyName),\n },\n blobStore: {\n read: () => asyncStorage.getItem(recordName),\n write: (value) => asyncStorage.setItem(recordName, value),\n remove: () => asyncStorage.removeItem(recordName),\n },\n });\n}\n","import { parseSolanaTransaction, WalletError } from \"@naculus/connect-core\";\nimport { base58Encode, base64Decode } from \"./codec\";\n\n/**\n * Solana Mobile Wallet Adapter as a Wallet Standard wallet, for\n * connector-solana's `registerWallet` (pass it in `config.solanaWallets`).\n *\n * Each operation opens one MWA session with `transact` (from\n * `@solana-mobile/mobile-wallet-adapter-protocol-kit`, passed in by the app),\n * re-authorizes with the stored auth token and asks the wallet app. Wire\n * transactions become the Kit `{ messageBytes, signatures }` objects that\n * package expects and back; the signer order is taken from the message.\n */\n\n/** MWA's app identity, shown by the wallet. */\nexport interface MwaAppIdentity {\n name?: string;\n uri?: string;\n icon?: string;\n}\n\ntype KitTransaction = {\n messageBytes: Uint8Array;\n signatures: Record<string, Uint8Array | null>;\n};\n\n/** The members of the Kit `KitMobileWallet` used here. */\nexport interface MwaWalletLike {\n authorize(params: {\n identity: MwaAppIdentity;\n chain?: string;\n auth_token?: string;\n }): Promise<{\n accounts: readonly { address: string; label?: string }[];\n auth_token: string;\n }>;\n deauthorize(params: { auth_token: string }): Promise<unknown>;\n signMessages(params: {\n addresses: string[];\n payloads: Uint8Array[];\n }): Promise<Uint8Array[]>;\n signTransactions(params: {\n transactions: KitTransaction[];\n }): Promise<KitTransaction[]>;\n signAndSendTransactions(params: {\n transactions: KitTransaction[];\n }): Promise<Uint8Array[]>;\n}\n\nexport type MwaTransact = <T>(\n callback: (wallet: MwaWalletLike) => Promise<T>,\n) => Promise<T>;\n\nexport interface MobileWalletAdapterOptions {\n transact: MwaTransact;\n identity: MwaAppIdentity;\n /** MWA 2.0 chain id: `solana:mainnet`, `solana:devnet` or `solana:testnet`. */\n chain: \"solana:mainnet\" | \"solana:devnet\" | \"solana:testnet\";\n name?: string;\n icon?: string;\n}\n\nconst SIGNATURE_LENGTH = 64;\n\n/** Wire transaction → Kit transaction (signatures keyed by signer address). */\nexport function kitTransactionFromWire(wire: Uint8Array): KitTransaction {\n const tx = parseSolanaTransaction(wire);\n const signatures: Record<string, Uint8Array | null> = {};\n for (let i = 0; i < tx.numRequiredSignatures; i++) {\n const signature = tx.signatures[i] as Uint8Array;\n signatures[tx.accountKeys[i] as string] = signature.some((b) => b !== 0)\n ? signature\n : null;\n }\n return { messageBytes: tx.message, signatures };\n}\n\n/** Kit transaction → wire, in the message's signer order. */\nexport function wireFromKitTransaction(tx: KitTransaction): Uint8Array {\n const count = Object.keys(tx.signatures).length;\n if (count === 0 || count > 127) {\n throw new WalletError(\"invalid_input\", \"Unexpected signature count.\");\n }\n const unsigned = new Uint8Array(1 + 64 * count + tx.messageBytes.length);\n unsigned[0] = count;\n unsigned.set(tx.messageBytes, 1 + 64 * count);\n const parsed = parseSolanaTransaction(unsigned);\n const out = unsigned.slice();\n parsed.accountKeys.slice(0, parsed.numRequiredSignatures).forEach((k, i) => {\n const signature = tx.signatures[k];\n if (!signature) return;\n if (signature.length !== 64) {\n throw new WalletError(\n \"signature_rejected\",\n \"The wallet returned a malformed signature.\",\n );\n }\n out.set(signature, 1 + 64 * i);\n });\n return out;\n}\n\nexport function createMobileWalletAdapterWallet(\n options: MobileWalletAdapterOptions,\n) {\n const { transact, identity, chain } = options;\n let authToken: string | undefined;\n let account: {\n address: string;\n base64: string;\n publicKey: Uint8Array;\n } | null = null;\n const listeners = new Set<(change: unknown) => void>();\n\n const authorize = async (wallet: MwaWalletLike) => {\n const result = await wallet.authorize({\n identity,\n chain,\n ...(authToken ? { auth_token: authToken } : {}),\n });\n authToken = result.auth_token;\n const first = result.accounts[0];\n if (!first) {\n throw new WalletError(\n \"wallet_unavailable\",\n \"The wallet returned no account.\",\n );\n }\n const publicKey = base64Decode(first.address);\n if (publicKey.length !== 32) {\n throw new WalletError(\n \"wallet_unavailable\",\n \"The wallet returned a malformed account.\",\n );\n }\n const previous = account?.address;\n account = {\n address: base58Encode(publicKey),\n base64: first.address,\n publicKey,\n };\n // The user may pick another account when the wallet re-authorizes: tell\n // the connector, so the session never shows one account while another\n // signs (as the official wallet-standard-mobile adapter does).\n if (previous !== undefined && previous !== account.address) {\n for (const listener of listeners) {\n listener({ accounts: [standardAccount()] });\n }\n }\n return account;\n };\n\n const standardAccount = () => {\n if (!account) {\n throw new WalletError(\"session_expired\", \"Connect the wallet first.\");\n }\n return {\n address: account.address,\n publicKey: account.publicKey,\n chains: [chain],\n features: [\n \"solana:signMessage\",\n \"solana:signTransaction\",\n \"solana:signAndSendTransaction\",\n ],\n };\n };\n\n return {\n version: \"1.0.0\" as const,\n name: options.name ?? \"Mobile Wallet Adapter\",\n icon: options.icon ?? \"data:image/svg+xml;base64,\",\n chains: [chain],\n get accounts() {\n return account ? [standardAccount()] : [];\n },\n features: {\n \"standard:connect\": {\n version: \"1.0.0\",\n connect: async () => {\n await transact((wallet) => authorize(wallet));\n for (const listener of listeners)\n listener({ accounts: [standardAccount()] });\n return { accounts: [standardAccount()] };\n },\n },\n \"standard:disconnect\": {\n version: \"1.0.0\",\n disconnect: async () => {\n const token = authToken;\n authToken = undefined;\n account = null;\n for (const listener of listeners) listener({ accounts: [] });\n if (token) {\n await transact((wallet) =>\n wallet.deauthorize({ auth_token: token }),\n ).catch(() => {});\n }\n },\n },\n \"standard:events\": {\n version: \"1.0.0\",\n on: (_event: string, listener: (change: unknown) => void) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n },\n \"solana:signMessage\": {\n version: \"1.0.0\",\n signMessage: async (message: Uint8Array) =>\n transact(async (wallet) => {\n const { base64 } = await authorize(wallet);\n const [signed] = await wallet.signMessages({\n addresses: [base64],\n payloads: [message],\n });\n if (!signed || signed.length < SIGNATURE_LENGTH) {\n throw new WalletError(\n \"signature_rejected\",\n \"No signature returned.\",\n );\n }\n // MWA returns the message with the signature appended.\n return { signature: signed.slice(-SIGNATURE_LENGTH) };\n }),\n },\n \"solana:signTransaction\": {\n version: \"1.0.0\",\n signTransaction: async (transaction: Uint8Array) =>\n transact(async (wallet) => {\n await authorize(wallet);\n const [signed] = await wallet.signTransactions({\n transactions: [kitTransactionFromWire(transaction)],\n });\n if (!signed) {\n throw new WalletError(\n \"signature_rejected\",\n \"No transaction returned.\",\n );\n }\n return { signedTransaction: wireFromKitTransaction(signed) };\n }),\n },\n \"solana:signAndSendTransaction\": {\n version: \"1.0.0\",\n signAndSendTransaction: async (transaction: Uint8Array) =>\n transact(async (wallet) => {\n await authorize(wallet);\n const [signature] = await wallet.signAndSendTransactions({\n transactions: [kitTransactionFromWire(transaction)],\n });\n if (!signature) {\n throw new WalletError(\"tx_failed\", \"No signature returned.\");\n }\n return { signature: base58Encode(signature) };\n }),\n },\n },\n };\n}\n","import { setPlatformOverride } from \"@naculus/connect-core\";\n\n/**\n * Tell connect-core it runs in a native app (`Platform.OS` from\n * react-native). React Native cannot be told apart from a browser by\n * `navigator`; without this, connectors record sessions as `desktop-web` and\n * the connector manager does not prefer mobile-capable connectors.\n */\nexport function setupNaculusNative(os: string): void {\n if (os === \"ios\") setPlatformOverride(\"native-ios\");\n else if (os === \"android\") setPlatformOverride(\"native-android\");\n else setPlatformOverride(null);\n}\n","import type {\n SessionStorage,\n StorageAdapter,\n UniversalWalletSession,\n} from \"@naculus/connect-core\";\nimport type { AsyncStorageLike } from \"./types\";\n\n/** connect-core `StorageAdapter` over AsyncStorage, under a key prefix. */\nexport function asyncStorageAdapter(\n storage: AsyncStorageLike,\n prefix = \"naculus:\",\n): StorageAdapter {\n const k = (key: string) => `${prefix}${key}`;\n return {\n async get<T>(key: string): Promise<T | null> {\n const raw = await storage.getItem(k(key));\n if (raw === null) return null;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n },\n async set<T>(key: string, value: T): Promise<void> {\n await storage.setItem(k(key), JSON.stringify(value));\n },\n async remove(key: string): Promise<void> {\n await storage.removeItem(k(key));\n },\n async clear(): Promise<void> {\n const keys = (await storage.getAllKeys()).filter((key) =>\n key.startsWith(prefix),\n );\n if (keys.length > 0) await storage.multiRemove(keys);\n },\n async has(key: string): Promise<boolean> {\n return (await storage.getItem(k(key))) !== null;\n },\n isAvailable(): boolean {\n return true;\n },\n };\n}\n\n/**\n * Where `Web3ConnectProvider` persists the connected session on React\n * Native (pass as `config.sessionStorage`); the web default is localStorage.\n */\nexport function asyncStorageSessionStorage(\n storage: AsyncStorageLike,\n key = \"naculus_web3_session\",\n): SessionStorage {\n return {\n async load(): Promise<UniversalWalletSession | null> {\n const raw = await storage.getItem(key);\n if (raw === null) return null;\n try {\n return JSON.parse(raw) as UniversalWalletSession;\n } catch {\n // An unreadable record is no session, not a crash at startup.\n await storage.removeItem(key);\n return null;\n }\n },\n async save(session: UniversalWalletSession): Promise<void> {\n await storage.setItem(key, JSON.stringify(session));\n },\n async clear(): Promise<void> {\n await storage.removeItem(key);\n },\n };\n}\n","import type { NativeWalletConnectOptions } from \"@naculus/connect-appkit-react\";\nimport type { LinkingLike } from \"./types\";\n\n/**\n * WalletConnect on React Native: deep links open through `Linking`. The\n * SignClient builds itself; with `@walletconnect/react-native-compat`\n * imported first in the app's entry file, its storage is AsyncStorage.\n */\nexport function nativeWalletConnect(\n linking: LinkingLike,\n): NativeWalletConnectOptions {\n return {\n openUrl: async (url) => {\n await linking.openURL(url);\n },\n };\n}\n\nexport interface WalletLinkTarget {\n id: string;\n name: string;\n /** Deep-link base the WalletConnect URI is appended to, e.g. `metamask://wc`. */\n deepLink: string;\n}\n\n/**\n * The wallets whose apps are installed. iOS answers only for schemes listed\n * under `LSApplicationQueriesSchemes` in Info.plist, Android only for those in\n * the manifest's `<queries>`; anything else reads as not installed.\n */\nexport async function installedWallets(\n linking: LinkingLike,\n wallets: readonly WalletLinkTarget[],\n): Promise<WalletLinkTarget[]> {\n const checks = await Promise.all(\n wallets.map(async (wallet) => {\n try {\n return await linking.canOpenURL(wallet.deepLink);\n } catch {\n return false;\n }\n }),\n );\n return wallets.filter((_, i) => checks[i]);\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@naculus/connect-native",
3
+ "version": "0.4.0",
4
+ "type": "module",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "peerDependencies": {
19
+ "@naculus/connect-core": "^0.5.0",
20
+ "@naculus/connect-appkit-react": "^0.4.0",
21
+ "@naculus/wallet-engine": "^0.5.0",
22
+ "react-native": "*",
23
+ "@react-native-async-storage/async-storage": "*",
24
+ "@walletconnect/react-native-compat": "*",
25
+ "@solana-mobile/mobile-wallet-adapter-protocol-kit": "*",
26
+ "@mobile-wallet-protocol/client": "*",
27
+ "expo-secure-store": "*"
28
+ },
29
+ "devDependencies": {
30
+ "@naculus/connect-core": "^0.5.0",
31
+ "@naculus/wallet-engine": "^0.5.0",
32
+ "tsup": "^8.5.1",
33
+ "typescript": "^5.9.3",
34
+ "@naculus/connect-appkit-react": "0.4.0"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public",
38
+ "registry": "https://registry.npmjs.org"
39
+ },
40
+ "sideEffects": false,
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/naculus-official/connect-appkit.git"
44
+ },
45
+ "license": "MIT",
46
+ "description": "Naculus Connect for React Native — AsyncStorage sessions, WalletConnect deep links, Solana Mobile Wallet Adapter, Coinbase Mobile Wallet Protocol and a Keystore-sealed embedded wallet, wired into the React hooks",
47
+ "homepage": "https://github.com/naculus-official/connect-appkit#readme",
48
+ "bugs": "https://github.com/naculus-official/connect-appkit/issues",
49
+ "keywords": [
50
+ "web3",
51
+ "wallet",
52
+ "react-native",
53
+ "expo",
54
+ "walletconnect",
55
+ "solana-mobile",
56
+ "mobile-wallet-adapter"
57
+ ],
58
+ "peerDependenciesMeta": {
59
+ "react-native": {
60
+ "optional": true
61
+ },
62
+ "@react-native-async-storage/async-storage": {
63
+ "optional": true
64
+ },
65
+ "@walletconnect/react-native-compat": {
66
+ "optional": true
67
+ },
68
+ "@solana-mobile/mobile-wallet-adapter-protocol-kit": {
69
+ "optional": true
70
+ },
71
+ "@mobile-wallet-protocol/client": {
72
+ "optional": true
73
+ },
74
+ "expo-secure-store": {
75
+ "optional": true
76
+ }
77
+ },
78
+ "scripts": {
79
+ "build": "tsup",
80
+ "clean": "rm -rf dist",
81
+ "test:run": "vitest run",
82
+ "typecheck": "tsc --noEmit"
83
+ }
84
+ }