@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
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// GENERATED by scripts/sync-native-wallet.mjs from kotlin/authon/src/main/kotlin/dev/authon/sdk/wallet/WalletApi.kt. Do not edit: change the source and run the script.
|
|
2
|
+
package dev.authon.sdk.wallet
|
|
3
|
+
|
|
4
|
+
import kotlinx.serialization.json.JsonArray
|
|
5
|
+
import kotlinx.serialization.json.JsonElement
|
|
6
|
+
import kotlinx.serialization.json.JsonNull
|
|
7
|
+
import kotlinx.serialization.json.JsonObject
|
|
8
|
+
import kotlinx.serialization.json.JsonObjectBuilder
|
|
9
|
+
import kotlinx.serialization.json.JsonPrimitive
|
|
10
|
+
import kotlinx.serialization.json.booleanOrNull
|
|
11
|
+
import kotlinx.serialization.json.buildJsonObject
|
|
12
|
+
import kotlinx.serialization.json.contentOrNull
|
|
13
|
+
import kotlinx.serialization.json.intOrNull
|
|
14
|
+
import kotlinx.serialization.json.longOrNull
|
|
15
|
+
import kotlinx.serialization.json.put
|
|
16
|
+
import okhttp3.OkHttpClient
|
|
17
|
+
import java.io.IOException
|
|
18
|
+
import java.net.URLEncoder
|
|
19
|
+
|
|
20
|
+
data class WalletDevice(
|
|
21
|
+
val id: String,
|
|
22
|
+
val label: String?,
|
|
23
|
+
val shareVersion: Int?,
|
|
24
|
+
val createdAt: String?,
|
|
25
|
+
val lastUsedAt: String?,
|
|
26
|
+
val revokedAt: String?,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
data class ServerWallet(
|
|
30
|
+
val id: String,
|
|
31
|
+
val address: String,
|
|
32
|
+
val status: String,
|
|
33
|
+
val protected: Boolean,
|
|
34
|
+
/** Null without a backup; false for a legacy backup that has no passkey public key. */
|
|
35
|
+
val backupVerifiable: Boolean?,
|
|
36
|
+
val shareVersion: Int,
|
|
37
|
+
val devices: List<WalletDevice>,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
/** `GET /v1/wallet/app-config` for this app (platform=android, appId=package name). */
|
|
41
|
+
data class WalletAppConfig(
|
|
42
|
+
val allowed: Boolean,
|
|
43
|
+
val approved: Boolean,
|
|
44
|
+
val projectId: String?,
|
|
45
|
+
val enabled: Boolean,
|
|
46
|
+
val chainIds: List<Long>,
|
|
47
|
+
/** Missing means true: signing needs a confirmation. */
|
|
48
|
+
val confirmSigning: Boolean,
|
|
49
|
+
/** PRF salt version for new backups. Missing means 1. */
|
|
50
|
+
val prfSaltVersion: Int,
|
|
51
|
+
val rpId: String,
|
|
52
|
+
val minFrameProtocol: Int?,
|
|
53
|
+
/** Android: whether the running app's signing certificate is registered; null when not asked or not registered. */
|
|
54
|
+
val signatureMatches: Boolean? = null,
|
|
55
|
+
/** `X-Request-Id` of the app-config response. */
|
|
56
|
+
val diagnosticId: String? = null,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
internal data class DeviceProof(val deviceId: String, val challenge: String, val signature: String) {
|
|
60
|
+
fun fill(b: JsonObjectBuilder) {
|
|
61
|
+
b.put("deviceId", deviceId)
|
|
62
|
+
b.put("challenge", challenge)
|
|
63
|
+
b.put("signature", signature)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
internal data class RestoreStart(
|
|
68
|
+
val backupCiphertext: String?,
|
|
69
|
+
val credentialId: String?,
|
|
70
|
+
val companyShare: String?,
|
|
71
|
+
val upgradeRecommended: Boolean,
|
|
72
|
+
val passkeyChallenge: String?,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
internal data class CreatedWallet(val wallet: ServerWallet?, val deviceId: String, val walletId: String, val shareVersion: Int)
|
|
76
|
+
|
|
77
|
+
internal data class EnrolledDevice(val deviceId: String, val shareVersion: Int)
|
|
78
|
+
|
|
79
|
+
/** HTTP contract of `apps/wallet/src/api.ts` (protocol 2), byte for byte in paths, headers and bodies. */
|
|
80
|
+
internal class WalletApi(
|
|
81
|
+
apiUrl: String,
|
|
82
|
+
private val publishableKey: String,
|
|
83
|
+
private val client: OkHttpClient = Http.defaultClient,
|
|
84
|
+
) {
|
|
85
|
+
private val base = apiUrl.trimEnd('/')
|
|
86
|
+
|
|
87
|
+
private suspend fun call(method: String, path: String, session: String?, body: JsonObject? = null): HttpResult {
|
|
88
|
+
val headers = LinkedHashMap<String, String>()
|
|
89
|
+
headers["x-api-key"] = publishableKey
|
|
90
|
+
headers["accept"] = "application/json"
|
|
91
|
+
if (session != null) headers["authorization"] = "Bearer $session"
|
|
92
|
+
if (body != null) headers["content-type"] = "application/json"
|
|
93
|
+
headers["user-agent"] = Http.userAgent()
|
|
94
|
+
return try {
|
|
95
|
+
Http.execute(client, Http.request("$base/v1/wallet$path", method, headers, body))
|
|
96
|
+
} catch (e: IOException) {
|
|
97
|
+
throw WalletException(WalletErrorCode.API_ERROR, "network error", e)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private fun ensureOk(res: HttpResult): JsonObject {
|
|
102
|
+
if (res.ok) return res.json
|
|
103
|
+
val serverCode = res.json.str("code")
|
|
104
|
+
val id = res.requestId
|
|
105
|
+
if (serverCode != null && serverCode in RESTORE_CODES) throw walletError(WalletErrorCode.NEEDS_RESTORE, diagnosticId = id)
|
|
106
|
+
if (serverCode != null && serverCode in KNOWN_CODES) throw walletError(WalletErrorCode.valueOf(serverCode), diagnosticId = id)
|
|
107
|
+
throw walletError(STATUS_CODES[res.status] ?: WalletErrorCode.API_ERROR, diagnosticId = id)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
suspend fun appConfig(packageName: String, certSha256: String? = null): WalletAppConfig {
|
|
111
|
+
val cert = certSha256?.let { "&certSha256=${enc(it)}" } ?: ""
|
|
112
|
+
val res = call("GET", "/app-config?platform=android&appId=${enc(packageName)}$cert", null)
|
|
113
|
+
if (res.status == 403 || res.status == 404) {
|
|
114
|
+
return WalletAppConfig(false, false, null, false, emptyList(), true, 1, DEFAULT_RP_ID, null, null, res.requestId)
|
|
115
|
+
}
|
|
116
|
+
val data = ensureOk(res)
|
|
117
|
+
val wc = data["walletConfig"] as? JsonObject ?: JsonObject(emptyMap())
|
|
118
|
+
return WalletAppConfig(
|
|
119
|
+
allowed = data.bool("allowed") != false,
|
|
120
|
+
approved = data.bool("approved") == true,
|
|
121
|
+
projectId = data.str("projectId"),
|
|
122
|
+
enabled = wc.bool("enabled") != false,
|
|
123
|
+
chainIds = (wc["chainIds"] as? JsonArray)?.mapNotNull { (it as? JsonPrimitive)?.longOrNull } ?: emptyList(),
|
|
124
|
+
confirmSigning = wc.bool("confirmSigning") != false,
|
|
125
|
+
prfSaltVersion = if ((wc["prfSaltVersion"] as? JsonPrimitive)?.intOrNull == 2) 2 else 1,
|
|
126
|
+
rpId = data.str("rpId") ?: DEFAULT_RP_ID,
|
|
127
|
+
minFrameProtocol = (data["minFrameProtocol"] as? JsonPrimitive)?.intOrNull,
|
|
128
|
+
signatureMatches = data.bool("signatureMatches"),
|
|
129
|
+
diagnosticId = res.requestId,
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
suspend fun getMe(session: String): ServerWallet? {
|
|
134
|
+
val res = call("GET", "/me", session)
|
|
135
|
+
if (res.status == 404) return null
|
|
136
|
+
val data = ensureOk(res)
|
|
137
|
+
return normalizeWallet(data["wallet"]?.takeIf { it !is JsonNull } ?: res.json)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
suspend fun proofChallenge(session: String, kind: String): ProofNonce {
|
|
141
|
+
val data = ensureOk(call("POST", "/me/proof-challenge", session, buildJsonObject { put("kind", kind) }))
|
|
142
|
+
val nonce = data.str("nonce")
|
|
143
|
+
val expiresAt = (data["expiresAt"] as? JsonPrimitive)?.takeIf { !it.isString }?.longOrNull
|
|
144
|
+
if (nonce == null || expiresAt == null) throw walletError(WalletErrorCode.API_ERROR)
|
|
145
|
+
return ProofNonce(nonce, expiresAt)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
suspend fun createWallet(
|
|
149
|
+
session: String,
|
|
150
|
+
address: String,
|
|
151
|
+
companyShare: String,
|
|
152
|
+
devicePublicKey: DevicePublicJwk,
|
|
153
|
+
proof: String,
|
|
154
|
+
bound: ProofNonce,
|
|
155
|
+
): CreatedWallet {
|
|
156
|
+
val body = buildJsonObject {
|
|
157
|
+
put("address", address)
|
|
158
|
+
put("companyShare", companyShare)
|
|
159
|
+
put("devicePublicKey", devicePublicKey.toJson())
|
|
160
|
+
put("proof", proof)
|
|
161
|
+
put("nonce", bound.nonce)
|
|
162
|
+
put("nonceExpiresAt", bound.expiresAt)
|
|
163
|
+
put("protocol", FRAME_PROTOCOL)
|
|
164
|
+
}
|
|
165
|
+
val data = ensureOk(call("POST", "/me", session, body))
|
|
166
|
+
val wallet = when {
|
|
167
|
+
data["wallet"] is JsonObject -> normalizeWallet(data["wallet"]!!)
|
|
168
|
+
data["id"] != null && data["id"] !is JsonNull -> normalizeWallet(data)
|
|
169
|
+
else -> null
|
|
170
|
+
}
|
|
171
|
+
val device = data["device"] as? JsonObject
|
|
172
|
+
return CreatedWallet(
|
|
173
|
+
wallet = wallet,
|
|
174
|
+
walletId = data.str("walletId") ?: wallet?.id ?: "undefined",
|
|
175
|
+
deviceId = data.str("deviceId") ?: device?.str("id") ?: "undefined",
|
|
176
|
+
shareVersion = data.int("shareVersion") ?: wallet?.shareVersion ?: 1,
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
suspend fun confirmWallet(session: String, proof: DeviceProof) {
|
|
181
|
+
ensureOk(call("POST", "/me/confirm", session, buildJsonObject { proof.fill(this) }))
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
suspend fun discardPending(session: String) {
|
|
185
|
+
val res = call("DELETE", "/me/pending", session)
|
|
186
|
+
if (res.status == 404) return
|
|
187
|
+
ensureOk(res)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
suspend fun challenge(session: String, deviceId: String): String {
|
|
191
|
+
val data = ensureOk(call("POST", "/me/share/challenge", session, buildJsonObject { put("deviceId", deviceId) }))
|
|
192
|
+
return data.str("challenge") ?: throw walletError(WalletErrorCode.API_ERROR)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
suspend fun releaseShare(session: String, proof: DeviceProof): String {
|
|
196
|
+
val data = ensureOk(call("POST", "/me/share", session, buildJsonObject { proof.fill(this) }))
|
|
197
|
+
return data.str("companyShare") ?: throw walletError(WalletErrorCode.API_ERROR)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
class BackupKeyFields(val credentialPublicKey: String?, val credentialAlg: Int?, val attestationObject: String?)
|
|
201
|
+
|
|
202
|
+
suspend fun putBackup(
|
|
203
|
+
session: String,
|
|
204
|
+
proof: DeviceProof,
|
|
205
|
+
backupCiphertext: String,
|
|
206
|
+
credentialId: String,
|
|
207
|
+
newCompanyShare: String,
|
|
208
|
+
key: BackupKeyFields,
|
|
209
|
+
upgrade: Boolean,
|
|
210
|
+
): Int {
|
|
211
|
+
val body = buildJsonObject {
|
|
212
|
+
proof.fill(this)
|
|
213
|
+
put("backupCiphertext", backupCiphertext)
|
|
214
|
+
put("credentialId", credentialId)
|
|
215
|
+
put("newCompanyShare", newCompanyShare)
|
|
216
|
+
if (key.credentialPublicKey != null && key.credentialAlg != null) {
|
|
217
|
+
put("credentialPublicKey", key.credentialPublicKey)
|
|
218
|
+
put("credentialAlg", key.credentialAlg)
|
|
219
|
+
} else if (key.attestationObject != null) {
|
|
220
|
+
put("attestationObject", key.attestationObject)
|
|
221
|
+
}
|
|
222
|
+
if (upgrade) put("upgrade", true)
|
|
223
|
+
put("protocol", FRAME_PROTOCOL)
|
|
224
|
+
}
|
|
225
|
+
val data = ensureOk(call("PUT", "/me/backup", session, body))
|
|
226
|
+
return data.int("shareVersion") ?: throw walletError(WalletErrorCode.API_ERROR)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
suspend fun restoreStart(session: String): RestoreStart {
|
|
230
|
+
val data = ensureOk(call("POST", "/me/restore/start", session, JsonObject(emptyMap())))
|
|
231
|
+
val companyShare = data.str("companyShare")
|
|
232
|
+
val passkeyChallenge = data.str("passkeyChallenge")
|
|
233
|
+
if (companyShare == null && passkeyChallenge == null) throw walletError(WalletErrorCode.API_ERROR)
|
|
234
|
+
return RestoreStart(
|
|
235
|
+
backupCiphertext = data.str("backupCiphertext"),
|
|
236
|
+
credentialId = data.str("credentialId"),
|
|
237
|
+
companyShare = companyShare,
|
|
238
|
+
passkeyChallenge = passkeyChallenge,
|
|
239
|
+
upgradeRecommended = companyShare != null || data.bool("upgradeRecommended") == true,
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
suspend fun restoreAssert(session: String, assertion: PasskeyAssertion, challenge: String): String {
|
|
244
|
+
val body = buildJsonObject {
|
|
245
|
+
put("credentialId", assertion.credentialId)
|
|
246
|
+
put("clientDataJSON", assertion.clientDataJSON)
|
|
247
|
+
put("authenticatorData", assertion.authenticatorData)
|
|
248
|
+
put("signature", assertion.signature)
|
|
249
|
+
put("challenge", challenge)
|
|
250
|
+
}
|
|
251
|
+
val data = ensureOk(call("POST", "/me/restore/assert", session, body))
|
|
252
|
+
return data.str("companyShare") ?: throw walletError(WalletErrorCode.API_ERROR)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
suspend fun restore(
|
|
256
|
+
session: String,
|
|
257
|
+
newCompanyShare: String,
|
|
258
|
+
newBackupCiphertext: String,
|
|
259
|
+
newDevicePublicKey: DevicePublicJwk,
|
|
260
|
+
credentialId: String,
|
|
261
|
+
proof: String,
|
|
262
|
+
bound: ProofNonce,
|
|
263
|
+
): EnrolledDevice {
|
|
264
|
+
val body = buildJsonObject {
|
|
265
|
+
put("newCompanyShare", newCompanyShare)
|
|
266
|
+
put("newBackupCiphertext", newBackupCiphertext)
|
|
267
|
+
put("newDevicePublicKey", newDevicePublicKey.toJson())
|
|
268
|
+
put("credentialId", credentialId)
|
|
269
|
+
put("proof", proof)
|
|
270
|
+
put("nonce", bound.nonce)
|
|
271
|
+
put("nonceExpiresAt", bound.expiresAt)
|
|
272
|
+
put("protocol", FRAME_PROTOCOL)
|
|
273
|
+
}
|
|
274
|
+
val data = ensureOk(call("POST", "/me/restore", session, body))
|
|
275
|
+
return EnrolledDevice(
|
|
276
|
+
data.str("deviceId") ?: (data["device"] as? JsonObject)?.str("id") ?: "undefined",
|
|
277
|
+
data.int("shareVersion") ?: throw walletError(WalletErrorCode.API_ERROR),
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
suspend fun addDevice(session: String, devicePublicKey: DevicePublicJwk, proof: String, bound: ProofNonce): EnrolledDevice {
|
|
282
|
+
val body = buildJsonObject {
|
|
283
|
+
put("devicePublicKey", devicePublicKey.toJson())
|
|
284
|
+
put("proof", proof)
|
|
285
|
+
put("nonce", bound.nonce)
|
|
286
|
+
put("nonceExpiresAt", bound.expiresAt)
|
|
287
|
+
put("protocol", FRAME_PROTOCOL)
|
|
288
|
+
}
|
|
289
|
+
val data = ensureOk(call("POST", "/me/devices", session, body))
|
|
290
|
+
return EnrolledDevice(
|
|
291
|
+
data.str("deviceId") ?: "undefined",
|
|
292
|
+
data.int("shareVersion") ?: throw walletError(WalletErrorCode.API_ERROR),
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
suspend fun revokeDevice(session: String, deviceId: String, proof: DeviceProof) {
|
|
297
|
+
ensureOk(call("DELETE", "/me/devices/${enc(deviceId)}", session, buildJsonObject { proof.fill(this) }))
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
companion object {
|
|
301
|
+
/** Protocol sent as `protocol` in proof and backup requests (the API's CURRENT_FRAME_PROTOCOL). */
|
|
302
|
+
const val FRAME_PROTOCOL = 2
|
|
303
|
+
const val DEFAULT_RP_ID = "wallet.authon.dev"
|
|
304
|
+
|
|
305
|
+
private val STATUS_CODES = mapOf(
|
|
306
|
+
400 to WalletErrorCode.INVALID_REQUEST,
|
|
307
|
+
401 to WalletErrorCode.UNAUTHORIZED,
|
|
308
|
+
403 to WalletErrorCode.FORBIDDEN,
|
|
309
|
+
404 to WalletErrorCode.NOT_FOUND,
|
|
310
|
+
409 to WalletErrorCode.ALREADY_EXISTS,
|
|
311
|
+
426 to WalletErrorCode.FRAME_OUTDATED,
|
|
312
|
+
429 to WalletErrorCode.RATE_LIMITED,
|
|
313
|
+
)
|
|
314
|
+
private val RESTORE_CODES = setOf("NEEDS_RESTORE", "DEVICE_REVOKED", "DEVICE_NOT_FOUND", "STALE_SHARE_VERSION")
|
|
315
|
+
private val KNOWN_CODES = setOf(
|
|
316
|
+
"NOT_PROTECTED", "ORIGIN_NOT_ALLOWED", "ALREADY_EXISTS", "NOT_FOUND", "LAST_DEVICE", "SESSION_REQUIRED",
|
|
317
|
+
"WALLET_PENDING", "RATE_LIMITED", "FRAME_OUTDATED", "BACKUP_KEY_REQUIRED", "ALREADY_UPGRADED",
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
/** encodeURIComponent. */
|
|
321
|
+
fun enc(value: String): String = URLEncoder.encode(value, "UTF-8")
|
|
322
|
+
.replace("+", "%20").replace("%21", "!").replace("%27", "'")
|
|
323
|
+
.replace("%28", "(").replace("%29", ")").replace("%7E", "~")
|
|
324
|
+
|
|
325
|
+
private fun JsonObject.str(key: String): String? = (this[key] as? JsonPrimitive)?.takeIf { it.isString }?.content
|
|
326
|
+
|
|
327
|
+
private fun JsonObject.bool(key: String): Boolean? = (this[key] as? JsonPrimitive)?.takeIf { !it.isString }?.booleanOrNull
|
|
328
|
+
|
|
329
|
+
/** `Number(x)` for counters the server sends as numbers (or numeric strings). */
|
|
330
|
+
private fun JsonObject.int(key: String): Int? = (this[key] as? JsonPrimitive)?.contentOrNull?.toBigDecimalOrNull()?.toInt()
|
|
331
|
+
|
|
332
|
+
private fun JsonElement.asObj(): JsonObject = this as? JsonObject ?: JsonObject(emptyMap())
|
|
333
|
+
|
|
334
|
+
fun normalizeWallet(raw: JsonElement): ServerWallet {
|
|
335
|
+
val w = raw.asObj()
|
|
336
|
+
val prot = w.bool("protected") ?: (w["protectedAt"].let { it != null && it !is JsonNull && (it as? JsonPrimitive)?.contentOrNull?.isNotEmpty() == true })
|
|
337
|
+
return ServerWallet(
|
|
338
|
+
id = w.anyString("id") ?: w.anyString("walletId") ?: "undefined",
|
|
339
|
+
address = w.anyString("address") ?: "undefined",
|
|
340
|
+
status = w.anyString("status") ?: "active",
|
|
341
|
+
protected = prot,
|
|
342
|
+
backupVerifiable = w.bool("backupVerifiable"),
|
|
343
|
+
shareVersion = w.int("shareVersion") ?: 1,
|
|
344
|
+
devices = (w["devices"] as? JsonArray)?.mapNotNull { d ->
|
|
345
|
+
val o = d as? JsonObject ?: return@mapNotNull null
|
|
346
|
+
WalletDevice(
|
|
347
|
+
id = o.anyString("id") ?: return@mapNotNull null,
|
|
348
|
+
label = o.str("label"),
|
|
349
|
+
shareVersion = o.int("shareVersion"),
|
|
350
|
+
createdAt = o.str("createdAt"),
|
|
351
|
+
lastUsedAt = o.str("lastUsedAt"),
|
|
352
|
+
revokedAt = o.str("revokedAt"),
|
|
353
|
+
)
|
|
354
|
+
} ?: emptyList(),
|
|
355
|
+
)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private fun JsonObject.anyString(key: String): String? =
|
|
359
|
+
(this[key] as? JsonPrimitive)?.takeIf { it !is JsonNull }?.contentOrNull
|
|
360
|
+
}
|
|
361
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// GENERATED by scripts/sync-native-wallet.mjs from kotlin/authon/src/main/kotlin/dev/authon/sdk/wallet/WalletCapabilities.kt. Do not edit: change the source and run the script.
|
|
2
|
+
package dev.authon.sdk.wallet
|
|
3
|
+
|
|
4
|
+
/** NATIVE.md section 9. [wire] is the contract string. */
|
|
5
|
+
enum class CapabilityStatus(val wire: String) { AVAILABLE("available"), UNAVAILABLE("unavailable"), UNKNOWN("unknown") }
|
|
6
|
+
|
|
7
|
+
data class CapabilityCheck(val status: CapabilityStatus, val reason: String? = null) {
|
|
8
|
+
companion object {
|
|
9
|
+
val AVAILABLE = CapabilityCheck(CapabilityStatus.AVAILABLE)
|
|
10
|
+
fun unavailable(reason: String) = CapabilityCheck(CapabilityStatus.UNAVAILABLE, reason)
|
|
11
|
+
fun unknown(reason: String) = CapabilityCheck(CapabilityStatus.UNKNOWN, reason)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Whether this device and app can use the wallet, answered before sign-in: no session, no
|
|
17
|
+
* login or passkey UI, no wallet is created.
|
|
18
|
+
*/
|
|
19
|
+
data class WalletCapabilities(
|
|
20
|
+
/** Any unavailable -> unavailable; else any unknown -> unknown; else available. */
|
|
21
|
+
val ready: CapabilityStatus,
|
|
22
|
+
val app: CapabilityCheck,
|
|
23
|
+
val wallet: CapabilityCheck,
|
|
24
|
+
val secureStorage: CapabilityCheck,
|
|
25
|
+
val deviceLock: CapabilityCheck,
|
|
26
|
+
val passkeyBackup: CapabilityCheck,
|
|
27
|
+
/** `X-Request-Id` of the app-config response, when the API answered. */
|
|
28
|
+
val diagnosticId: String?,
|
|
29
|
+
) {
|
|
30
|
+
companion object {
|
|
31
|
+
fun readyOf(checks: List<CapabilityCheck>): CapabilityStatus = when {
|
|
32
|
+
checks.any { it.status == CapabilityStatus.UNAVAILABLE } -> CapabilityStatus.UNAVAILABLE
|
|
33
|
+
checks.any { it.status == CapabilityStatus.UNKNOWN } -> CapabilityStatus.UNKNOWN
|
|
34
|
+
else -> CapabilityStatus.AVAILABLE
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Static device checks behind capabilities(); the Android implementation is [AndroidCapabilityProbe]. */
|
|
40
|
+
interface CapabilityProbe {
|
|
41
|
+
/** SHA-256 of the running app's signing certificate, uppercase hex with colons; null when unreadable. */
|
|
42
|
+
fun signingCertSha256(): String?
|
|
43
|
+
suspend fun secureStorage(): CapabilityCheck
|
|
44
|
+
fun deviceLock(): CapabilityCheck
|
|
45
|
+
fun passkeyBackup(): CapabilityCheck
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
internal object Capabilities {
|
|
49
|
+
const val APP_NOT_REGISTERED = "APP_NOT_REGISTERED"
|
|
50
|
+
const val APP_SIGNATURE_MISMATCH = "APP_SIGNATURE_MISMATCH"
|
|
51
|
+
const val APP_NOT_APPROVED = "APP_NOT_APPROVED"
|
|
52
|
+
const val WALLET_DISABLED = "WALLET_DISABLED"
|
|
53
|
+
const val NETWORK_ERROR = "NETWORK_ERROR"
|
|
54
|
+
const val API_ERROR = "API_ERROR"
|
|
55
|
+
const val HARDWARE_KEYSTORE_UNAVAILABLE = "HARDWARE_KEYSTORE_UNAVAILABLE"
|
|
56
|
+
const val SIMULATOR = "SIMULATOR"
|
|
57
|
+
const val CHECK_FAILED = "CHECK_FAILED"
|
|
58
|
+
const val DEVICE_LOCK_NOT_SET = "DEVICE_LOCK_NOT_SET"
|
|
59
|
+
const val OS_UNSUPPORTED = "OS_UNSUPPORTED"
|
|
60
|
+
const val PASSKEY_API_UNAVAILABLE = "PASSKEY_API_UNAVAILABLE"
|
|
61
|
+
const val PRF_PROVIDER_UNVERIFIED = "PRF_PROVIDER_UNVERIFIED"
|
|
62
|
+
|
|
63
|
+
fun appCheck(cfg: WalletAppConfig): CapabilityCheck = when {
|
|
64
|
+
!cfg.allowed || cfg.projectId == null -> CapabilityCheck.unavailable(APP_NOT_REGISTERED)
|
|
65
|
+
cfg.signatureMatches == false -> CapabilityCheck.unavailable(APP_SIGNATURE_MISMATCH)
|
|
66
|
+
!cfg.approved -> CapabilityCheck.unknown(APP_NOT_APPROVED)
|
|
67
|
+
else -> CapabilityCheck.AVAILABLE
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fun walletCheck(cfg: WalletAppConfig): CapabilityCheck =
|
|
71
|
+
if (cfg.enabled) CapabilityCheck.AVAILABLE else CapabilityCheck.unavailable(WALLET_DISABLED)
|
|
72
|
+
|
|
73
|
+
suspend fun evaluate(api: WalletApi, appId: String, probe: CapabilityProbe): Pair<WalletCapabilities, WalletAppConfig?> {
|
|
74
|
+
val cert = runCatching { probe.signingCertSha256() }.getOrNull()
|
|
75
|
+
var config: WalletAppConfig? = null
|
|
76
|
+
val (app, wallet, diagnosticId) = try {
|
|
77
|
+
val cfg = api.appConfig(appId, cert)
|
|
78
|
+
config = cfg
|
|
79
|
+
Triple(appCheck(cfg), walletCheck(cfg), cfg.diagnosticId)
|
|
80
|
+
} catch (e: WalletException) {
|
|
81
|
+
// No response at all (WalletApi wraps the IOException) vs. an API error.
|
|
82
|
+
val reason = if (e.cause is java.io.IOException) NETWORK_ERROR else API_ERROR
|
|
83
|
+
Triple(CapabilityCheck.unknown(reason), CapabilityCheck.unknown(reason), e.diagnosticId)
|
|
84
|
+
}
|
|
85
|
+
val secure = runCatching { probe.secureStorage() }.getOrElse { CapabilityCheck.unknown(CHECK_FAILED) }
|
|
86
|
+
val lock = runCatching { probe.deviceLock() }.getOrElse { CapabilityCheck.unknown(CHECK_FAILED) }
|
|
87
|
+
val passkey = runCatching { probe.passkeyBackup() }.getOrElse { CapabilityCheck.unknown(PASSKEY_API_UNAVAILABLE) }
|
|
88
|
+
val checks = listOf(app, wallet, secure, lock, passkey)
|
|
89
|
+
return WalletCapabilities(WalletCapabilities.readyOf(checks), app, wallet, secure, lock, passkey, diagnosticId) to config
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// GENERATED by scripts/sync-native-wallet.mjs from kotlin/authon/src/main/kotlin/dev/authon/sdk/wallet/WalletError.kt. Do not edit: change the source and run the script.
|
|
2
|
+
package dev.authon.sdk.wallet
|
|
3
|
+
|
|
4
|
+
/** Error codes of the web wallet frame (`apps/wallet/src/errors.ts`); [name] is the wire string. */
|
|
5
|
+
enum class 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
|
+
|
|
27
|
+
/** The approval UI could not be shown. Nothing was signed. */
|
|
28
|
+
CONFIRM_WINDOW_BLOCKED,
|
|
29
|
+
|
|
30
|
+
/** The approval UI was dismissed before the user approved. Nothing was signed. */
|
|
31
|
+
CONFIRM_WINDOW_CLOSED,
|
|
32
|
+
|
|
33
|
+
/** The message or typed data is too long to show in full; it is refused. */
|
|
34
|
+
MESSAGE_TOO_LARGE,
|
|
35
|
+
|
|
36
|
+
/** The API no longer serves this SDK protocol version. */
|
|
37
|
+
FRAME_OUTDATED,
|
|
38
|
+
|
|
39
|
+
/** The backup passkey did not report a usable public key. */
|
|
40
|
+
BACKUP_KEY_REQUIRED,
|
|
41
|
+
|
|
42
|
+
/** upgradeBackup on a wallet whose backup is already verifiable. */
|
|
43
|
+
ALREADY_UPGRADED,
|
|
44
|
+
|
|
45
|
+
/** The wallet predates HD wallets and has only account 0. */
|
|
46
|
+
ACCOUNT_UNAVAILABLE,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A wallet error with the fields of NATIVE.md section 10. [diagnosticId] is the server's request id
|
|
51
|
+
* when the API answered (log only this id, never tokens or key material).
|
|
52
|
+
*/
|
|
53
|
+
class WalletException(
|
|
54
|
+
val code: WalletErrorCode,
|
|
55
|
+
message: String? = null,
|
|
56
|
+
cause: Throwable? = null,
|
|
57
|
+
val diagnosticId: String? = null,
|
|
58
|
+
val retryable: Boolean = code in RETRYABLE,
|
|
59
|
+
) : Exception(message ?: code.name, cause) {
|
|
60
|
+
companion object {
|
|
61
|
+
/** Codes worth retrying unchanged (later); everything else needs a different action. */
|
|
62
|
+
val RETRYABLE: Set<WalletErrorCode> = setOf(
|
|
63
|
+
WalletErrorCode.API_ERROR,
|
|
64
|
+
WalletErrorCode.RATE_LIMITED,
|
|
65
|
+
WalletErrorCode.WALLET_PENDING,
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
internal fun walletError(code: WalletErrorCode, message: String? = null, diagnosticId: String? = null): WalletException =
|
|
71
|
+
WalletException(code, message, diagnosticId = diagnosticId)
|
|
72
|
+
|
|
73
|
+
internal fun Throwable.toWalletException(): WalletException =
|
|
74
|
+
this as? WalletException ?: WalletException(WalletErrorCode.INTERNAL, "unexpected error", this)
|