-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathTrezorKnownDeviceStorage.swift
More file actions
103 lines (93 loc) · 3.82 KB
/
Copy pathTrezorKnownDeviceStorage.swift
File metadata and controls
103 lines (93 loc) · 3.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import Foundation
/// Represents a previously connected Trezor device
struct TrezorKnownDevice: Codable, Identifiable {
let id: String
let name: String
let path: String
let transportType: String
var label: String?
var model: String?
var lastConnectedAt: Date
/// Account-level extended public keys keyed by `AddressScriptType.stringValue`.
/// Persisted so watch-only balances/activity stay available while disconnected.
var xpubs: [String: String]
/// User-set name applied while managing the wallet in Bitkit; nil until renamed. Takes priority
/// over the device's own `label`/`model` when resolving the display name.
var customLabel: String?
init(
id: String,
name: String,
path: String,
transportType: String,
label: String? = nil,
model: String? = nil,
lastConnectedAt: Date,
xpubs: [String: String] = [:],
customLabel: String? = nil
) {
self.id = id
self.name = name
self.path = path
self.transportType = transportType
self.label = label
self.model = model
self.lastConnectedAt = lastConnectedAt
self.xpubs = xpubs
self.customLabel = customLabel
}
init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
path = try container.decode(String.self, forKey: .path)
transportType = try container.decode(String.self, forKey: .transportType)
label = try container.decodeIfPresent(String.self, forKey: .label)
model = try container.decodeIfPresent(String.self, forKey: .model)
lastConnectedAt = try container.decode(Date.self, forKey: .lastConnectedAt)
xpubs = try container.decodeIfPresent([String: String].self, forKey: .xpubs) ?? [:]
customLabel = try container.decodeIfPresent(String.self, forKey: .customLabel)
}
}
/// Persists known Trezor device metadata in UserDefaults
/// THP credentials remain in Keychain via TrezorCredentialStorage
enum TrezorKnownDeviceStorage {
private static let key = "trezor.knownDevices"
/// Load all known devices, sorted by most recently connected
static func loadAll() -> [TrezorKnownDevice] {
guard let data = UserDefaults.standard.data(forKey: key) else { return [] }
let devices = (try? JSONDecoder().decode([TrezorKnownDevice].self, from: data)) ?? []
return devices.sorted { $0.lastConnectedAt > $1.lastConnectedAt }
}
/// Save or update a known device
static func save(_ device: TrezorKnownDevice) {
var devices = loadAll()
devices.removeAll { $0.id == device.id }
devices.insert(device, at: 0)
if let data = try? JSONEncoder().encode(devices) {
UserDefaults.standard.set(data, forKey: key)
}
}
/// Persist the full device list as-is. Used for bulk updates (e.g. renaming every entry of a
/// device shared across transports) without per-device reordering.
static func saveAll(_ devices: [TrezorKnownDevice]) {
if let data = try? JSONEncoder().encode(devices) {
UserDefaults.standard.set(data, forKey: key)
}
}
/// Remove a known device by ID
static func remove(id: String) {
var devices = loadAll()
devices.removeAll { $0.id == id }
if let data = try? JSONEncoder().encode(devices) {
UserDefaults.standard.set(data, forKey: key)
}
}
/// Remove all remembered Trezor devices.
static func removeAll() {
UserDefaults.standard.removeObject(forKey: key)
}
/// Check if a device is known
static func isKnown(id: String) -> Bool {
loadAll().contains { $0.id == id }
}
}