@authon/react-native-wallet 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -2
- package/android/build.gradle +40 -0
- package/android/consumer-rules.pro +10 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/dev/authon/reactnative/wallet/AuthonWalletModule.kt +181 -0
- package/android/src/vendor/GENERATED.md +4 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/AuthonWallet.kt +191 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Bytes.kt +74 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/CredentialManagerPasskeys.kt +192 -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/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 +82 -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 +232 -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/WalletApi.kt +353 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletError.kt +58 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletOps.kt +449 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletTypes.kt +71 -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 +76 -0
- package/build/client.d.ts.map +1 -0
- package/build/client.js +349 -0
- package/build/client.js.map +1 -0
- package/build/errors.d.ts +18 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +9 -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 +39 -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 +121 -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 +37 -0
- package/ios/AuthonWalletModule.swift +190 -0
- package/ios/vendor/AuthonWallet/AuthonWallet.swift +212 -0
- package/ios/vendor/AuthonWallet/Crypto/BIP39Wordlist.swift +262 -0
- package/ios/vendor/AuthonWallet/Crypto/HDKey.swift +108 -0
- package/ios/vendor/AuthonWallet/Crypto/Secp256k1.swift +104 -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 +350 -0
- package/ios/vendor/AuthonWallet/Wallet/WalletOps.swift +555 -0
- package/ios/vendor/AuthonWallet/WalletError.swift +54 -0
- package/ios/vendor/CKeccak/LICENSE +22 -0
- package/ios/vendor/CKeccak/README.md +27 -0
- package/ios/vendor/CKeccak/byte_order.h +59 -0
- package/ios/vendor/CKeccak/include/ckeccak.h +9 -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/sha3.c +413 -0
- package/ios/vendor/CKeccak/sha3.h +89 -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 +65 -3
- package/src/authonContext.ts +30 -0
- package/src/client.ts +375 -0
- package/src/errors.ts +52 -0
- package/src/index.ts +21 -0
- package/src/native.ts +50 -0
- package/src/types.ts +120 -0
- package/src/useAuthonWallet.ts +103 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
// GENERATED by scripts/sync-native-wallet.mjs from swift/Sources/AuthonWallet/Wallet/WalletAPI.swift. Do not edit: change the source and run the script.
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
public struct WalletDevice: Equatable, Sendable {
|
|
5
|
+
public var id: String
|
|
6
|
+
public var label: String?
|
|
7
|
+
public var shareVersion: Int?
|
|
8
|
+
public var createdAt: String?
|
|
9
|
+
public var lastUsedAt: String?
|
|
10
|
+
public var revokedAt: String?
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public struct ServerWallet: Equatable, Sendable {
|
|
14
|
+
public var id: String
|
|
15
|
+
public var address: String
|
|
16
|
+
public var status: String
|
|
17
|
+
public var isProtected: Bool
|
|
18
|
+
/// nil without a backup; false for a legacy backup that has no passkey public key.
|
|
19
|
+
public var backupVerifiable: Bool?
|
|
20
|
+
public var shareVersion: Int
|
|
21
|
+
public var devices: [WalletDevice]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/// `GET /v1/wallet/app-config`, the native counterpart of the frame's frame-config.
|
|
25
|
+
public struct WalletAppConfig: Equatable, Sendable {
|
|
26
|
+
public var projectId: String?
|
|
27
|
+
/// The app (team ID + bundle ID) is registered for this project.
|
|
28
|
+
public var allowed: Bool
|
|
29
|
+
/// A platform admin approved the registration, so passkeys for the wallet RP work.
|
|
30
|
+
public var approved: Bool
|
|
31
|
+
public var enabled: Bool
|
|
32
|
+
public var chainIds: [Int]
|
|
33
|
+
/// Server-side project setting. Missing means true.
|
|
34
|
+
public var confirmSigning: Bool
|
|
35
|
+
public var prfSaltVersion: BackupVersion
|
|
36
|
+
public var rpId: String
|
|
37
|
+
public var minFrameProtocol: Int?
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
enum ProofKind: String {
|
|
41
|
+
case create, device, restore
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
struct DeviceProof: Equatable {
|
|
45
|
+
var deviceId: String
|
|
46
|
+
var challenge: String
|
|
47
|
+
var signature: String
|
|
48
|
+
|
|
49
|
+
var fields: [(String, JSONValue)] {
|
|
50
|
+
[("deviceId", .string(deviceId)), ("challenge", .string(challenge)), ("signature", .string(signature))]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
struct RestoreStart {
|
|
55
|
+
var backupCiphertext: String?
|
|
56
|
+
var credentialId: String?
|
|
57
|
+
/// Present only for backups without a registered passkey key (older backups).
|
|
58
|
+
var companyShare: String?
|
|
59
|
+
var upgradeRecommended: Bool
|
|
60
|
+
var passkeyChallenge: String?
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/// HTTP client for `/v1/wallet/*`, request for request the same as `apps/wallet/src/api.ts`.
|
|
64
|
+
final class WalletAPIClient: @unchecked Sendable {
|
|
65
|
+
/// Protocol sent as `protocol` in proof and backup requests (the API's CURRENT_FRAME_PROTOCOL).
|
|
66
|
+
static let frameProtocol = 2
|
|
67
|
+
|
|
68
|
+
private let base: String
|
|
69
|
+
let publishableKey: String
|
|
70
|
+
private let session: URLSession
|
|
71
|
+
|
|
72
|
+
init(apiURL: String, publishableKey: String, session: URLSession = .shared) {
|
|
73
|
+
var b = apiURL
|
|
74
|
+
while b.hasSuffix("/") { b.removeLast() }
|
|
75
|
+
base = b
|
|
76
|
+
self.publishableKey = publishableKey
|
|
77
|
+
self.session = session
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private static let statusCodes: [Int: WalletErrorCode] = [
|
|
81
|
+
400: .invalidRequest, 401: .unauthorized, 403: .forbidden, 404: .notFound,
|
|
82
|
+
409: .alreadyExists, 426: .frameOutdated, 429: .rateLimited,
|
|
83
|
+
]
|
|
84
|
+
private static let restoreCodes: Set<String> = ["NEEDS_RESTORE", "DEVICE_REVOKED", "DEVICE_NOT_FOUND", "STALE_SHARE_VERSION"]
|
|
85
|
+
private static let knownCodes: Set<String> = [
|
|
86
|
+
"NOT_PROTECTED", "ORIGIN_NOT_ALLOWED", "ALREADY_EXISTS", "NOT_FOUND", "LAST_DEVICE", "SESSION_REQUIRED",
|
|
87
|
+
"WALLET_PENDING", "RATE_LIMITED", "FRAME_OUTDATED", "BACKUP_KEY_REQUIRED", "ALREADY_UPGRADED",
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
struct Response {
|
|
91
|
+
var status: Int
|
|
92
|
+
var data: JSONObject
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private func call(_ method: String, _ path: String, _ token: String?, _ body: JSONValue? = nil) async throws -> Response {
|
|
96
|
+
guard let url = URL(string: "\(base)/v1/wallet\(path)") else { throw WalletError(.apiError, "invalid URL") }
|
|
97
|
+
var req = URLRequest(url: url)
|
|
98
|
+
req.httpMethod = method
|
|
99
|
+
req.cachePolicy = .reloadIgnoringLocalCacheData
|
|
100
|
+
req.httpShouldHandleCookies = false
|
|
101
|
+
req.setValue(publishableKey, forHTTPHeaderField: "x-api-key")
|
|
102
|
+
req.setValue("application/json", forHTTPHeaderField: "accept")
|
|
103
|
+
if let token { req.setValue("Bearer \(token)", forHTTPHeaderField: "authorization") }
|
|
104
|
+
if let body {
|
|
105
|
+
req.setValue("application/json", forHTTPHeaderField: "content-type")
|
|
106
|
+
req.httpBody = Data(body.compact().utf8)
|
|
107
|
+
}
|
|
108
|
+
let data: Data
|
|
109
|
+
let response: URLResponse
|
|
110
|
+
do {
|
|
111
|
+
(data, response) = try await session.data(for: req)
|
|
112
|
+
} catch {
|
|
113
|
+
throw WalletError(.apiError, "network error")
|
|
114
|
+
}
|
|
115
|
+
let status = (response as? HTTPURLResponse)?.statusCode ?? 0
|
|
116
|
+
let parsed = (try? JSONValue.parse(String(decoding: data, as: UTF8.self)))?.objectValue ?? JSONObject()
|
|
117
|
+
return Response(status: status, data: parsed)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private func ensureOk(_ res: Response) throws -> JSONObject {
|
|
121
|
+
if (200..<300).contains(res.status) { return res.data }
|
|
122
|
+
if case .string(let code) = res.data["code"] ?? .null {
|
|
123
|
+
if Self.restoreCodes.contains(code) { throw WalletError(.needsRestore) }
|
|
124
|
+
if Self.knownCodes.contains(code), let c = WalletErrorCode(rawValue: code) { throw WalletError(c) }
|
|
125
|
+
}
|
|
126
|
+
throw WalletError(Self.statusCodes[res.status] ?? .apiError)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private static func string(_ v: JSONValue?) -> String? { v?.stringValue }
|
|
130
|
+
|
|
131
|
+
/// JavaScript String(x) for ids.
|
|
132
|
+
private static func text(_ v: JSONValue?) -> String {
|
|
133
|
+
guard let v else { return "undefined" }
|
|
134
|
+
return EvmAddress.jsString(v)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/// JavaScript Number(x) for integer fields.
|
|
138
|
+
private static func int(_ v: JSONValue?, default d: Int? = nil) -> Int? {
|
|
139
|
+
guard let v, v != .null else { return d }
|
|
140
|
+
if let n = v.jsBigInt, let u = n.magnitude.uint64, u <= UInt64(Int.max) { return n.negative ? -Int(u) : Int(u) }
|
|
141
|
+
return nil
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
static func normalizeWallet(_ raw: JSONValue?) -> ServerWallet {
|
|
145
|
+
let w = raw?.objectValue ?? JSONObject()
|
|
146
|
+
let devices: [WalletDevice] = (w["devices"]?.arrayValue ?? []).map { d in
|
|
147
|
+
WalletDevice(
|
|
148
|
+
id: text(d["id"]),
|
|
149
|
+
label: string(d["label"]),
|
|
150
|
+
shareVersion: int(d["shareVersion"]),
|
|
151
|
+
createdAt: string(d["createdAt"]),
|
|
152
|
+
lastUsedAt: string(d["lastUsedAt"]),
|
|
153
|
+
revokedAt: d["revokedAt"]?.isTruthy == true ? text(d["revokedAt"]) : nil
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
let isProtected: Bool
|
|
157
|
+
if case .bool(let b) = w["protected"] ?? .null { isProtected = b } else { isProtected = w["protectedAt"]?.isTruthy == true }
|
|
158
|
+
var backupVerifiable: Bool?
|
|
159
|
+
if case .bool(let b) = w["backupVerifiable"] ?? .null { backupVerifiable = b }
|
|
160
|
+
let idValue = (w["id"].flatMap { $0 == .null ? nil : $0 }) ?? w["walletId"]
|
|
161
|
+
let statusValue = (w["status"].flatMap { $0 == .null ? nil : $0 }).map { text($0) } ?? "active"
|
|
162
|
+
return ServerWallet(
|
|
163
|
+
id: text(idValue),
|
|
164
|
+
address: text(w["address"]),
|
|
165
|
+
status: statusValue,
|
|
166
|
+
isProtected: isProtected,
|
|
167
|
+
backupVerifiable: backupVerifiable,
|
|
168
|
+
shareVersion: int(w["shareVersion"], default: 1) ?? 1,
|
|
169
|
+
devices: devices
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private static func bound(_ fields: [(String, JSONValue)], _ bound: ProofNonce) -> JSONValue {
|
|
174
|
+
.object(JSONObject(fields + [
|
|
175
|
+
("nonce", .string(bound.nonce)),
|
|
176
|
+
("nonceExpiresAt", .number(String(bound.expiresAt))),
|
|
177
|
+
("protocol", .number(String(frameProtocol))),
|
|
178
|
+
]))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// MARK: - Endpoints
|
|
182
|
+
|
|
183
|
+
func appConfig(platform: String, appId: String) async throws -> WalletAppConfig {
|
|
184
|
+
let query = "platform=\(Self.encode(platform))&appId=\(Self.encode(appId))"
|
|
185
|
+
let res = try await call("GET", "/app-config?\(query)", nil)
|
|
186
|
+
if res.status == 403 || res.status == 404 {
|
|
187
|
+
return WalletAppConfig(projectId: nil, allowed: false, approved: false, enabled: false, chainIds: [], confirmSigning: true, prfSaltVersion: .v1, rpId: "wallet.authon.dev", minFrameProtocol: nil)
|
|
188
|
+
}
|
|
189
|
+
let data = try ensureOk(res)
|
|
190
|
+
let cfg = data["walletConfig"]?.objectValue ?? JSONObject()
|
|
191
|
+
let rpId = Self.string(data["rpId"]).flatMap { $0.isEmpty ? nil : $0 } ?? "wallet.authon.dev"
|
|
192
|
+
return WalletAppConfig(
|
|
193
|
+
projectId: Self.string(data["projectId"]),
|
|
194
|
+
allowed: data["allowed"] != .bool(false),
|
|
195
|
+
approved: data["approved"] == .bool(true),
|
|
196
|
+
enabled: cfg["enabled"] == .bool(true),
|
|
197
|
+
chainIds: (cfg["chainIds"]?.arrayValue ?? []).compactMap { Self.int($0) },
|
|
198
|
+
confirmSigning: cfg["confirmSigning"] != .bool(false),
|
|
199
|
+
prfSaltVersion: Self.int(cfg["prfSaltVersion"]) == 2 ? .v2 : .v1,
|
|
200
|
+
rpId: rpId,
|
|
201
|
+
minFrameProtocol: Self.int(data["minFrameProtocol"])
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
func getMe(_ token: String) async throws -> ServerWallet? {
|
|
206
|
+
let res = try await call("GET", "/me", token)
|
|
207
|
+
if res.status == 404 { return nil }
|
|
208
|
+
let data = try ensureOk(res)
|
|
209
|
+
return Self.normalizeWallet(data["wallet"].flatMap { $0 == .null ? nil : $0 } ?? .object(data))
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
func proofChallenge(_ token: String, kind: ProofKind) async throws -> ProofNonce {
|
|
213
|
+
let data = try ensureOk(try await call("POST", "/me/proof-challenge", token, .object(JSONObject([("kind", .string(kind.rawValue))]))))
|
|
214
|
+
guard case .string(let nonce) = data["nonce"] ?? .null, case .number(let exp) = data["expiresAt"] ?? .null,
|
|
215
|
+
let expiresAt = Int64(exp) ?? Double(exp).map({ Int64($0) }) else {
|
|
216
|
+
throw WalletError(.apiError)
|
|
217
|
+
}
|
|
218
|
+
return ProofNonce(nonce: nonce, expiresAt: expiresAt)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
struct Created {
|
|
222
|
+
var wallet: ServerWallet?
|
|
223
|
+
var walletId: String
|
|
224
|
+
var deviceId: String
|
|
225
|
+
var shareVersion: Int
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
func createWallet(_ token: String, address: String, companyShare: String, devicePublicKey: DevicePublicJwk, proof: String, bound: ProofNonce) async throws -> Created {
|
|
229
|
+
let body = Self.bound([
|
|
230
|
+
("address", .string(address)), ("companyShare", .string(companyShare)),
|
|
231
|
+
("devicePublicKey", devicePublicKey.json), ("proof", .string(proof)),
|
|
232
|
+
], bound)
|
|
233
|
+
let data = try ensureOk(try await call("POST", "/me", token, body))
|
|
234
|
+
let wallet: ServerWallet?
|
|
235
|
+
if let w = data["wallet"], w.isTruthy {
|
|
236
|
+
wallet = Self.normalizeWallet(w)
|
|
237
|
+
} else if data["id"]?.isTruthy == true {
|
|
238
|
+
wallet = Self.normalizeWallet(.object(data))
|
|
239
|
+
} else {
|
|
240
|
+
wallet = nil
|
|
241
|
+
}
|
|
242
|
+
let device = data["device"]?.objectValue ?? JSONObject()
|
|
243
|
+
let walletIdValue = data["walletId"].flatMap { $0 == .null ? nil : $0 }
|
|
244
|
+
let deviceIdValue = data["deviceId"].flatMap { $0 == .null ? nil : $0 } ?? device["id"]
|
|
245
|
+
return Created(
|
|
246
|
+
wallet: wallet,
|
|
247
|
+
walletId: walletIdValue.map { Self.text($0) } ?? wallet?.id ?? "undefined",
|
|
248
|
+
deviceId: Self.text(deviceIdValue),
|
|
249
|
+
shareVersion: Self.int(data["shareVersion"]) ?? wallet?.shareVersion ?? 1
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
func confirmWallet(_ token: String, _ proof: DeviceProof) async throws {
|
|
254
|
+
_ = try ensureOk(try await call("POST", "/me/confirm", token, .object(JSONObject(proof.fields))))
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
func discardPending(_ token: String) async throws {
|
|
258
|
+
let res = try await call("DELETE", "/me/pending", token)
|
|
259
|
+
if res.status == 404 { return }
|
|
260
|
+
_ = try ensureOk(res)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
func challenge(_ token: String, deviceId: String) async throws -> String {
|
|
264
|
+
let data = try ensureOk(try await call("POST", "/me/share/challenge", token, .object(JSONObject([("deviceId", .string(deviceId))]))))
|
|
265
|
+
guard case .string(let c) = data["challenge"] ?? .null else { throw WalletError(.apiError) }
|
|
266
|
+
return c
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
func releaseShare(_ token: String, _ proof: DeviceProof) async throws -> String {
|
|
270
|
+
let data = try ensureOk(try await call("POST", "/me/share", token, .object(JSONObject(proof.fields))))
|
|
271
|
+
guard case .string(let s) = data["companyShare"] ?? .null else { throw WalletError(.apiError) }
|
|
272
|
+
return s
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
struct BackupKeyFields {
|
|
276
|
+
var credentialPublicKey: String?
|
|
277
|
+
var credentialAlg: Int?
|
|
278
|
+
var attestationObject: String?
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
func putBackup(_ token: String, proof: DeviceProof, backupCiphertext: String, credentialId: String, newCompanyShare: String, key: BackupKeyFields, upgrade: Bool) async throws -> Int {
|
|
282
|
+
var fields = proof.fields + [
|
|
283
|
+
("backupCiphertext", .string(backupCiphertext)), ("credentialId", .string(credentialId)),
|
|
284
|
+
("newCompanyShare", .string(newCompanyShare)),
|
|
285
|
+
]
|
|
286
|
+
if let pk = key.credentialPublicKey, let alg = key.credentialAlg {
|
|
287
|
+
fields += [("credentialPublicKey", .string(pk)), ("credentialAlg", .number(String(alg)))]
|
|
288
|
+
} else if let att = key.attestationObject {
|
|
289
|
+
fields += [("attestationObject", .string(att))]
|
|
290
|
+
}
|
|
291
|
+
if upgrade { fields.append(("upgrade", .bool(true))) }
|
|
292
|
+
fields.append(("protocol", .number(String(Self.frameProtocol))))
|
|
293
|
+
let data = try ensureOk(try await call("PUT", "/me/backup", token, .object(JSONObject(fields))))
|
|
294
|
+
guard let v = Self.int(data["shareVersion"]) else { throw WalletError(.apiError) }
|
|
295
|
+
return v
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
func restoreStart(_ token: String) async throws -> RestoreStart {
|
|
299
|
+
let data = try ensureOk(try await call("POST", "/me/restore/start", token, .object(JSONObject())))
|
|
300
|
+
let companyShare = Self.string(data["companyShare"])
|
|
301
|
+
let passkeyChallenge = Self.string(data["passkeyChallenge"])
|
|
302
|
+
if (companyShare ?? "").isEmpty && (passkeyChallenge ?? "").isEmpty { throw WalletError(.apiError) }
|
|
303
|
+
return RestoreStart(
|
|
304
|
+
backupCiphertext: Self.string(data["backupCiphertext"]),
|
|
305
|
+
credentialId: Self.string(data["credentialId"]),
|
|
306
|
+
companyShare: companyShare,
|
|
307
|
+
upgradeRecommended: !(companyShare ?? "").isEmpty || data["upgradeRecommended"] == .bool(true),
|
|
308
|
+
passkeyChallenge: passkeyChallenge
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
func restoreAssert(_ token: String, _ a: PasskeyAssertion, challenge: String) async throws -> String {
|
|
313
|
+
let body = JSONValue.object(JSONObject([
|
|
314
|
+
("credentialId", .string(a.credentialId)), ("clientDataJSON", .string(a.clientDataJSON)),
|
|
315
|
+
("authenticatorData", .string(a.authenticatorData)), ("signature", .string(a.signature)),
|
|
316
|
+
("challenge", .string(challenge)),
|
|
317
|
+
]))
|
|
318
|
+
let data = try ensureOk(try await call("POST", "/me/restore/assert", token, body))
|
|
319
|
+
guard case .string(let s) = data["companyShare"] ?? .null else { throw WalletError(.apiError) }
|
|
320
|
+
return s
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
func restore(_ token: String, newCompanyShare: String, newBackupCiphertext: String, newDevicePublicKey: DevicePublicJwk, credentialId: String, proof: String, bound: ProofNonce) async throws -> (deviceId: String, shareVersion: Int) {
|
|
324
|
+
let body = Self.bound([
|
|
325
|
+
("newCompanyShare", .string(newCompanyShare)), ("newBackupCiphertext", .string(newBackupCiphertext)),
|
|
326
|
+
("newDevicePublicKey", newDevicePublicKey.json), ("credentialId", .string(credentialId)), ("proof", .string(proof)),
|
|
327
|
+
], bound)
|
|
328
|
+
let data = try ensureOk(try await call("POST", "/me/restore", token, body))
|
|
329
|
+
let deviceId = data["deviceId"].flatMap { $0 == .null ? nil : $0 } ?? data["device"]?["id"]
|
|
330
|
+
guard let v = Self.int(data["shareVersion"]) else { throw WalletError(.apiError) }
|
|
331
|
+
return (Self.text(deviceId), v)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
func addDevice(_ token: String, devicePublicKey: DevicePublicJwk, proof: String, bound: ProofNonce) async throws -> (deviceId: String, shareVersion: Int) {
|
|
335
|
+
let body = Self.bound([("devicePublicKey", devicePublicKey.json), ("proof", .string(proof))], bound)
|
|
336
|
+
let data = try ensureOk(try await call("POST", "/me/devices", token, body))
|
|
337
|
+
guard let v = Self.int(data["shareVersion"]) else { throw WalletError(.apiError) }
|
|
338
|
+
return (Self.text(data["deviceId"]), v)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
func revokeDevice(_ token: String, deviceId: String, proof: DeviceProof) async throws {
|
|
342
|
+
_ = try ensureOk(try await call("DELETE", "/me/devices/\(Self.encode(deviceId))", token, .object(JSONObject(proof.fields))))
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/// encodeURIComponent
|
|
346
|
+
static func encode(_ s: String) -> String {
|
|
347
|
+
let allowed = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'()")
|
|
348
|
+
return s.addingPercentEncoding(withAllowedCharacters: allowed) ?? s
|
|
349
|
+
}
|
|
350
|
+
}
|