@authon/react-native-wallet 0.0.0-stage → 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/CHANGELOG.md +20 -0
- package/README.md +251 -2
- package/android/build.gradle +40 -0
- package/android/consumer-rules.pro +10 -0
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/dev/authon/reactnative/wallet/AuthonWalletModule.kt +235 -0
- package/android/src/vendor/GENERATED.md +4 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/AndroidCapabilityProbe.kt +86 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/AuthonWallet.kt +218 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Bitcoin.kt +296 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Bytes.kt +74 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Chains.kt +157 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/CredentialManagerPasskeys.kt +192 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Der.kt +17 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/DeviceStore.kt +170 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/DialogWalletApprover.kt +141 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Eip712.kt +176 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Encodings.kt +133 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/EvmAccount.kt +45 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/EvmTransaction.kt +180 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/GfTables.kt +44 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/HdWallet.kt +130 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Http.kt +86 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/JsValues.kt +83 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/KeystoreDeviceStore.kt +236 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Prf.kt +111 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Proof.kt +134 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Shamir.kt +106 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/SignSummary.kt +335 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Solana.kt +195 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Tron.kt +184 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletApi.kt +361 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletCapabilities.kt +91 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletError.kt +74 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletOps.kt +551 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletTypes.kt +78 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Xrp.kt +193 -0
- package/build/authonContext.d.ts +19 -0
- package/build/authonContext.d.ts.map +1 -0
- package/build/authonContext.js +20 -0
- package/build/authonContext.js.map +1 -0
- package/build/client.d.ts +92 -0
- package/build/client.d.ts.map +1 -0
- package/build/client.js +431 -0
- package/build/client.js.map +1 -0
- package/build/errors.d.ts +25 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +15 -0
- package/build/errors.js.map +1 -0
- package/build/index.d.ts +7 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +4 -0
- package/build/index.js.map +1 -0
- package/build/native.d.ts +43 -0
- package/build/native.d.ts.map +1 -0
- package/build/native.js +12 -0
- package/build/native.js.map +1 -0
- package/build/types.d.ts +167 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +3 -0
- package/build/types.js.map +1 -0
- package/build/useAuthonWallet.d.ts +25 -0
- package/build/useAuthonWallet.d.ts.map +1 -0
- package/build/useAuthonWallet.js +77 -0
- package/build/useAuthonWallet.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/AuthonReactNativeWallet.podspec +41 -0
- package/ios/AuthonWalletModule.swift +233 -0
- package/ios/vendor/AuthonWallet/AuthonWallet.swift +232 -0
- package/ios/vendor/AuthonWallet/Capabilities.swift +149 -0
- package/ios/vendor/AuthonWallet/Chains/Bitcoin.swift +296 -0
- package/ios/vendor/AuthonWallet/Chains/Solana.swift +207 -0
- package/ios/vendor/AuthonWallet/Chains/Tron.swift +181 -0
- package/ios/vendor/AuthonWallet/Chains/XRP.swift +191 -0
- package/ios/vendor/AuthonWallet/Crypto/BIP39Wordlist.swift +262 -0
- package/ios/vendor/AuthonWallet/Crypto/Chains.swift +278 -0
- package/ios/vendor/AuthonWallet/Crypto/HDKey.swift +108 -0
- package/ios/vendor/AuthonWallet/Crypto/Secp256k1.swift +128 -0
- package/ios/vendor/AuthonWallet/Crypto/Shamir.swift +123 -0
- package/ios/vendor/AuthonWallet/EVM/Address.swift +57 -0
- package/ios/vendor/AuthonWallet/EVM/EvmAccount.swift +48 -0
- package/ios/vendor/AuthonWallet/EVM/Transaction.swift +165 -0
- package/ios/vendor/AuthonWallet/EVM/TypedData.swift +193 -0
- package/ios/vendor/AuthonWallet/Support/BigInteger.swift +220 -0
- package/ios/vendor/AuthonWallet/Support/Bytes.swift +89 -0
- package/ios/vendor/AuthonWallet/Support/JSONValue.swift +341 -0
- package/ios/vendor/AuthonWallet/UI/WalletApprover.swift +250 -0
- package/ios/vendor/AuthonWallet/Wallet/Backup.swift +69 -0
- package/ios/vendor/AuthonWallet/Wallet/DeviceStore.swift +351 -0
- package/ios/vendor/AuthonWallet/Wallet/Passkeys.swift +190 -0
- package/ios/vendor/AuthonWallet/Wallet/Proof.swift +143 -0
- package/ios/vendor/AuthonWallet/Wallet/Shares.swift +116 -0
- package/ios/vendor/AuthonWallet/Wallet/Summary.swift +342 -0
- package/ios/vendor/AuthonWallet/Wallet/WalletAPI.swift +361 -0
- package/ios/vendor/AuthonWallet/Wallet/WalletOps.swift +654 -0
- package/ios/vendor/AuthonWallet/WalletError.swift +84 -0
- package/ios/vendor/CKeccak/LICENSE +22 -0
- package/ios/vendor/CKeccak/README.md +30 -0
- package/ios/vendor/CKeccak/byte_order.h +59 -0
- package/ios/vendor/CKeccak/include/ckeccak.h +13 -0
- package/ios/vendor/CKeccak/memzero.c +75 -0
- package/ios/vendor/CKeccak/memzero.h +8 -0
- package/ios/vendor/CKeccak/options.h +99 -0
- package/ios/vendor/CKeccak/ripemd160.c +343 -0
- package/ios/vendor/CKeccak/ripemd160.h +22 -0
- package/ios/vendor/CKeccak/sha3.c +413 -0
- package/ios/vendor/CKeccak/sha3.h +89 -0
- package/ios/vendor/CMonocypher/LICENCE.md +173 -0
- package/ios/vendor/CMonocypher/README.md +27 -0
- package/ios/vendor/CMonocypher/include/monocypher-ed25519.h +140 -0
- package/ios/vendor/CMonocypher/include/monocypher.h +321 -0
- package/ios/vendor/CMonocypher/monocypher-ed25519.c +500 -0
- package/ios/vendor/CMonocypher/monocypher.c +2956 -0
- package/ios/vendor/CSecp256k1/COPYING +19 -0
- package/ios/vendor/CSecp256k1/README.md +35 -0
- package/ios/vendor/CSecp256k1/build/precomputed_ecmult.c +3 -0
- package/ios/vendor/CSecp256k1/build/precomputed_ecmult_gen.c +3 -0
- package/ios/vendor/CSecp256k1/build/secp256k1.c +3 -0
- package/ios/vendor/CSecp256k1/include/secp256k1.h +899 -0
- package/ios/vendor/CSecp256k1/include/secp256k1_preallocated.h +134 -0
- package/ios/vendor/CSecp256k1/include/secp256k1_recovery.h +113 -0
- package/ios/vendor/CSecp256k1/src/assumptions.h +87 -0
- package/ios/vendor/CSecp256k1/src/checkmem.h +102 -0
- package/ios/vendor/CSecp256k1/src/ecdsa.h +21 -0
- package/ios/vendor/CSecp256k1/src/ecdsa_impl.h +304 -0
- package/ios/vendor/CSecp256k1/src/eckey.h +25 -0
- package/ios/vendor/CSecp256k1/src/eckey_impl.h +92 -0
- package/ios/vendor/CSecp256k1/src/ecmult.h +61 -0
- package/ios/vendor/CSecp256k1/src/ecmult_const.h +38 -0
- package/ios/vendor/CSecp256k1/src/ecmult_const_impl.h +399 -0
- package/ios/vendor/CSecp256k1/src/ecmult_gen.h +143 -0
- package/ios/vendor/CSecp256k1/src/ecmult_gen_impl.h +341 -0
- package/ios/vendor/CSecp256k1/src/ecmult_impl.h +853 -0
- package/ios/vendor/CSecp256k1/src/field.h +350 -0
- package/ios/vendor/CSecp256k1/src/field_10x26.h +57 -0
- package/ios/vendor/CSecp256k1/src/field_10x26_impl.h +1232 -0
- package/ios/vendor/CSecp256k1/src/field_5x52.h +62 -0
- package/ios/vendor/CSecp256k1/src/field_5x52_impl.h +522 -0
- package/ios/vendor/CSecp256k1/src/field_5x52_int128_impl.h +274 -0
- package/ios/vendor/CSecp256k1/src/field_impl.h +457 -0
- package/ios/vendor/CSecp256k1/src/group.h +212 -0
- package/ios/vendor/CSecp256k1/src/group_impl.h +974 -0
- package/ios/vendor/CSecp256k1/src/hash.h +44 -0
- package/ios/vendor/CSecp256k1/src/hash_impl.h +299 -0
- package/ios/vendor/CSecp256k1/src/hsort.h +33 -0
- package/ios/vendor/CSecp256k1/src/hsort_impl.h +125 -0
- package/ios/vendor/CSecp256k1/src/int128.h +90 -0
- package/ios/vendor/CSecp256k1/src/int128_impl.h +18 -0
- package/ios/vendor/CSecp256k1/src/int128_native.h +19 -0
- package/ios/vendor/CSecp256k1/src/int128_native_impl.h +94 -0
- package/ios/vendor/CSecp256k1/src/int128_struct.h +14 -0
- package/ios/vendor/CSecp256k1/src/int128_struct_impl.h +205 -0
- package/ios/vendor/CSecp256k1/src/modinv32.h +43 -0
- package/ios/vendor/CSecp256k1/src/modinv32_impl.h +725 -0
- package/ios/vendor/CSecp256k1/src/modinv64.h +47 -0
- package/ios/vendor/CSecp256k1/src/modinv64_impl.h +780 -0
- package/ios/vendor/CSecp256k1/src/modules/recovery/main_impl.h +159 -0
- package/ios/vendor/CSecp256k1/src/precomputed_ecmult.c +16456 -0
- package/ios/vendor/CSecp256k1/src/precomputed_ecmult.h +38 -0
- package/ios/vendor/CSecp256k1/src/precomputed_ecmult_gen.c +1779 -0
- package/ios/vendor/CSecp256k1/src/precomputed_ecmult_gen.h +26 -0
- package/ios/vendor/CSecp256k1/src/scalar.h +105 -0
- package/ios/vendor/CSecp256k1/src/scalar_4x64.h +19 -0
- package/ios/vendor/CSecp256k1/src/scalar_4x64_impl.h +1000 -0
- package/ios/vendor/CSecp256k1/src/scalar_8x32.h +19 -0
- package/ios/vendor/CSecp256k1/src/scalar_8x32_impl.h +816 -0
- package/ios/vendor/CSecp256k1/src/scalar_impl.h +321 -0
- package/ios/vendor/CSecp256k1/src/scalar_low.h +24 -0
- package/ios/vendor/CSecp256k1/src/scalar_low_impl.h +206 -0
- package/ios/vendor/CSecp256k1/src/scratch.h +44 -0
- package/ios/vendor/CSecp256k1/src/scratch_impl.h +99 -0
- package/ios/vendor/CSecp256k1/src/secp256k1.c +831 -0
- package/ios/vendor/CSecp256k1/src/selftest.h +32 -0
- package/ios/vendor/CSecp256k1/src/util.h +451 -0
- package/ios/vendor/GENERATED.md +4 -0
- package/package.json +66 -3
- package/src/authonContext.ts +30 -0
- package/src/client.ts +459 -0
- package/src/errors.ts +58 -0
- package/src/index.ts +26 -0
- package/src/native.ts +52 -0
- package/src/types.ts +171 -0
- package/src/useAuthonWallet.ts +103 -0
package/src/client.ts
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import { Platform } from 'expo-modules-core';
|
|
2
|
+
import { AuthonWalletError } from './errors';
|
|
3
|
+
import { getNativeModule, type AuthonWalletNativeModule, type NativeResult } from './native';
|
|
4
|
+
import type {
|
|
5
|
+
AuthonWalletOptions,
|
|
6
|
+
EIP1193Provider,
|
|
7
|
+
EmbeddedWallet,
|
|
8
|
+
EmbeddedWalletDevice,
|
|
9
|
+
Hex,
|
|
10
|
+
TypedDataPayload,
|
|
11
|
+
WalletAccount,
|
|
12
|
+
CapabilityCheck,
|
|
13
|
+
CapabilityStatus,
|
|
14
|
+
WalletAppConfig,
|
|
15
|
+
WalletCapabilities,
|
|
16
|
+
WalletChain,
|
|
17
|
+
WalletChainSignRequest,
|
|
18
|
+
WalletChangeListener,
|
|
19
|
+
WalletMessage,
|
|
20
|
+
WalletSignOptions,
|
|
21
|
+
WalletTransactionRequest,
|
|
22
|
+
} from './types';
|
|
23
|
+
|
|
24
|
+
const MAX_ACCOUNT_INDEX = 0x7fffffff;
|
|
25
|
+
let counter = 0;
|
|
26
|
+
|
|
27
|
+
const isObject = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
28
|
+
const str = (v: unknown): string | null => (typeof v === 'string' ? v : null);
|
|
29
|
+
|
|
30
|
+
/** Copies only the public fields, so nothing else the native side might add reaches the app. */
|
|
31
|
+
function toWallet(value: unknown): EmbeddedWallet {
|
|
32
|
+
if (!isObject(value) || typeof value.address !== 'string') throw new AuthonWalletError('UNKNOWN', 'Wallet returned no wallet');
|
|
33
|
+
const devices: EmbeddedWalletDevice[] = Array.isArray(value.devices)
|
|
34
|
+
? value.devices.filter(isObject).map((d) => ({
|
|
35
|
+
id: String(d.id),
|
|
36
|
+
label: str(d.label),
|
|
37
|
+
createdAt: str(d.createdAt),
|
|
38
|
+
lastUsedAt: str(d.lastUsedAt),
|
|
39
|
+
current: d.current === true,
|
|
40
|
+
}))
|
|
41
|
+
: [];
|
|
42
|
+
return {
|
|
43
|
+
address: value.address,
|
|
44
|
+
protected: value.protected === true,
|
|
45
|
+
needsRestore: value.needsRestore === true,
|
|
46
|
+
backupUpgradeRecommended: value.backupUpgradeRecommended === true,
|
|
47
|
+
devices,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const CHAINS: WalletChain[] = ['evm', 'tron', 'xrp', 'bitcoin', 'bitcoin-testnet', 'solana'];
|
|
52
|
+
|
|
53
|
+
function toAccounts(value: unknown): WalletAccount[] {
|
|
54
|
+
if (!Array.isArray(value)) throw new AuthonWalletError('UNKNOWN', 'Wallet returned no accounts');
|
|
55
|
+
return value.filter(isObject).map((a) => {
|
|
56
|
+
const account: WalletAccount = { index: Number(a.index), path: String(a.path), address: String(a.address) };
|
|
57
|
+
if (CHAINS.includes(a.chain as WalletChain)) account.chain = a.chain as WalletChain;
|
|
58
|
+
if (typeof a.publicKey === 'string' && /^(0x)?[0-9a-fA-F]+$/.test(a.publicKey)) account.publicKey = a.publicKey;
|
|
59
|
+
return account;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function toHex(value: unknown): Hex {
|
|
64
|
+
if (typeof value !== 'string' || !/^0x[0-9a-fA-F]*$/.test(value)) throw new AuthonWalletError('UNKNOWN', 'Wallet returned no signature');
|
|
65
|
+
return value as Hex;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function toAppConfig(value: unknown): WalletAppConfig {
|
|
69
|
+
if (!isObject(value)) throw new AuthonWalletError('UNKNOWN', 'Wallet returned no app config');
|
|
70
|
+
return {
|
|
71
|
+
projectId: str(value.projectId),
|
|
72
|
+
allowed: value.allowed === true,
|
|
73
|
+
approved: value.approved === true,
|
|
74
|
+
enabled: value.enabled === true,
|
|
75
|
+
chainIds: Array.isArray(value.chainIds) ? value.chainIds.map(Number) : [],
|
|
76
|
+
confirmSigning: value.confirmSigning !== false,
|
|
77
|
+
rpId: String(value.rpId ?? ''),
|
|
78
|
+
minFrameProtocol: typeof value.minFrameProtocol === 'number' ? value.minFrameProtocol : null,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const STATUSES: CapabilityStatus[] = ['available', 'unavailable', 'unknown'];
|
|
83
|
+
|
|
84
|
+
function toCheck(value: unknown): CapabilityCheck {
|
|
85
|
+
const v = isObject(value) ? value : {};
|
|
86
|
+
const status = STATUSES.includes(v.status as CapabilityStatus) ? (v.status as CapabilityStatus) : 'unknown';
|
|
87
|
+
return { status, reason: status === 'available' ? null : str(v.reason) ?? 'CHECK_FAILED' };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function toCapabilities(value: unknown): WalletCapabilities {
|
|
91
|
+
if (!isObject(value)) throw new AuthonWalletError('UNKNOWN', 'Wallet returned no capabilities');
|
|
92
|
+
const checks = {
|
|
93
|
+
app: toCheck(value.app),
|
|
94
|
+
wallet: toCheck(value.wallet),
|
|
95
|
+
secureStorage: toCheck(value.secureStorage),
|
|
96
|
+
deviceLock: toCheck(value.deviceLock),
|
|
97
|
+
passkeyBackup: toCheck(value.passkeyBackup),
|
|
98
|
+
};
|
|
99
|
+
const all = Object.values(checks).map((c) => c.status);
|
|
100
|
+
const ready: CapabilityStatus = all.includes('unavailable') ? 'unavailable' : all.includes('unknown') ? 'unknown' : 'available';
|
|
101
|
+
return { ready, ...checks, diagnosticId: str(value.diagnosticId) };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** bigint → 0x hex (transactions, like @authon/js). */
|
|
105
|
+
const txReplacer = (_key: string, v: unknown) => (typeof v === 'bigint' ? `0x${v.toString(16)}` : v);
|
|
106
|
+
/** bigint → decimal string (EIP-712 integer values). */
|
|
107
|
+
const typedReplacer = (_key: string, v: unknown) => (typeof v === 'bigint' ? v.toString(10) : v);
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The Authon embedded wallet in a React Native app. Signing runs in the native Swift / Kotlin
|
|
111
|
+
* wallet; JS only sees addresses, signatures, status and devices.
|
|
112
|
+
*/
|
|
113
|
+
export class AuthonWalletClient {
|
|
114
|
+
private readonly id = `authon-wallet-${++counter}`;
|
|
115
|
+
private readonly options: AuthonWalletOptions;
|
|
116
|
+
private readonly listeners = new Set<WalletChangeListener>();
|
|
117
|
+
private readonly accountAddresses = new Map<number, string>();
|
|
118
|
+
private configured: Promise<AuthonWalletNativeModule> | null = null;
|
|
119
|
+
private ensuring: Promise<EmbeddedWallet> | null = null;
|
|
120
|
+
/** Bumped by dispose(); results of calls started before it are dropped (DISPOSED). */
|
|
121
|
+
private generation = 0;
|
|
122
|
+
|
|
123
|
+
constructor(options: AuthonWalletOptions) {
|
|
124
|
+
if (!options?.publishableKey) throw new AuthonWalletError('INVALID_PARAMS', 'publishableKey is required');
|
|
125
|
+
if (typeof options.getToken !== 'function') throw new AuthonWalletError('INVALID_PARAMS', 'getToken is required');
|
|
126
|
+
this.options = options;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** App registration and wallet settings (does not need a session). */
|
|
130
|
+
async appConfig(): Promise<WalletAppConfig> {
|
|
131
|
+
const generation = this.generation;
|
|
132
|
+
const native = await this.native();
|
|
133
|
+
return toAppConfig(this.unwrap(await native.appConfig(this.id), generation));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Whether this device and app can use the wallet, before sign-in (no session, creates nothing,
|
|
138
|
+
* opens no UI). `ready === 'unavailable'`: tell the user before signing in; `unknown`: continue
|
|
139
|
+
* but expect that feature to fail. Answers for release builds (test options are ignored).
|
|
140
|
+
*/
|
|
141
|
+
async capabilities(): Promise<WalletCapabilities> {
|
|
142
|
+
const generation = this.generation;
|
|
143
|
+
const native = await this.native();
|
|
144
|
+
return toCapabilities(this.unwrap(await native.capabilities(this.id), generation));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** The signed-in user's wallet, or null when there is none yet. */
|
|
148
|
+
async get(): Promise<EmbeddedWallet | null> {
|
|
149
|
+
try {
|
|
150
|
+
return this.remember(toWallet(await this.call((n, id, token) => n.status(id, token))));
|
|
151
|
+
} catch (error) {
|
|
152
|
+
if (error instanceof AuthonWalletError && error.code === 'NOT_FOUND') return null;
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Same as `get()` (the frame method is `wallet.status`). */
|
|
158
|
+
status(): Promise<EmbeddedWallet | null> {
|
|
159
|
+
return this.get();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Creates the wallet (two-phase on the server), or returns the existing one. */
|
|
163
|
+
async create(): Promise<EmbeddedWallet> {
|
|
164
|
+
const wallet = this.remember(toWallet(await this.call((n, id, token) => n.create(id, token))));
|
|
165
|
+
this.notify(wallet);
|
|
166
|
+
return wallet;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Returns the wallet, creating it first when the user has none. Concurrent calls share one request. */
|
|
170
|
+
ensure(): Promise<EmbeddedWallet> {
|
|
171
|
+
if (this.ensuring) return this.ensuring;
|
|
172
|
+
const run = (async () => (await this.get()) ?? (await this.create()))();
|
|
173
|
+
this.ensuring = run;
|
|
174
|
+
const clear = () => {
|
|
175
|
+
if (this.ensuring === run) this.ensuring = null;
|
|
176
|
+
};
|
|
177
|
+
run.then(clear, clear);
|
|
178
|
+
return run;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** True when the wallet exists and has a passkey backup. */
|
|
182
|
+
async isProtected(): Promise<boolean> {
|
|
183
|
+
return (await this.get())?.protected === true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Addresses of accounts `start`..`start + count - 1` (public, no approval) on `chain` (default
|
|
188
|
+
* `evm`). Signing is EVM only for now; other chains are for receiving and display.
|
|
189
|
+
*/
|
|
190
|
+
async getAccounts(options: { start?: number; count?: number; chain?: WalletChain } = {}): Promise<WalletAccount[]> {
|
|
191
|
+
const start = options.start ?? 0;
|
|
192
|
+
const count = options.count ?? 1;
|
|
193
|
+
const chain = options.chain;
|
|
194
|
+
if (!Number.isInteger(start) || start < 0 || start > MAX_ACCOUNT_INDEX || !Number.isInteger(count) || count < 1) {
|
|
195
|
+
throw new AuthonWalletError('INVALID_PARAMS', 'start must be an integer 0..2147483647 and count a positive integer');
|
|
196
|
+
}
|
|
197
|
+
if (chain !== undefined && !CHAINS.includes(chain)) {
|
|
198
|
+
throw new AuthonWalletError('INVALID_PARAMS', `chain must be one of ${CHAINS.join(', ')}`);
|
|
199
|
+
}
|
|
200
|
+
const list = toAccounts(await this.call((n, id, token) => n.accounts(id, token, start, count, chain ?? 'evm')));
|
|
201
|
+
if (chain && list.some((account) => account.chain !== chain)) {
|
|
202
|
+
// A native wallet without multichain support would answer with EVM addresses.
|
|
203
|
+
throw new AuthonWalletError('UNSUPPORTED_TYPE', 'This wallet build does not support other chains yet');
|
|
204
|
+
}
|
|
205
|
+
// Only EVM addresses can name a signing account.
|
|
206
|
+
if (!chain || chain === 'evm') for (const account of list) this.accountAddresses.set(account.index, account.address);
|
|
207
|
+
return list;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* EIP-191 personal_sign. The native approval sheet / dialog shows the full text first when the
|
|
212
|
+
* project requires confirmation (default); rejecting gives USER_CANCELLED and nothing is signed.
|
|
213
|
+
*/
|
|
214
|
+
async signMessage(message: WalletMessage, options: WalletSignOptions = {}): Promise<Hex> {
|
|
215
|
+
let kind: 'text' | 'raw';
|
|
216
|
+
let text: string;
|
|
217
|
+
if (typeof message === 'string') {
|
|
218
|
+
kind = 'text';
|
|
219
|
+
text = message;
|
|
220
|
+
} else if (isObject(message) && typeof message.raw === 'string') {
|
|
221
|
+
kind = 'raw';
|
|
222
|
+
text = message.raw;
|
|
223
|
+
} else {
|
|
224
|
+
throw new AuthonWalletError('INVALID_PARAMS', 'message must be a string or { raw: hex }');
|
|
225
|
+
}
|
|
226
|
+
const [account, address] = await this.account(options.account);
|
|
227
|
+
return toHex(await this.call((n, id, token) => n.signMessage(id, token, kind, text, account, address)));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** EIP-712 v4 typed data. */
|
|
231
|
+
async signTypedData(typedData: TypedDataPayload, options: WalletSignOptions = {}): Promise<Hex> {
|
|
232
|
+
if (!isObject(typedData)) throw new AuthonWalletError('INVALID_PARAMS', 'typedData must be an object');
|
|
233
|
+
const json = JSON.stringify(typedData, typedReplacer);
|
|
234
|
+
const [account, address] = await this.account(options.account);
|
|
235
|
+
return toHex(await this.call((n, id, token) => n.signTypedData(id, token, json, account, address)));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Signs a legacy, EIP-2930 or EIP-1559 transaction and returns the serialized signed transaction. */
|
|
239
|
+
async signTransaction(tx: WalletTransactionRequest, options: WalletSignOptions = {}): Promise<Hex> {
|
|
240
|
+
if (!isObject(tx)) throw new AuthonWalletError('INVALID_PARAMS', 'transaction must be an object');
|
|
241
|
+
const json = JSON.stringify(tx, txReplacer);
|
|
242
|
+
const [account, address] = await this.account(options.account);
|
|
243
|
+
return toHex(await this.call((n, id, token) => n.signTransaction(id, token, json, account, address)));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Signs an app-built transaction on another chain (NATIVE.md section 12): Solana `{ signature }`,
|
|
248
|
+
* TRON `{ signature, txID }`, Bitcoin `{ psbt, txHex? }`, XRP `{ signature }`. The native approval
|
|
249
|
+
* UI shows what the transaction does (unknown programs / contracts are shown as high risk).
|
|
250
|
+
*/
|
|
251
|
+
async signChainTransaction(request: WalletChainSignRequest): Promise<Record<string, string>> {
|
|
252
|
+
if (!isObject(request) || (request.chain as WalletChain) === 'evm') {
|
|
253
|
+
throw new AuthonWalletError('INVALID_PARAMS', 'use signTransaction for EVM');
|
|
254
|
+
}
|
|
255
|
+
if (!CHAINS.includes(request.chain)) throw new AuthonWalletError('INVALID_PARAMS', `chain must be one of ${CHAINS.filter((c) => c !== 'evm').join(', ')}`);
|
|
256
|
+
const account = request.account ?? 0;
|
|
257
|
+
if (!Number.isInteger(account) || account < 0 || account > MAX_ACCOUNT_INDEX || typeof request.address !== 'string' || !request.address) {
|
|
258
|
+
throw new AuthonWalletError('INVALID_PARAMS', 'account index and its address are required');
|
|
259
|
+
}
|
|
260
|
+
if (!isObject(request.payload)) throw new AuthonWalletError('INVALID_PARAMS', 'payload must be an object');
|
|
261
|
+
const payload = JSON.stringify(request.payload, txReplacer);
|
|
262
|
+
const result = await this.call((n, id, token) => n.signChainTransaction(id, token, request.chain, account, request.address, payload));
|
|
263
|
+
if (!isObject(result)) throw new AuthonWalletError('UNKNOWN', 'Wallet returned no signature');
|
|
264
|
+
// Signatures and signed transactions only; anything that is not a string is dropped.
|
|
265
|
+
const out: Record<string, string> = {};
|
|
266
|
+
for (const [k, v] of Object.entries(result)) if (typeof v === 'string') out[k] = v;
|
|
267
|
+
return out;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Creates the passkey backup (iOS 18+ / Android with PRF support). Needs an approved app registration. */
|
|
271
|
+
async protect(): Promise<void> {
|
|
272
|
+
await this.call((n, id, token) => n.protect(id, token));
|
|
273
|
+
await this.refresh();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Replaces a legacy backup with a verifiable one, on a device that holds the wallet. */
|
|
277
|
+
async upgradeBackup(): Promise<void> {
|
|
278
|
+
await this.call((n, id, token) => n.upgradeBackup(id, token));
|
|
279
|
+
await this.refresh();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Restores the wallet on this install with the backup passkey. By default this install is added
|
|
284
|
+
* as another device; `resetOtherDevices: true` re-splits the key and revokes every other device.
|
|
285
|
+
*/
|
|
286
|
+
async restore(options: { resetOtherDevices?: boolean } = {}): Promise<void> {
|
|
287
|
+
const reset = options.resetOtherDevices === true;
|
|
288
|
+
await this.call((n, id, token) => n.restore(id, token, reset));
|
|
289
|
+
await this.refresh();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** Revokes a device (signed with this install's device key). */
|
|
293
|
+
async revokeDevice(deviceId: string): Promise<void> {
|
|
294
|
+
if (typeof deviceId !== 'string' || !deviceId) throw new AuthonWalletError('INVALID_PARAMS', 'deviceId required');
|
|
295
|
+
await this.call((n, id, token) => n.revokeDevice(id, token, deviceId));
|
|
296
|
+
await this.refresh();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
onChange(listener: WalletChangeListener): () => void {
|
|
300
|
+
this.listeners.add(listener);
|
|
301
|
+
return () => {
|
|
302
|
+
this.listeners.delete(listener);
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** A minimal EIP-1193 provider (same methods as @authon/js). */
|
|
307
|
+
provider(chainId: number): EIP1193Provider {
|
|
308
|
+
const hexChainId = `0x${chainId.toString(16)}`;
|
|
309
|
+
return {
|
|
310
|
+
request: async ({ method, params }) => {
|
|
311
|
+
const list = Array.isArray(params) ? params : [];
|
|
312
|
+
switch (method) {
|
|
313
|
+
case 'eth_chainId':
|
|
314
|
+
return hexChainId;
|
|
315
|
+
case 'eth_accounts': {
|
|
316
|
+
const wallet = await this.get();
|
|
317
|
+
return wallet ? [wallet.address] : [];
|
|
318
|
+
}
|
|
319
|
+
case 'eth_requestAccounts':
|
|
320
|
+
return [(await this.ensure()).address];
|
|
321
|
+
case 'personal_sign': {
|
|
322
|
+
const data = list[0];
|
|
323
|
+
if (typeof data !== 'string') throw new AuthonWalletError('INVALID_PARAMS', 'personal_sign requires a message');
|
|
324
|
+
return this.signMessage({ raw: data });
|
|
325
|
+
}
|
|
326
|
+
case 'eth_signTypedData_v4': {
|
|
327
|
+
const raw = list[1];
|
|
328
|
+
const typed = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
329
|
+
if (!isObject(typed)) throw new AuthonWalletError('INVALID_PARAMS', 'eth_signTypedData_v4 requires typed data');
|
|
330
|
+
return this.signTypedData(typed as unknown as TypedDataPayload);
|
|
331
|
+
}
|
|
332
|
+
case 'eth_signTransaction': {
|
|
333
|
+
const tx = list[0];
|
|
334
|
+
if (!isObject(tx)) throw new AuthonWalletError('INVALID_PARAMS', 'eth_signTransaction requires a transaction');
|
|
335
|
+
return this.signTransaction({ chainId: hexChainId, ...(tx as WalletTransactionRequest) });
|
|
336
|
+
}
|
|
337
|
+
default:
|
|
338
|
+
throw Object.assign(new AuthonWalletError('UNSUPPORTED_METHOD', `Method ${method} is not supported`), { code: 4200 });
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Call on sign-out: releases the native instance, rejects calls in flight with DISPOSED and
|
|
346
|
+
* forgets cached account addresses. The device share stays on the device. Like @authon/js, the
|
|
347
|
+
* client can be used again afterwards (it reconnects on the next call).
|
|
348
|
+
*/
|
|
349
|
+
dispose(): void {
|
|
350
|
+
this.generation++;
|
|
351
|
+
const configured = this.configured;
|
|
352
|
+
this.configured = null;
|
|
353
|
+
this.ensuring = null;
|
|
354
|
+
this.accountAddresses.clear();
|
|
355
|
+
configured?.then((n) => n.release(this.id)).catch(() => {});
|
|
356
|
+
this.notify(null);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Internals
|
|
360
|
+
|
|
361
|
+
private async native(): Promise<AuthonWalletNativeModule> {
|
|
362
|
+
if (!this.configured) {
|
|
363
|
+
const run = (async () => {
|
|
364
|
+
const native = getNativeModule();
|
|
365
|
+
if (!native) {
|
|
366
|
+
throw new AuthonWalletError(
|
|
367
|
+
'NOT_CONFIGURED',
|
|
368
|
+
'@authon/react-native-wallet native module not found. Use a development build (not Expo Go) and rebuild the app.',
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
const appId = this.options.appId;
|
|
372
|
+
const platformAppId = typeof appId === 'string' ? appId : Platform.OS === 'ios' ? appId?.ios : appId?.android;
|
|
373
|
+
this.unwrap(
|
|
374
|
+
await native.configure(this.id, {
|
|
375
|
+
publishableKey: this.options.publishableKey,
|
|
376
|
+
apiUrl: this.options.apiUrl ?? null,
|
|
377
|
+
appId: platformAppId ?? null,
|
|
378
|
+
requireDeviceAuthentication: this.options.requireDeviceAuthentication === true,
|
|
379
|
+
testing: {
|
|
380
|
+
allowSoftwareKeys: this.options.testing?.allowSoftwareKeys === true,
|
|
381
|
+
autoApprove: this.options.testing?.autoApprove === true,
|
|
382
|
+
},
|
|
383
|
+
}),
|
|
384
|
+
);
|
|
385
|
+
return native;
|
|
386
|
+
})();
|
|
387
|
+
this.configured = run;
|
|
388
|
+
run.catch(() => {
|
|
389
|
+
if (this.configured === run) this.configured = null;
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
return this.configured;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
private async token(): Promise<string> {
|
|
396
|
+
const token = await this.options.getToken();
|
|
397
|
+
if (typeof token !== 'string' || !token) throw new AuthonWalletError('NOT_SIGNED_IN', 'Sign in before using the wallet');
|
|
398
|
+
return token;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
private async call(fn: (native: AuthonWalletNativeModule, id: string, token: string) => Promise<NativeResult<unknown>>): Promise<unknown> {
|
|
402
|
+
const generation = this.generation;
|
|
403
|
+
const native = await this.native();
|
|
404
|
+
const token = await this.token();
|
|
405
|
+
return this.unwrap(await fn(native, this.id, token), generation);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
private unwrap<T>(result: NativeResult<T>, generation = this.generation): T {
|
|
409
|
+
if (generation !== this.generation) throw new AuthonWalletError('DISPOSED', 'Wallet was disposed');
|
|
410
|
+
if (!isObject(result)) throw new AuthonWalletError('UNKNOWN', 'Wallet returned no result');
|
|
411
|
+
if (result.ok === true) return result.value as T;
|
|
412
|
+
const code = typeof result.code === 'string' && result.code ? result.code : 'UNKNOWN';
|
|
413
|
+
throw new AuthonWalletError(code, typeof result.message === 'string' && result.message ? result.message : code, {
|
|
414
|
+
diagnosticId: typeof result.diagnosticId === 'string' && result.diagnosticId ? result.diagnosticId : null,
|
|
415
|
+
retryable: result.retryable === true,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/** For accounts other than 0 the native wallet needs the expected address and refuses on mismatch. */
|
|
420
|
+
private async account(index: number | undefined): Promise<[number, string | null]> {
|
|
421
|
+
if (index === undefined || index === 0) return [0, null];
|
|
422
|
+
if (!Number.isInteger(index) || index < 0 || index > MAX_ACCOUNT_INDEX) {
|
|
423
|
+
throw new AuthonWalletError('INVALID_PARAMS', 'account must be an integer 0..2147483647');
|
|
424
|
+
}
|
|
425
|
+
let address = this.accountAddresses.get(index);
|
|
426
|
+
if (!address) {
|
|
427
|
+
const [account] = await this.getAccounts({ start: index, count: 1 });
|
|
428
|
+
if (!account || account.index !== index) throw new AuthonWalletError('UNKNOWN', 'Wallet returned another account');
|
|
429
|
+
address = account.address;
|
|
430
|
+
}
|
|
431
|
+
return [index, address];
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/** A different wallet (another user) invalidates the cached account addresses. */
|
|
435
|
+
private remember(wallet: EmbeddedWallet): EmbeddedWallet {
|
|
436
|
+
const known = this.accountAddresses.get(0);
|
|
437
|
+
if (known && known.toLowerCase() !== wallet.address.toLowerCase()) this.accountAddresses.clear();
|
|
438
|
+
this.accountAddresses.set(0, wallet.address);
|
|
439
|
+
return wallet;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
private async refresh(): Promise<void> {
|
|
443
|
+
this.notify(await this.get());
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
private notify(wallet: EmbeddedWallet | null): void {
|
|
447
|
+
for (const listener of this.listeners) {
|
|
448
|
+
try {
|
|
449
|
+
listener(wallet);
|
|
450
|
+
} catch {
|
|
451
|
+
// A listener error must not break the wallet call.
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export function createAuthonWallet(options: AuthonWalletOptions): AuthonWalletClient {
|
|
458
|
+
return new AuthonWalletClient(options);
|
|
459
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error codes of the web wallet (`@authon/js` WalletErrorCode, frame `errors.ts`). The native
|
|
3
|
+
* Swift and Kotlin wallets report the same strings; they reach JS unchanged.
|
|
4
|
+
*/
|
|
5
|
+
export type WalletErrorCode =
|
|
6
|
+
| 'NOT_FOUND'
|
|
7
|
+
| 'NEEDS_RESTORE'
|
|
8
|
+
| 'NOT_PROTECTED'
|
|
9
|
+
| 'PRF_UNSUPPORTED'
|
|
10
|
+
| 'ORIGIN_NOT_ALLOWED'
|
|
11
|
+
| 'USER_CANCELLED'
|
|
12
|
+
| 'NEEDS_POPUP'
|
|
13
|
+
| 'INVALID_REQUEST'
|
|
14
|
+
| 'UNSUPPORTED_TYPE'
|
|
15
|
+
| 'UNAUTHORIZED'
|
|
16
|
+
| 'FORBIDDEN'
|
|
17
|
+
| 'ALREADY_EXISTS'
|
|
18
|
+
| 'RATE_LIMITED'
|
|
19
|
+
| 'ADDRESS_MISMATCH'
|
|
20
|
+
| 'STORAGE_UNAVAILABLE'
|
|
21
|
+
| 'API_ERROR'
|
|
22
|
+
| 'INTERNAL'
|
|
23
|
+
| 'LAST_DEVICE'
|
|
24
|
+
| 'SESSION_REQUIRED'
|
|
25
|
+
| 'WALLET_PENDING'
|
|
26
|
+
| 'CONFIRM_WINDOW_BLOCKED'
|
|
27
|
+
| 'CONFIRM_WINDOW_CLOSED'
|
|
28
|
+
| 'MESSAGE_TOO_LARGE'
|
|
29
|
+
| 'FRAME_OUTDATED'
|
|
30
|
+
| 'BACKUP_KEY_REQUIRED'
|
|
31
|
+
| 'ALREADY_UPGRADED'
|
|
32
|
+
| 'ACCOUNT_UNAVAILABLE'
|
|
33
|
+
/** Raised in JS: getToken() returned no token. */
|
|
34
|
+
| 'NOT_SIGNED_IN'
|
|
35
|
+
/** Raised in JS: bad arguments caught before the native call. */
|
|
36
|
+
| 'INVALID_PARAMS'
|
|
37
|
+
| 'UNSUPPORTED_METHOD'
|
|
38
|
+
/** Raised in JS: the client was disposed. */
|
|
39
|
+
| 'DISPOSED'
|
|
40
|
+
/** Raised in JS: the native module is missing (Expo Go, or the app was not rebuilt). */
|
|
41
|
+
| 'NOT_CONFIGURED'
|
|
42
|
+
| 'UNKNOWN'
|
|
43
|
+
| (string & {});
|
|
44
|
+
|
|
45
|
+
export class AuthonWalletError extends Error {
|
|
46
|
+
readonly code: WalletErrorCode;
|
|
47
|
+
/** The server's request ID when the failure came from an API response; log this, never secrets. */
|
|
48
|
+
readonly diagnosticId: string | null;
|
|
49
|
+
/** Whether trying the same call again can succeed (from the native wallet's code table). */
|
|
50
|
+
readonly retryable: boolean;
|
|
51
|
+
constructor(code: WalletErrorCode, message?: string, options: { diagnosticId?: string | null; retryable?: boolean } = {}) {
|
|
52
|
+
super(message ?? code);
|
|
53
|
+
this.name = 'AuthonWalletError';
|
|
54
|
+
this.code = code;
|
|
55
|
+
this.diagnosticId = options.diagnosticId ?? null;
|
|
56
|
+
this.retryable = options.retryable ?? false;
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export { AuthonWalletClient, createAuthonWallet } from './client';
|
|
2
|
+
export { AuthonWalletError } from './errors';
|
|
3
|
+
export type { WalletErrorCode } from './errors';
|
|
4
|
+
export { useAuthonWallet } from './useAuthonWallet';
|
|
5
|
+
export type { UseAuthonWalletOptions, UseAuthonWalletResult } from './useAuthonWallet';
|
|
6
|
+
export type {
|
|
7
|
+
AuthonWalletOptions,
|
|
8
|
+
CapabilityCheck,
|
|
9
|
+
CapabilityStatus,
|
|
10
|
+
WalletCapabilities,
|
|
11
|
+
WalletChain,
|
|
12
|
+
WalletChainSignRequest,
|
|
13
|
+
EIP1193Provider,
|
|
14
|
+
EIP1193RequestArguments,
|
|
15
|
+
EmbeddedWallet,
|
|
16
|
+
EmbeddedWalletDevice,
|
|
17
|
+
Hex,
|
|
18
|
+
TokenGetter,
|
|
19
|
+
TypedDataPayload,
|
|
20
|
+
WalletAccount,
|
|
21
|
+
WalletAppConfig,
|
|
22
|
+
WalletChangeListener,
|
|
23
|
+
WalletMessage,
|
|
24
|
+
WalletSignOptions,
|
|
25
|
+
WalletTransactionRequest,
|
|
26
|
+
} from './types';
|
package/src/native.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { requireOptionalNativeModule } from 'expo-modules-core';
|
|
2
|
+
|
|
3
|
+
/** Every native function resolves an envelope instead of rejecting. */
|
|
4
|
+
export type NativeResult<T> =
|
|
5
|
+
| { ok: true; value: T }
|
|
6
|
+
| { ok: false; code: string; message?: string; diagnosticId?: string | null; retryable?: boolean };
|
|
7
|
+
|
|
8
|
+
export interface NativeConfig {
|
|
9
|
+
publishableKey: string;
|
|
10
|
+
apiUrl: string | null;
|
|
11
|
+
appId: string | null;
|
|
12
|
+
requireDeviceAuthentication: boolean;
|
|
13
|
+
testing: { allowSoftwareKeys: boolean; autoApprove: boolean };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** The Swift / Kotlin module `AuthonWallet`. Tokens go in per call; nothing secret comes out. */
|
|
17
|
+
export interface AuthonWalletNativeModule {
|
|
18
|
+
configure(id: string, config: NativeConfig): Promise<NativeResult<null>>;
|
|
19
|
+
release(id: string): Promise<NativeResult<null>>;
|
|
20
|
+
appConfig(id: string): Promise<NativeResult<unknown>>;
|
|
21
|
+
capabilities(id: string): Promise<NativeResult<unknown>>;
|
|
22
|
+
status(id: string, token: string): Promise<NativeResult<unknown>>;
|
|
23
|
+
create(id: string, token: string): Promise<NativeResult<unknown>>;
|
|
24
|
+
accounts(id: string, token: string, start: number, count: number, chain: string): Promise<NativeResult<unknown>>;
|
|
25
|
+
signMessage(
|
|
26
|
+
id: string,
|
|
27
|
+
token: string,
|
|
28
|
+
kind: 'text' | 'raw',
|
|
29
|
+
message: string,
|
|
30
|
+
account: number,
|
|
31
|
+
address: string | null,
|
|
32
|
+
): Promise<NativeResult<unknown>>;
|
|
33
|
+
signTypedData(id: string, token: string, typedDataJson: string, account: number, address: string | null): Promise<NativeResult<unknown>>;
|
|
34
|
+
signTransaction(id: string, token: string, transactionJson: string, account: number, address: string | null): Promise<NativeResult<unknown>>;
|
|
35
|
+
signChainTransaction(id: string, token: string, chain: string, account: number, address: string, payloadJson: string): Promise<NativeResult<unknown>>;
|
|
36
|
+
protect(id: string, token: string): Promise<NativeResult<unknown>>;
|
|
37
|
+
upgradeBackup(id: string, token: string): Promise<NativeResult<unknown>>;
|
|
38
|
+
restore(id: string, token: string, reset: boolean): Promise<NativeResult<unknown>>;
|
|
39
|
+
revokeDevice(id: string, token: string, deviceId: string): Promise<NativeResult<unknown>>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let override: AuthonWalletNativeModule | null | undefined;
|
|
43
|
+
|
|
44
|
+
export function getNativeModule(): AuthonWalletNativeModule | null {
|
|
45
|
+
if (override !== undefined) return override;
|
|
46
|
+
return requireOptionalNativeModule<AuthonWalletNativeModule>('AuthonWallet');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Tests only: replaces the native module (pass undefined to restore). */
|
|
50
|
+
export function setNativeModuleForTesting(module: AuthonWalletNativeModule | null | undefined): void {
|
|
51
|
+
override = module;
|
|
52
|
+
}
|