@authon/react-native-wallet 0.1.0 → 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 +34 -4
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/dev/authon/reactnative/wallet/AuthonWalletModule.kt +57 -3
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/AndroidCapabilityProbe.kt +86 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/AuthonWallet.kt +29 -2
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Bitcoin.kt +296 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Chains.kt +157 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Der.kt +17 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Encodings.kt +133 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Http.kt +6 -2
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/KeystoreDeviceStore.kt +27 -23
- 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 +16 -8
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletCapabilities.kt +91 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletError.kt +18 -2
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletOps.kt +103 -1
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletTypes.kt +8 -1
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Xrp.kt +193 -0
- package/build/client.d.ts +18 -2
- package/build/client.d.ts.map +1 -1
- package/build/client.js +88 -6
- package/build/client.js.map +1 -1
- package/build/errors.d.ts +8 -1
- package/build/errors.d.ts.map +1 -1
- package/build/errors.js +7 -1
- package/build/errors.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/native.d.ts +5 -1
- package/build/native.d.ts.map +1 -1
- package/build/native.js.map +1 -1
- package/build/types.d.ts +47 -1
- package/build/types.d.ts.map +1 -1
- package/ios/AuthonReactNativeWallet.podspec +6 -2
- package/ios/AuthonWalletModule.swift +46 -3
- package/ios/vendor/AuthonWallet/AuthonWallet.swift +24 -4
- 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/Chains.swift +278 -0
- package/ios/vendor/AuthonWallet/Crypto/Secp256k1.swift +24 -0
- package/ios/vendor/AuthonWallet/Wallet/WalletAPI.swift +19 -8
- package/ios/vendor/AuthonWallet/Wallet/WalletOps.swift +101 -2
- package/ios/vendor/AuthonWallet/WalletError.swift +32 -2
- package/ios/vendor/CKeccak/README.md +6 -3
- package/ios/vendor/CKeccak/include/ckeccak.h +4 -0
- package/ios/vendor/CKeccak/ripemd160.c +343 -0
- package/ios/vendor/CKeccak/ripemd160.h +22 -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/GENERATED.md +1 -1
- package/package.json +3 -2
- package/src/client.ts +90 -6
- package/src/errors.ts +7 -1
- package/src/index.ts +5 -0
- package/src/native.ts +4 -2
- package/src/types.ts +52 -1
package/build/errors.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
export class AuthonWalletError extends Error {
|
|
2
2
|
code;
|
|
3
|
-
|
|
3
|
+
/** The server's request ID when the failure came from an API response; log this, never secrets. */
|
|
4
|
+
diagnosticId;
|
|
5
|
+
/** Whether trying the same call again can succeed (from the native wallet's code table). */
|
|
6
|
+
retryable;
|
|
7
|
+
constructor(code, message, options = {}) {
|
|
4
8
|
super(message ?? code);
|
|
5
9
|
this.name = 'AuthonWalletError';
|
|
6
10
|
this.code = code;
|
|
11
|
+
this.diagnosticId = options.diagnosticId ?? null;
|
|
12
|
+
this.retryable = options.retryable ?? false;
|
|
7
13
|
}
|
|
8
14
|
}
|
|
9
15
|
//# sourceMappingURL=errors.js.map
|
package/build/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AA4CA,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACjC,IAAI,CAAkB;IAC/B,YAAY,IAAqB,EAAE,OAAgB;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AA4CA,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACjC,IAAI,CAAkB;IAC/B,mGAAmG;IAC1F,YAAY,CAAgB;IACrC,4FAA4F;IACnF,SAAS,CAAU;IAC5B,YAAY,IAAqB,EAAE,OAAgB,EAAE,UAAiE,EAAE;QACtH,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,IAAI,CAAC;QACjD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC9C,CAAC;CACF"}
|
package/build/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export { AuthonWalletError } from './errors';
|
|
|
3
3
|
export type { WalletErrorCode } from './errors';
|
|
4
4
|
export { useAuthonWallet } from './useAuthonWallet';
|
|
5
5
|
export type { UseAuthonWalletOptions, UseAuthonWalletResult } from './useAuthonWallet';
|
|
6
|
-
export type { AuthonWalletOptions, EIP1193Provider, EIP1193RequestArguments, EmbeddedWallet, EmbeddedWalletDevice, Hex, TokenGetter, TypedDataPayload, WalletAccount, WalletAppConfig, WalletChangeListener, WalletMessage, WalletSignOptions, WalletTransactionRequest, } from './types';
|
|
6
|
+
export type { AuthonWalletOptions, CapabilityCheck, CapabilityStatus, WalletCapabilities, WalletChain, WalletChainSignRequest, EIP1193Provider, EIP1193RequestArguments, EmbeddedWallet, EmbeddedWalletDevice, Hex, TokenGetter, TypedDataPayload, WalletAccount, WalletAppConfig, WalletChangeListener, WalletMessage, WalletSignOptions, WalletTransactionRequest, } from './types';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AACvF,YAAY,EACV,mBAAmB,EACnB,eAAe,EACf,uBAAuB,EACvB,cAAc,EACd,oBAAoB,EACpB,GAAG,EACH,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AACvF,YAAY,EACV,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,uBAAuB,EACvB,cAAc,EACd,oBAAoB,EACpB,GAAG,EACH,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,SAAS,CAAC"}
|
package/build/native.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export type NativeResult<T> = {
|
|
|
6
6
|
ok: false;
|
|
7
7
|
code: string;
|
|
8
8
|
message?: string;
|
|
9
|
+
diagnosticId?: string | null;
|
|
10
|
+
retryable?: boolean;
|
|
9
11
|
};
|
|
10
12
|
export interface NativeConfig {
|
|
11
13
|
publishableKey: string;
|
|
@@ -22,12 +24,14 @@ export interface AuthonWalletNativeModule {
|
|
|
22
24
|
configure(id: string, config: NativeConfig): Promise<NativeResult<null>>;
|
|
23
25
|
release(id: string): Promise<NativeResult<null>>;
|
|
24
26
|
appConfig(id: string): Promise<NativeResult<unknown>>;
|
|
27
|
+
capabilities(id: string): Promise<NativeResult<unknown>>;
|
|
25
28
|
status(id: string, token: string): Promise<NativeResult<unknown>>;
|
|
26
29
|
create(id: string, token: string): Promise<NativeResult<unknown>>;
|
|
27
|
-
accounts(id: string, token: string, start: number, count: number): Promise<NativeResult<unknown>>;
|
|
30
|
+
accounts(id: string, token: string, start: number, count: number, chain: string): Promise<NativeResult<unknown>>;
|
|
28
31
|
signMessage(id: string, token: string, kind: 'text' | 'raw', message: string, account: number, address: string | null): Promise<NativeResult<unknown>>;
|
|
29
32
|
signTypedData(id: string, token: string, typedDataJson: string, account: number, address: string | null): Promise<NativeResult<unknown>>;
|
|
30
33
|
signTransaction(id: string, token: string, transactionJson: string, account: number, address: string | null): Promise<NativeResult<unknown>>;
|
|
34
|
+
signChainTransaction(id: string, token: string, chain: string, account: number, address: string, payloadJson: string): Promise<NativeResult<unknown>>;
|
|
31
35
|
protect(id: string, token: string): Promise<NativeResult<unknown>>;
|
|
32
36
|
upgradeBackup(id: string, token: string): Promise<NativeResult<unknown>>;
|
|
33
37
|
restore(id: string, token: string, reset: boolean): Promise<NativeResult<unknown>>;
|
package/build/native.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAEA,uEAAuE;AACvE,MAAM,MAAM,YAAY,CAAC,CAAC,IACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAEA,uEAAuE;AACvE,MAAM,MAAM,YAAY,CAAC,CAAC,IACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAErG,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2BAA2B,EAAE,OAAO,CAAC;IACrC,OAAO,EAAE;QAAE,iBAAiB,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC;CAC/D;AAED,iGAAiG;AACjG,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IACzE,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACzD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IAClE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IAClE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACjH,WAAW,CACT,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GAAG,KAAK,EACpB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IAClC,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACzI,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7I,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACtJ,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACnE,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACzE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IACnF,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;CAC3F;AAID,wBAAgB,eAAe,IAAI,wBAAwB,GAAG,IAAI,CAGjE;AAED,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAEnG"}
|
package/build/native.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAyChE,IAAI,QAAqD,CAAC;AAE1D,MAAM,UAAU,eAAe;IAC7B,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC5C,OAAO,2BAA2B,CAA2B,cAAc,CAAC,CAAC;AAC/E,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,yBAAyB,CAAC,MAAmD;IAC3F,QAAQ,GAAG,MAAM,CAAC;AACpB,CAAC"}
|
package/build/types.d.ts
CHANGED
|
@@ -23,11 +23,34 @@ export interface EmbeddedWallet {
|
|
|
23
23
|
backupUpgradeRecommended: boolean;
|
|
24
24
|
devices: EmbeddedWalletDevice[];
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
26
|
+
/** Chains whose account addresses the wallet derives from the same seed (NATIVE.md section 11). */
|
|
27
|
+
export type WalletChain = 'evm' | 'tron' | 'xrp' | 'bitcoin' | 'bitcoin-testnet' | 'solana';
|
|
28
|
+
/**
|
|
29
|
+
* One HD account. EVM: `m/44'/60'/0'/0/{index}` (account 0 is `EmbeddedWallet.address`); other chains
|
|
30
|
+
* use their standard paths (TRON `m/44'/195'/n'/0/0`, XRP `m/44'/144'/n'/0/0`, Bitcoin
|
|
31
|
+
* `m/84'/0'/n'/0/0`, Bitcoin testnet `m/84'/1'/n'/0/0`, Solana `m/44'/501'/n'/0'`).
|
|
32
|
+
*/
|
|
27
33
|
export interface WalletAccount {
|
|
28
34
|
index: number;
|
|
29
35
|
path: string;
|
|
30
36
|
address: string;
|
|
37
|
+
/** Set when `getAccounts` was asked for a chain. */
|
|
38
|
+
chain?: WalletChain;
|
|
39
|
+
/** Public key (hex) when the chain needs it to build a transaction (XRP SigningPubKey). */
|
|
40
|
+
publicKey?: string;
|
|
41
|
+
}
|
|
42
|
+
/** `signChainTransaction` input (NATIVE.md section 12). The app builds the unsigned transaction. */
|
|
43
|
+
export interface WalletChainSignRequest {
|
|
44
|
+
chain: Exclude<WalletChain, 'evm'>;
|
|
45
|
+
/** Account index (default 0). */
|
|
46
|
+
account?: number;
|
|
47
|
+
/** The account's address on `chain`, from getAccounts. Signing fails (ADDRESS_MISMATCH) if it differs. */
|
|
48
|
+
address: string;
|
|
49
|
+
/**
|
|
50
|
+
* Chain-specific unsigned transaction: Solana `{ message }` (base64 Message, legacy or v0),
|
|
51
|
+
* TRON `{ rawDataHex }`, Bitcoin `{ psbt }` (base64), XRP `{ txBlobHex }` (encodeForSigning output).
|
|
52
|
+
*/
|
|
53
|
+
payload: Record<string, unknown>;
|
|
31
54
|
}
|
|
32
55
|
/** Signs with another account of the same wallet. Omitted or 0 = the wallet address. */
|
|
33
56
|
export interface WalletSignOptions {
|
|
@@ -80,6 +103,29 @@ export interface WalletAppConfig {
|
|
|
80
103
|
rpId: string;
|
|
81
104
|
minFrameProtocol: number | null;
|
|
82
105
|
}
|
|
106
|
+
export type CapabilityStatus = 'available' | 'unavailable' | 'unknown';
|
|
107
|
+
export interface CapabilityCheck {
|
|
108
|
+
status: CapabilityStatus;
|
|
109
|
+
/** Reason code (see the README table); null when available. */
|
|
110
|
+
reason: string | null;
|
|
111
|
+
}
|
|
112
|
+
/** `capabilities()`: can this device and app use the wallet? Asked before sign-in, no session. */
|
|
113
|
+
export interface WalletCapabilities {
|
|
114
|
+
/** unavailable if any check is unavailable, else unknown if any is unknown, else available. */
|
|
115
|
+
ready: CapabilityStatus;
|
|
116
|
+
/** App registration and approval (APP_NOT_REGISTERED, APP_SIGNATURE_MISMATCH, APP_NOT_APPROVED, NETWORK_ERROR, API_ERROR). */
|
|
117
|
+
app: CapabilityCheck;
|
|
118
|
+
/** Embedded wallet enabled for the project (WALLET_DISABLED, NETWORK_ERROR). */
|
|
119
|
+
wallet: CapabilityCheck;
|
|
120
|
+
/** Hardware-protected key storage (HARDWARE_KEYSTORE_UNAVAILABLE, SIMULATOR, CHECK_FAILED). */
|
|
121
|
+
secureStorage: CapabilityCheck;
|
|
122
|
+
/** A device passcode / biometrics is set (DEVICE_LOCK_NOT_SET, CHECK_FAILED). */
|
|
123
|
+
deviceLock: CapabilityCheck;
|
|
124
|
+
/** Passkey PRF backup, static OS/API support only (OS_UNSUPPORTED, PASSKEY_API_UNAVAILABLE, PRF_PROVIDER_UNVERIFIED). */
|
|
125
|
+
passkeyBackup: CapabilityCheck;
|
|
126
|
+
/** X-Request-Id of the app-config response. */
|
|
127
|
+
diagnosticId: string | null;
|
|
128
|
+
}
|
|
83
129
|
export interface EIP1193RequestArguments {
|
|
84
130
|
method: string;
|
|
85
131
|
params?: unknown[] | Record<string, unknown>;
|
package/build/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAE7F,MAAM,MAAM,GAAG,GAAG,KAAK,MAAM,EAAE,CAAC;AAEhC,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wBAAwB;IACxB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB,mGAAmG;IACnG,YAAY,EAAE,OAAO,CAAC;IACtB,8GAA8G;IAC9G,wBAAwB,EAAE,OAAO,CAAC;IAClC,OAAO,EAAE,oBAAoB,EAAE,CAAC;CACjC;AAED,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,17 +18,21 @@ Pod::Spec.new do |s|
|
|
|
18
18
|
s.frameworks = 'Security', 'CryptoKit', 'LocalAuthentication', 'AuthenticationServices', 'SwiftUI'
|
|
19
19
|
|
|
20
20
|
# vendor/ is generated from swift/Sources by scripts/sync-native-wallet.mjs.
|
|
21
|
-
# CSecp256k1: compile only build/*.c (they include src/*.c); CKeccak: sha3.c and memzero.c
|
|
21
|
+
# CSecp256k1: compile only build/*.c (they include src/*.c); CKeccak: sha3.c, ripemd160.c and memzero.c;
|
|
22
|
+
# CMonocypher (Ed25519, deterministic like the web wallet): both .c files, no flags.
|
|
22
23
|
s.source_files = [
|
|
23
24
|
'*.swift',
|
|
24
25
|
'vendor/AuthonWallet/**/*.swift',
|
|
25
26
|
'vendor/CSecp256k1/build/*.c',
|
|
26
27
|
'vendor/CSecp256k1/include/*.h',
|
|
27
28
|
'vendor/CKeccak/sha3.c',
|
|
29
|
+
'vendor/CKeccak/ripemd160.c',
|
|
28
30
|
'vendor/CKeccak/memzero.c',
|
|
29
31
|
'vendor/CKeccak/include/ckeccak.h',
|
|
32
|
+
'vendor/CMonocypher/*.c',
|
|
33
|
+
'vendor/CMonocypher/include/*.h',
|
|
30
34
|
]
|
|
31
|
-
s.public_header_files = ['vendor/CSecp256k1/include/*.h', 'vendor/CKeccak/include/ckeccak.h']
|
|
35
|
+
s.public_header_files = ['vendor/CSecp256k1/include/*.h', 'vendor/CKeccak/include/ckeccak.h', 'vendor/CMonocypher/include/*.h']
|
|
32
36
|
s.preserve_paths = ['vendor/CSecp256k1/src/**/*', 'vendor/CKeccak/*.h']
|
|
33
37
|
s.pod_target_xcconfig = {
|
|
34
38
|
'DEFINES_MODULE' => 'YES',
|
|
@@ -27,6 +27,10 @@ public final class AuthonWalletModule: Module {
|
|
|
27
27
|
await self.call(id, token: nil) { try Self.appConfig(await $0.appConfig()) }
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
AsyncFunction("capabilities") { (id: String) async -> [String: Any] in
|
|
31
|
+
await self.call(id, token: nil) { Self.capabilities(await $0.capabilities()) }
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
AsyncFunction("status") { (id: String, token: String) async -> [String: Any] in
|
|
31
35
|
await self.call(id, token: token) { try Self.wallet(await $0.status()) }
|
|
32
36
|
}
|
|
@@ -35,9 +39,14 @@ public final class AuthonWalletModule: Module {
|
|
|
35
39
|
await self.call(id, token: token) { try Self.wallet(await $0.create()) }
|
|
36
40
|
}
|
|
37
41
|
|
|
38
|
-
AsyncFunction("accounts") { (id: String, token: String, start: Int, count: Int) async -> [String: Any] in
|
|
42
|
+
AsyncFunction("accounts") { (id: String, token: String, start: Int, count: Int, chain: String) async -> [String: Any] in
|
|
39
43
|
await self.call(id, token: token) { w in
|
|
40
|
-
|
|
44
|
+
guard let walletChain = WalletChain(rawValue: chain) else {
|
|
45
|
+
throw WalletError(.invalidRequest, "chain must be one of \(WalletChain.allCases.map(\.rawValue).joined(separator: ", "))")
|
|
46
|
+
}
|
|
47
|
+
return try await w.accounts(start: start, count: count, chain: walletChain).map {
|
|
48
|
+
["index": $0.index, "path": $0.path, "address": $0.address, "chain": $0.chain.rawValue, "publicKey": Self.orNull($0.publicKey)] as [String: Any]
|
|
49
|
+
}
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
52
|
|
|
@@ -56,6 +65,19 @@ public final class AuthonWalletModule: Module {
|
|
|
56
65
|
await self.call(id, token: token) { try await $0.signTransaction(transactionJson, account: account, address: address) }
|
|
57
66
|
}
|
|
58
67
|
|
|
68
|
+
AsyncFunction("signChainTransaction") { (id: String, token: String, chain: String, account: Int, address: String, payloadJson: String) async -> [String: Any] in
|
|
69
|
+
await self.call(id, token: token) { w in
|
|
70
|
+
guard let walletChain = WalletChain(rawValue: chain) else {
|
|
71
|
+
throw WalletError(.invalidRequest, "unknown chain")
|
|
72
|
+
}
|
|
73
|
+
guard let data = payloadJson.data(using: .utf8),
|
|
74
|
+
let payload = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
75
|
+
throw WalletError(.invalidRequest, "payload must be a JSON object")
|
|
76
|
+
}
|
|
77
|
+
return try await w.signChainTransaction(chain: walletChain, account: account, address: address, payload: payload)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
59
81
|
AsyncFunction("protect") { (id: String, token: String) async -> [String: Any] in
|
|
60
82
|
await self.call(id, token: token) { ["shareVersion": try await $0.protect().shareVersion] }
|
|
61
83
|
}
|
|
@@ -140,7 +162,13 @@ public final class AuthonWalletModule: Module {
|
|
|
140
162
|
} else {
|
|
141
163
|
e = WalletError(.internal, "unexpected error")
|
|
142
164
|
}
|
|
143
|
-
return [
|
|
165
|
+
return [
|
|
166
|
+
"ok": false,
|
|
167
|
+
"code": e.code.rawValue,
|
|
168
|
+
"message": e.message,
|
|
169
|
+
"diagnosticId": orNull(e.diagnosticId),
|
|
170
|
+
"retryable": e.retryable,
|
|
171
|
+
]
|
|
144
172
|
}
|
|
145
173
|
|
|
146
174
|
// MARK: - Public data only
|
|
@@ -165,6 +193,21 @@ public final class AuthonWalletModule: Module {
|
|
|
165
193
|
]
|
|
166
194
|
}
|
|
167
195
|
|
|
196
|
+
private static func capabilities(_ c: WalletCapabilities) -> [String: Any] {
|
|
197
|
+
func check(_ k: WalletCapabilities.Check) -> [String: Any] {
|
|
198
|
+
["status": k.status.rawValue, "reason": orNull(k.reason)]
|
|
199
|
+
}
|
|
200
|
+
return [
|
|
201
|
+
"ready": c.ready.rawValue,
|
|
202
|
+
"app": check(c.app),
|
|
203
|
+
"wallet": check(c.wallet),
|
|
204
|
+
"secureStorage": check(c.secureStorage),
|
|
205
|
+
"deviceLock": check(c.deviceLock),
|
|
206
|
+
"passkeyBackup": check(c.passkeyBackup),
|
|
207
|
+
"diagnosticId": orNull(c.diagnosticId),
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
|
|
168
211
|
private static func appConfig(_ c: WalletAppConfig) -> [String: Any] {
|
|
169
212
|
[
|
|
170
213
|
"projectId": orNull(c.projectId),
|
|
@@ -61,11 +61,16 @@ public final class AuthonWallet: @unchecked Sendable {
|
|
|
61
61
|
/// App registration and wallet settings from the API (cached).
|
|
62
62
|
public func appConfig() async throws -> WalletAppConfig {
|
|
63
63
|
if let c = lock.withLock({ cachedConfig }) { return c }
|
|
64
|
+
return try await refreshAppConfig()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Fetches app-config again (used by capabilities()); a successful answer replaces the cache.
|
|
68
|
+
func refreshAppConfig() async throws -> WalletAppConfig {
|
|
64
69
|
guard let appId = appId ?? Self.detectAppIdentifier() else {
|
|
65
70
|
throw WalletError(.originNotAllowed, "appId (TEAMID.bundleId) could not be determined")
|
|
66
71
|
}
|
|
67
72
|
let cfg = try await api.appConfig(platform: "ios", appId: appId)
|
|
68
|
-
lock.withLock { cachedConfig = cfg }
|
|
73
|
+
if cfg.projectFound { lock.withLock { cachedConfig = cfg } }
|
|
69
74
|
return cfg
|
|
70
75
|
}
|
|
71
76
|
|
|
@@ -135,9 +140,10 @@ public final class AuthonWallet: @unchecked Sendable {
|
|
|
135
140
|
try await run { ops, s in try PublicWallet(try await ops.create(s)) }
|
|
136
141
|
}
|
|
137
142
|
|
|
138
|
-
/// Addresses of accounts start..<start+count (m/44'/60'/0'/0/n
|
|
139
|
-
|
|
140
|
-
|
|
143
|
+
/// Addresses of accounts start..<start+count on `chain` (EVM: m/44'/60'/0'/0/n; other paths in
|
|
144
|
+
/// `WalletChain.path`). A wallet from before HD wallets has only EVM account 0 (ACCOUNT_UNAVAILABLE).
|
|
145
|
+
public func accounts(start: Int = 0, count: Int = 1, chain: WalletChain = .evm) async throws -> [WalletAccount] {
|
|
146
|
+
try await run { ops, s in try await ops.accounts(s, start: start, count: count, chain: chain) }
|
|
141
147
|
}
|
|
142
148
|
|
|
143
149
|
/// EIP-191 personal_sign. `account` > 0 must come with the address from `accounts()`.
|
|
@@ -166,6 +172,20 @@ public final class AuthonWallet: @unchecked Sendable {
|
|
|
166
172
|
}
|
|
167
173
|
}
|
|
168
174
|
|
|
175
|
+
/// Signs a transaction the app built for another chain (EVM uses `signTransaction`: UNSUPPORTED_TYPE).
|
|
176
|
+
/// - Solana: `["message": <serialized Message, base64>, "cluster": "devnet"]` (cluster optional,
|
|
177
|
+
/// only labels the approval) → `["signature": <base58>]`.
|
|
178
|
+
/// - TRON: `["rawDataHex": <raw_data protobuf, hex>]` → `["txID": <hex>, "signature": <r||s||v hex>]`.
|
|
179
|
+
/// - Bitcoin / testnet: `["psbt": <base64>]` → `["psbt": <signed PSBT base64>, "txHex": <raw tx>]`
|
|
180
|
+
/// (`txHex` only when every input was the account's).
|
|
181
|
+
/// - XRP: `["txBlobHex": <encodeForSigning output>]` → `["signature": <DER, uppercase hex>]`.
|
|
182
|
+
/// `address` is the expected address of `account` on that chain (from `accounts(chain:)`).
|
|
183
|
+
public func signChainTransaction(chain: WalletChain, account: Int = 0, address: String, payload: [String: Any]) async throws -> [String: String] {
|
|
184
|
+
try await run { ops, s in
|
|
185
|
+
try await ops.signChainTransaction(s, chain: chain, account: account, address: address, payload: try JSONValue.from(payload))
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
169
189
|
/// Creates the passkey backup (iOS 18 / macOS 15 for PRF).
|
|
170
190
|
public func protect() async throws -> ProtectResult {
|
|
171
191
|
try await run(passkeys: true) { ops, s in try await ops.protect(s) }
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// GENERATED by scripts/sync-native-wallet.mjs from swift/Sources/AuthonWallet/Capabilities.swift. Do not edit: change the source and run the script.
|
|
2
|
+
import CryptoKit
|
|
3
|
+
import Foundation
|
|
4
|
+
import LocalAuthentication
|
|
5
|
+
import Security
|
|
6
|
+
|
|
7
|
+
/// Pre-sign-in check (docs/wallet/NATIVE.md section 9): whether this app and device can use the
|
|
8
|
+
/// wallet. Needs no session, opens no login or passkey UI and never creates a wallet.
|
|
9
|
+
public struct WalletCapabilities: Equatable, Sendable {
|
|
10
|
+
public enum Status: String, Sendable {
|
|
11
|
+
case available, unavailable, unknown
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public struct Check: Equatable, Sendable {
|
|
15
|
+
public var status: Status
|
|
16
|
+
/// Section 9 reason code; nil when available.
|
|
17
|
+
public var reason: String?
|
|
18
|
+
|
|
19
|
+
public static let available = Check(status: .available, reason: nil)
|
|
20
|
+
static func unavailable(_ reason: String) -> Check { Check(status: .unavailable, reason: reason) }
|
|
21
|
+
static func unknown(_ reason: String) -> Check { Check(status: .unknown, reason: reason) }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// unavailable if any check is unavailable, else unknown if any is unknown, else available.
|
|
25
|
+
public var ready: Status
|
|
26
|
+
/// App registration and approval (GET /v1/wallet/app-config).
|
|
27
|
+
public var app: Check
|
|
28
|
+
/// The embedded wallet is enabled for the project.
|
|
29
|
+
public var wallet: Check
|
|
30
|
+
/// Hardware-protected key storage (Secure Enclave on a real device).
|
|
31
|
+
public var secureStorage: Check
|
|
32
|
+
/// A device passcode or biometrics is set.
|
|
33
|
+
public var deviceLock: Check
|
|
34
|
+
/// Passkey PRF backup and restore (static OS support only).
|
|
35
|
+
public var passkeyBackup: Check
|
|
36
|
+
/// `X-Request-Id` of the app-config response.
|
|
37
|
+
public var diagnosticId: String?
|
|
38
|
+
|
|
39
|
+
init(app: Check, wallet: Check, secureStorage: Check, deviceLock: Check, passkeyBackup: Check, diagnosticId: String?) {
|
|
40
|
+
self.app = app
|
|
41
|
+
self.wallet = wallet
|
|
42
|
+
self.secureStorage = secureStorage
|
|
43
|
+
self.deviceLock = deviceLock
|
|
44
|
+
self.passkeyBackup = passkeyBackup
|
|
45
|
+
self.diagnosticId = diagnosticId
|
|
46
|
+
let all = [app, wallet, secureStorage, deviceLock, passkeyBackup].map(\.status)
|
|
47
|
+
ready = all.contains(.unavailable) ? .unavailable : all.contains(.unknown) ? .unknown : .available
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Device checks behind capabilities(); replaceable in tests.
|
|
52
|
+
struct CapabilityProbes: Sendable {
|
|
53
|
+
var secureStorage: @Sendable () -> WalletCapabilities.Check
|
|
54
|
+
var deviceLock: @Sendable () -> WalletCapabilities.Check
|
|
55
|
+
var passkeyBackup: @Sendable () -> WalletCapabilities.Check
|
|
56
|
+
|
|
57
|
+
static let device = CapabilityProbes(
|
|
58
|
+
secureStorage: { probeSecureEnclave() },
|
|
59
|
+
deviceLock: { probeDeviceLock() },
|
|
60
|
+
passkeyBackup: { probePasskeyBackup() }
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
/// The simulator never counts as hardware protection, even where it emulates an enclave.
|
|
64
|
+
/// On a device a throwaway enclave key is created and released (never stored).
|
|
65
|
+
static func probeSecureEnclave() -> WalletCapabilities.Check {
|
|
66
|
+
#if targetEnvironment(simulator)
|
|
67
|
+
return .unknown("SIMULATOR")
|
|
68
|
+
#else
|
|
69
|
+
guard SecureEnclave.isAvailable else { return .unavailable("HARDWARE_KEYSTORE_UNAVAILABLE") }
|
|
70
|
+
var error: Unmanaged<CFError>?
|
|
71
|
+
guard let access = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, .privateKeyUsage, &error) else {
|
|
72
|
+
return .unknown("CHECK_FAILED")
|
|
73
|
+
}
|
|
74
|
+
let attrs: [String: Any] = [
|
|
75
|
+
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
|
|
76
|
+
kSecAttrKeySizeInBits as String: 256,
|
|
77
|
+
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
|
|
78
|
+
kSecPrivateKeyAttrs as String: [
|
|
79
|
+
kSecAttrIsPermanent as String: false,
|
|
80
|
+
kSecAttrAccessControl as String: access,
|
|
81
|
+
] as [String: Any],
|
|
82
|
+
]
|
|
83
|
+
guard SecKeyCreateRandomKey(attrs as CFDictionary, &error) != nil else { return .unknown("CHECK_FAILED") }
|
|
84
|
+
return .available
|
|
85
|
+
#endif
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static func probeDeviceLock() -> WalletCapabilities.Check {
|
|
89
|
+
let context = LAContext()
|
|
90
|
+
var error: NSError?
|
|
91
|
+
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) { return .available }
|
|
92
|
+
if let code = error.map({ LAError.Code(rawValue: $0.code) }), code == .passcodeNotSet {
|
|
93
|
+
return .unavailable("DEVICE_LOCK_NOT_SET")
|
|
94
|
+
}
|
|
95
|
+
return .unknown("CHECK_FAILED")
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static func probePasskeyBackup() -> WalletCapabilities.Check {
|
|
99
|
+
if #available(iOS 18.0, macOS 15.0, *) {
|
|
100
|
+
// The API exists; whether the user's passkey provider evaluates PRF shows on the first protect().
|
|
101
|
+
return .unknown("PRF_PROVIDER_UNVERIFIED")
|
|
102
|
+
}
|
|
103
|
+
return .unavailable("OS_UNSUPPORTED")
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
extension AuthonWallet {
|
|
108
|
+
/// See `WalletCapabilities`. Never throws: failures become `unknown` checks.
|
|
109
|
+
/// `allowSoftwareKeys` does not affect the answer.
|
|
110
|
+
public func capabilities() async -> WalletCapabilities {
|
|
111
|
+
await capabilities(probes: .device)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
func capabilities(probes: CapabilityProbes) async -> WalletCapabilities {
|
|
115
|
+
let app: WalletCapabilities.Check
|
|
116
|
+
let wallet: WalletCapabilities.Check
|
|
117
|
+
var diagnosticId: String?
|
|
118
|
+
do {
|
|
119
|
+
let cfg = try await refreshAppConfig()
|
|
120
|
+
diagnosticId = cfg.diagnosticId
|
|
121
|
+
if !cfg.projectFound {
|
|
122
|
+
app = .unavailable("APP_NOT_REGISTERED")
|
|
123
|
+
wallet = .unknown("API_ERROR")
|
|
124
|
+
} else {
|
|
125
|
+
app = !cfg.allowed ? .unavailable("APP_NOT_REGISTERED") : cfg.approved ? .available : .unknown("APP_NOT_APPROVED")
|
|
126
|
+
wallet = cfg.enabled ? .available : .unavailable("WALLET_DISABLED")
|
|
127
|
+
}
|
|
128
|
+
} catch let e as WalletError where e.isNetworkFailure {
|
|
129
|
+
app = .unknown("NETWORK_ERROR")
|
|
130
|
+
wallet = .unknown("NETWORK_ERROR")
|
|
131
|
+
} catch let e as WalletError where e.code == .originNotAllowed {
|
|
132
|
+
// The app ID could not be determined, so no registration can match it.
|
|
133
|
+
app = .unavailable("APP_NOT_REGISTERED")
|
|
134
|
+
wallet = .unknown("API_ERROR")
|
|
135
|
+
} catch {
|
|
136
|
+
diagnosticId = (error as? WalletError)?.diagnosticId
|
|
137
|
+
app = .unknown("API_ERROR")
|
|
138
|
+
wallet = .unknown("API_ERROR")
|
|
139
|
+
}
|
|
140
|
+
return WalletCapabilities(
|
|
141
|
+
app: app,
|
|
142
|
+
wallet: wallet,
|
|
143
|
+
secureStorage: probes.secureStorage(),
|
|
144
|
+
deviceLock: probes.deviceLock(),
|
|
145
|
+
passkeyBackup: probes.passkeyBackup(),
|
|
146
|
+
diagnosticId: diagnosticId
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
}
|