agentvault 1.0.0 → 1.0.2

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.
Files changed (293) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/README.md +1 -1
  3. package/dist/cli/commands/approve.js +5 -5
  4. package/dist/cli/commands/archive.js +5 -5
  5. package/dist/cli/commands/backup.js +5 -5
  6. package/dist/cli/commands/cloud-backup.js +12 -12
  7. package/dist/cli/commands/decrypt.js +2 -2
  8. package/dist/cli/commands/deploy.js +1 -1
  9. package/dist/cli/commands/exec.js +2 -2
  10. package/dist/cli/commands/fetch.js +4 -4
  11. package/dist/cli/commands/inference.js +5 -5
  12. package/dist/cli/commands/init.d.ts +1 -1
  13. package/dist/cli/commands/init.js +16 -16
  14. package/dist/cli/commands/list.js +4 -4
  15. package/dist/cli/commands/package.js +2 -2
  16. package/dist/cli/commands/profile.js +1 -1
  17. package/dist/cli/commands/rebuild.js +2 -2
  18. package/dist/cli/commands/show.js +1 -1
  19. package/dist/cli/commands/status.d.ts +1 -1
  20. package/dist/cli/commands/status.js +8 -8
  21. package/dist/cli/commands/trace.js +1 -1
  22. package/dist/cli/commands/wallet-export.js +1 -1
  23. package/dist/cli/commands/wallet-sign.js +1 -1
  24. package/dist/cli/commands/wallet.d.ts +1 -1
  25. package/dist/cli/commands/wallet.js +1 -1
  26. package/dist/cli/index.d.ts +2 -2
  27. package/dist/cli/index.js +3 -3
  28. package/dist/src/archival/archive-manager.d.ts +85 -0
  29. package/dist/src/archival/archive-manager.js +294 -0
  30. package/dist/src/archival/arweave-client.d.ts +88 -0
  31. package/dist/src/archival/arweave-client.js +223 -0
  32. package/dist/src/archival/index.d.ts +8 -0
  33. package/{src/archival/index.ts → dist/src/archival/index.js} +1 -1
  34. package/dist/src/backup/backup.d.ts +67 -0
  35. package/dist/src/backup/backup.js +231 -0
  36. package/dist/src/backup/index.d.ts +7 -0
  37. package/{src/backup/index.ts → dist/src/backup/index.js} +1 -1
  38. package/dist/src/cloud-storage/cloud-sync.d.ts +49 -0
  39. package/dist/src/cloud-storage/cloud-sync.js +372 -0
  40. package/dist/src/cloud-storage/index.d.ts +11 -0
  41. package/{src/cloud-storage/index.ts → dist/src/cloud-storage/index.js} +1 -1
  42. package/dist/src/cloud-storage/provider-detector.d.ts +34 -0
  43. package/dist/src/cloud-storage/provider-detector.js +158 -0
  44. package/{src/cloud-storage/types.ts → dist/src/cloud-storage/types.d.ts} +40 -53
  45. package/dist/src/cloud-storage/types.js +10 -0
  46. package/dist/src/debugging/index.d.ts +6 -0
  47. package/{src/debugging/index.ts → dist/src/debugging/index.js} +1 -1
  48. package/dist/src/debugging/logs.d.ts +32 -0
  49. package/dist/src/debugging/logs.js +158 -0
  50. package/dist/src/debugging/types.d.ts +91 -0
  51. package/dist/src/debugging/types.js +5 -0
  52. package/dist/src/deployment/deployer.d.ts +52 -0
  53. package/dist/src/deployment/deployer.js +211 -0
  54. package/dist/src/deployment/icpClient.d.ts +144 -0
  55. package/dist/src/deployment/icpClient.js +545 -0
  56. package/dist/src/deployment/index.d.ts +11 -0
  57. package/dist/src/deployment/index.js +14 -0
  58. package/dist/src/deployment/promotion.d.ts +32 -0
  59. package/dist/src/deployment/promotion.js +114 -0
  60. package/dist/src/deployment/types.d.ts +101 -0
  61. package/dist/src/deployment/types.js +5 -0
  62. package/dist/src/icp/batch.d.ts +112 -0
  63. package/dist/src/icp/batch.js +273 -0
  64. package/dist/src/icp/cycles.d.ts +29 -0
  65. package/{src/icp/cycles.ts → dist/src/icp/cycles.js} +8 -22
  66. package/dist/src/icp/environment.d.ts +60 -0
  67. package/dist/src/icp/environment.js +183 -0
  68. package/dist/src/icp/icpcli.d.ts +204 -0
  69. package/dist/src/icp/icpcli.js +374 -0
  70. package/dist/src/icp/icwasm.d.ts +94 -0
  71. package/dist/src/icp/icwasm.js +197 -0
  72. package/dist/src/icp/identity.d.ts +50 -0
  73. package/{src/icp/identity.ts → dist/src/icp/identity.js} +15 -28
  74. package/dist/src/icp/index.d.ts +16 -0
  75. package/dist/src/icp/index.js +20 -0
  76. package/dist/src/icp/optimization.d.ts +16 -0
  77. package/dist/src/icp/optimization.js +225 -0
  78. package/dist/src/icp/tokens.d.ts +24 -0
  79. package/{src/icp/tokens.ts → dist/src/icp/tokens.js} +5 -12
  80. package/dist/src/icp/tool-detector.d.ts +31 -0
  81. package/dist/src/icp/tool-detector.js +104 -0
  82. package/dist/src/icp/types.d.ts +493 -0
  83. package/dist/src/icp/types.js +7 -0
  84. package/dist/src/index.d.ts +12 -0
  85. package/dist/src/index.js +18 -0
  86. package/dist/src/inference/bittensor-client.d.ts +108 -0
  87. package/dist/src/inference/bittensor-client.js +224 -0
  88. package/dist/src/inference/index.d.ts +8 -0
  89. package/{src/inference/index.ts → dist/src/inference/index.js} +1 -1
  90. package/dist/src/inference/inference-manager.d.ts +76 -0
  91. package/dist/src/inference/inference-manager.js +228 -0
  92. package/dist/src/metrics/index.d.ts +7 -0
  93. package/{src/metrics/index.ts → dist/src/metrics/index.js} +1 -1
  94. package/dist/src/metrics/metrics.d.ts +39 -0
  95. package/dist/src/metrics/metrics.js +129 -0
  96. package/dist/src/monitoring/alerting.d.ts +51 -0
  97. package/dist/src/monitoring/alerting.js +169 -0
  98. package/dist/src/monitoring/health.d.ts +40 -0
  99. package/dist/src/monitoring/health.js +164 -0
  100. package/dist/src/monitoring/index.d.ts +10 -0
  101. package/dist/src/monitoring/index.js +12 -0
  102. package/dist/src/monitoring/info.d.ts +15 -0
  103. package/dist/src/monitoring/info.js +109 -0
  104. package/dist/src/monitoring/types.d.ts +93 -0
  105. package/dist/src/monitoring/types.js +7 -0
  106. package/dist/src/network/index.d.ts +5 -0
  107. package/{src/network/index.ts → dist/src/network/index.js} +1 -1
  108. package/dist/src/network/network-config.d.ts +31 -0
  109. package/dist/src/network/network-config.js +109 -0
  110. package/dist/src/packaging/compiler.d.ts +61 -0
  111. package/dist/src/packaging/compiler.js +562 -0
  112. package/dist/src/packaging/config-persistence.d.ts +46 -0
  113. package/dist/src/packaging/config-persistence.js +108 -0
  114. package/dist/src/packaging/config-schemas.d.ts +115 -0
  115. package/dist/src/packaging/config-schemas.js +43 -0
  116. package/dist/src/packaging/detector.d.ts +26 -0
  117. package/dist/src/packaging/detector.js +193 -0
  118. package/dist/src/packaging/index.d.ts +16 -0
  119. package/dist/src/packaging/index.js +22 -0
  120. package/dist/src/packaging/packager.d.ts +31 -0
  121. package/dist/src/packaging/packager.js +90 -0
  122. package/dist/src/packaging/parsers/clawdbot.d.ts +19 -0
  123. package/dist/src/packaging/parsers/clawdbot.js +231 -0
  124. package/dist/src/packaging/parsers/cline.d.ts +26 -0
  125. package/dist/src/packaging/parsers/cline.js +185 -0
  126. package/dist/src/packaging/parsers/generic.d.ts +27 -0
  127. package/dist/src/packaging/parsers/generic.js +228 -0
  128. package/dist/src/packaging/parsers/goose.d.ts +26 -0
  129. package/dist/src/packaging/parsers/goose.js +175 -0
  130. package/dist/src/packaging/parsers/index.d.ts +11 -0
  131. package/{src/packaging/parsers/index.ts → dist/src/packaging/parsers/index.js} +1 -1
  132. package/dist/src/packaging/serializer.d.ts +108 -0
  133. package/dist/src/packaging/serializer.js +153 -0
  134. package/dist/src/packaging/types.d.ts +131 -0
  135. package/dist/src/packaging/types.js +5 -0
  136. package/dist/src/packaging/wasmedge-compiler.d.ts +76 -0
  137. package/dist/src/packaging/wasmedge-compiler.js +349 -0
  138. package/dist/src/security/index.d.ts +11 -0
  139. package/{src/security/index.ts → dist/src/security/index.js} +1 -4
  140. package/dist/src/security/multisig.d.ts +102 -0
  141. package/dist/src/security/multisig.js +283 -0
  142. package/dist/src/security/types.d.ts +207 -0
  143. package/dist/src/security/types.js +217 -0
  144. package/dist/src/security/vetkeys.d.ts +179 -0
  145. package/dist/src/security/vetkeys.js +499 -0
  146. package/dist/src/testing/index.d.ts +6 -0
  147. package/{src/testing/index.ts → dist/src/testing/index.js} +1 -1
  148. package/dist/src/testing/local-runner.d.ts +23 -0
  149. package/dist/src/testing/local-runner.js +226 -0
  150. package/dist/src/testing/types.d.ts +98 -0
  151. package/dist/src/testing/types.js +5 -0
  152. package/dist/src/wallet/cbor-serializer.d.ts +82 -0
  153. package/dist/src/wallet/cbor-serializer.js +282 -0
  154. package/dist/src/wallet/chain-dispatcher.d.ts +112 -0
  155. package/dist/src/wallet/chain-dispatcher.js +241 -0
  156. package/dist/src/wallet/cross-chain-aggregator.d.ts +119 -0
  157. package/dist/src/wallet/cross-chain-aggregator.js +235 -0
  158. package/dist/src/wallet/index.d.ts +16 -0
  159. package/dist/src/wallet/index.js +22 -0
  160. package/dist/src/wallet/key-derivation.d.ts +117 -0
  161. package/dist/src/wallet/key-derivation.js +325 -0
  162. package/dist/src/wallet/providers/base-provider.d.ts +111 -0
  163. package/dist/src/wallet/providers/base-provider.js +58 -0
  164. package/dist/src/wallet/providers/cketh-provider.d.ts +104 -0
  165. package/dist/src/wallet/providers/cketh-provider.js +343 -0
  166. package/dist/src/wallet/providers/polkadot-provider.d.ts +115 -0
  167. package/dist/src/wallet/providers/polkadot-provider.js +407 -0
  168. package/dist/src/wallet/providers/solana-provider.d.ts +102 -0
  169. package/dist/src/wallet/providers/solana-provider.js +393 -0
  170. package/dist/src/wallet/transaction-queue.d.ts +133 -0
  171. package/dist/src/wallet/transaction-queue.js +195 -0
  172. package/dist/src/wallet/types.d.ts +167 -0
  173. package/dist/src/wallet/types.js +5 -0
  174. package/dist/src/wallet/vetkeys-adapter.d.ts +134 -0
  175. package/dist/src/wallet/vetkeys-adapter.js +313 -0
  176. package/dist/src/wallet/wallet-manager.d.ts +202 -0
  177. package/dist/src/wallet/wallet-manager.js +451 -0
  178. package/dist/src/wallet/wallet-storage.d.ts +131 -0
  179. package/dist/src/wallet/wallet-storage.js +274 -0
  180. package/macos-wallet-app/AgentVaultWallet/App/AgentVaultWalletApp.swift +54 -0
  181. package/macos-wallet-app/AgentVaultWallet/Models/AppState.swift +102 -0
  182. package/macos-wallet-app/AgentVaultWallet/Models/Chain.swift +121 -0
  183. package/macos-wallet-app/AgentVaultWallet/Models/Wallet.swift +98 -0
  184. package/macos-wallet-app/AgentVaultWallet/Resources/AgentVaultWallet.entitlements +27 -0
  185. package/macos-wallet-app/AgentVaultWallet/Resources/Info.plist +69 -0
  186. package/macos-wallet-app/AgentVaultWallet/Services/BackupService.swift +270 -0
  187. package/macos-wallet-app/AgentVaultWallet/Services/CLIBridge.swift +367 -0
  188. package/macos-wallet-app/AgentVaultWallet/Services/CryptoService.swift +157 -0
  189. package/macos-wallet-app/AgentVaultWallet/Services/FileService.swift +120 -0
  190. package/macos-wallet-app/AgentVaultWallet/Services/KeychainService.swift +219 -0
  191. package/macos-wallet-app/AgentVaultWallet/Utilities/Constants.swift +44 -0
  192. package/macos-wallet-app/AgentVaultWallet/Utilities/Extensions.swift +115 -0
  193. package/macos-wallet-app/AgentVaultWallet/ViewModels/BackupViewModel.swift +237 -0
  194. package/macos-wallet-app/AgentVaultWallet/ViewModels/CreateWalletViewModel.swift +137 -0
  195. package/macos-wallet-app/AgentVaultWallet/ViewModels/ImportWalletViewModel.swift +179 -0
  196. package/macos-wallet-app/AgentVaultWallet/ViewModels/WalletStore.swift +286 -0
  197. package/macos-wallet-app/AgentVaultWallet/Views/Backup/BackupView.swift +235 -0
  198. package/macos-wallet-app/AgentVaultWallet/Views/Backup/RestoreView.swift +316 -0
  199. package/macos-wallet-app/AgentVaultWallet/Views/Create/CreateWalletFlow.swift +438 -0
  200. package/macos-wallet-app/AgentVaultWallet/Views/Import/ImportWalletFlow.swift +399 -0
  201. package/macos-wallet-app/AgentVaultWallet/Views/MainView.swift +134 -0
  202. package/macos-wallet-app/AgentVaultWallet/Views/Settings/SettingsView.swift +276 -0
  203. package/macos-wallet-app/AgentVaultWallet/Views/Sidebar/SidebarView.swift +133 -0
  204. package/macos-wallet-app/AgentVaultWallet/Views/Wallet/DashboardView.swift +233 -0
  205. package/macos-wallet-app/AgentVaultWallet/Views/Wallet/WalletDetailView.swift +281 -0
  206. package/macos-wallet-app/AgentVaultWallet/Views/Wallet/WalletListView.swift +280 -0
  207. package/macos-wallet-app/AgentVaultWallet/Views/Welcome/WelcomeView.swift +176 -0
  208. package/macos-wallet-app/Makefile +47 -0
  209. package/macos-wallet-app/project.yml +40 -0
  210. package/macos-wallet-app/setup.sh +73 -0
  211. package/package.json +10 -2
  212. package/backups/agentvault-backup-test-agent-2026-02-12T17-54-28-967Z.json +0 -28
  213. package/backups/agentvault-backup-test-agent-2026-02-12T17-54-29-032Z.backup +0 -1
  214. package/backups/agentvault-backup-test-agent-2026-02-12T17-57-42-373Z.json +0 -28
  215. package/backups/agentvault-backup-test-agent-2026-02-12T17-57-42-428Z.backup +0 -1
  216. package/backups/agentvault-backup-test-agent-2026-02-12T18-52-25-132Z.json +0 -28
  217. package/backups/agentvault-backup-test-agent-2026-02-12T18-52-25-247Z.backup +0 -1
  218. package/backups/agentvault-backup-test-agent-2026-02-12T18-54-09-216Z.json +0 -28
  219. package/backups/agentvault-backup-test-agent-2026-02-12T18-54-09-283Z.backup +0 -1
  220. package/backups/agentvault-backup-test-agent-2026-02-12T22-18-22-772Z.backup +0 -1
  221. package/backups/agentvault-backup-test-agent-2026-02-12T22-18-22-793Z.json +0 -28
  222. package/backups/test-backup.json +0 -28
  223. package/scripts/dev-dashboard.mjs +0 -84
  224. package/site/README.md +0 -63
  225. package/site/docusaurus.config.ts +0 -148
  226. package/site/package-lock.json +0 -18383
  227. package/site/package.json +0 -47
  228. package/site/sidebars.ts +0 -86
  229. package/site/static/.gitkeep +0 -0
  230. package/site/static/img/logo.svg +0 -28
  231. package/site/static/img/og-image.svg +0 -35
  232. package/src/archival/archive-manager.ts +0 -372
  233. package/src/archival/arweave-client.ts +0 -289
  234. package/src/backup/backup.ts +0 -315
  235. package/src/cloud-storage/cloud-sync.ts +0 -461
  236. package/src/cloud-storage/provider-detector.ts +0 -198
  237. package/src/debugging/logs.ts +0 -193
  238. package/src/debugging/types.ts +0 -100
  239. package/src/deployment/deployer.ts +0 -274
  240. package/src/deployment/icpClient.ts +0 -620
  241. package/src/deployment/index.ts +0 -46
  242. package/src/deployment/promotion.ts +0 -161
  243. package/src/deployment/types.ts +0 -111
  244. package/src/icp/batch.ts +0 -374
  245. package/src/icp/environment.ts +0 -215
  246. package/src/icp/icpcli.ts +0 -438
  247. package/src/icp/icwasm.ts +0 -222
  248. package/src/icp/index.ts +0 -94
  249. package/src/icp/optimization.ts +0 -242
  250. package/src/icp/tool-detector.ts +0 -110
  251. package/src/icp/types.ts +0 -574
  252. package/src/index.ts +0 -25
  253. package/src/inference/bittensor-client.ts +0 -304
  254. package/src/inference/inference-manager.ts +0 -327
  255. package/src/metrics/metrics.ts +0 -186
  256. package/src/monitoring/alerting.ts +0 -190
  257. package/src/monitoring/health.ts +0 -197
  258. package/src/monitoring/index.ts +0 -38
  259. package/src/monitoring/info.ts +0 -114
  260. package/src/monitoring/types.ts +0 -99
  261. package/src/network/network-config.ts +0 -129
  262. package/src/packaging/compiler.ts +0 -647
  263. package/src/packaging/config-persistence.ts +0 -135
  264. package/src/packaging/config-schemas.ts +0 -156
  265. package/src/packaging/detector.ts +0 -220
  266. package/src/packaging/index.ts +0 -90
  267. package/src/packaging/packager.ts +0 -118
  268. package/src/packaging/parsers/clawdbot.ts +0 -278
  269. package/src/packaging/parsers/cline.ts +0 -223
  270. package/src/packaging/parsers/generic.ts +0 -266
  271. package/src/packaging/parsers/goose.ts +0 -214
  272. package/src/packaging/serializer.ts +0 -260
  273. package/src/packaging/types.ts +0 -144
  274. package/src/packaging/wasmedge-compiler.ts +0 -406
  275. package/src/security/multisig.ts +0 -415
  276. package/src/security/types.ts +0 -416
  277. package/src/security/vetkeys.ts +0 -655
  278. package/src/testing/local-runner.ts +0 -264
  279. package/src/testing/types.ts +0 -104
  280. package/src/wallet/cbor-serializer.ts +0 -323
  281. package/src/wallet/chain-dispatcher.ts +0 -313
  282. package/src/wallet/cross-chain-aggregator.ts +0 -346
  283. package/src/wallet/index.ts +0 -76
  284. package/src/wallet/key-derivation.ts +0 -425
  285. package/src/wallet/providers/base-provider.ts +0 -154
  286. package/src/wallet/providers/cketh-provider.ts +0 -434
  287. package/src/wallet/providers/polkadot-provider.ts +0 -503
  288. package/src/wallet/providers/solana-provider.ts +0 -490
  289. package/src/wallet/transaction-queue.ts +0 -284
  290. package/src/wallet/types.ts +0 -178
  291. package/src/wallet/vetkeys-adapter.ts +0 -431
  292. package/src/wallet/wallet-manager.ts +0 -597
  293. package/src/wallet/wallet-storage.ts +0 -380
@@ -0,0 +1,281 @@
1
+ import SwiftUI
2
+
3
+ /// Detailed view for a single wallet
4
+ struct WalletDetailView: View {
5
+ let wallet: Wallet
6
+ @EnvironmentObject var walletStore: WalletStore
7
+ @EnvironmentObject var appState: AppState
8
+ @State private var isEditingName = false
9
+ @State private var editedName: String = ""
10
+ @State private var showDeleteConfirmation = false
11
+ @State private var isRefreshing = false
12
+
13
+ var body: some View {
14
+ ScrollView {
15
+ VStack(spacing: 24) {
16
+ // Header card
17
+ walletHeader
18
+
19
+ // Address section
20
+ addressSection
21
+
22
+ // Balance section
23
+ balanceSection
24
+
25
+ // Details section
26
+ detailsSection
27
+
28
+ // Actions section
29
+ actionsSection
30
+ }
31
+ .padding(24)
32
+ }
33
+ .navigationTitle(wallet.name)
34
+ .confirmationDialog("Delete Wallet", isPresented: $showDeleteConfirmation) {
35
+ Button("Delete \"\(wallet.name)\"", role: .destructive) {
36
+ walletStore.deleteWallet(wallet)
37
+ appState.selectedDestination = .walletList
38
+ }
39
+ } message: {
40
+ Text("This will permanently delete the wallet and remove its keys from your Keychain. Make sure you have a backup of your recovery phrase.")
41
+ }
42
+ }
43
+
44
+ private var walletHeader: some View {
45
+ HStack(spacing: 20) {
46
+ // Chain badge
47
+ ZStack {
48
+ RoundedRectangle(cornerRadius: 16)
49
+ .fill(
50
+ LinearGradient(colors: wallet.chain.gradientColors,
51
+ startPoint: .topLeading, endPoint: .bottomTrailing)
52
+ )
53
+ .frame(width: 64, height: 64)
54
+
55
+ Image(systemName: wallet.chain.iconName)
56
+ .font(.system(size: 28))
57
+ .foregroundStyle(.white)
58
+ }
59
+
60
+ VStack(alignment: .leading, spacing: 6) {
61
+ if isEditingName {
62
+ HStack {
63
+ TextField("Wallet name", text: $editedName)
64
+ .textFieldStyle(.roundedBorder)
65
+ .onSubmit {
66
+ walletStore.renameWallet(wallet, to: editedName)
67
+ isEditingName = false
68
+ }
69
+ Button("Save") {
70
+ walletStore.renameWallet(wallet, to: editedName)
71
+ isEditingName = false
72
+ }
73
+ .buttonStyle(.bordered)
74
+ .controlSize(.small)
75
+ }
76
+ } else {
77
+ HStack {
78
+ Text(wallet.name)
79
+ .font(.title2.bold())
80
+
81
+ Button {
82
+ editedName = wallet.name
83
+ isEditingName = true
84
+ } label: {
85
+ Image(systemName: "pencil")
86
+ .font(.caption)
87
+ }
88
+ .buttonStyle(.plain)
89
+ .foregroundStyle(.secondary)
90
+ }
91
+ }
92
+
93
+ HStack(spacing: 8) {
94
+ Text(wallet.chain.displayName)
95
+ .font(.subheadline)
96
+ .foregroundStyle(.secondary)
97
+
98
+ if wallet.isImported {
99
+ Text("IMPORTED")
100
+ .font(.caption2.bold())
101
+ .foregroundStyle(.secondary)
102
+ .padding(.horizontal, 6)
103
+ .padding(.vertical, 2)
104
+ .background(.secondary.opacity(0.15), in: Capsule())
105
+ }
106
+ }
107
+ }
108
+
109
+ Spacer()
110
+ }
111
+ .padding(20)
112
+ .background(.quaternary.opacity(0.5), in: RoundedRectangle(cornerRadius: 16))
113
+ }
114
+
115
+ private var addressSection: some View {
116
+ VStack(alignment: .leading, spacing: 12) {
117
+ Text("Address")
118
+ .font(.headline)
119
+
120
+ HStack {
121
+ Text(wallet.address)
122
+ .font(.system(.body, design: .monospaced))
123
+ .lineLimit(2)
124
+ .textSelection(.enabled)
125
+
126
+ Spacer()
127
+
128
+ VStack(spacing: 6) {
129
+ Button {
130
+ NSPasteboard.general.clearContents()
131
+ NSPasteboard.general.setString(wallet.address, forType: .string)
132
+ } label: {
133
+ Image(systemName: "doc.on.doc")
134
+ }
135
+ .buttonStyle(.bordered)
136
+ .controlSize(.small)
137
+ .help("Copy address to clipboard")
138
+
139
+ if let url = wallet.explorerURL {
140
+ Button {
141
+ NSWorkspace.shared.open(url)
142
+ } label: {
143
+ Image(systemName: "arrow.up.right.square")
144
+ }
145
+ .buttonStyle(.bordered)
146
+ .controlSize(.small)
147
+ .help("View on block explorer")
148
+ }
149
+ }
150
+ }
151
+ .padding(16)
152
+ .background(.background, in: RoundedRectangle(cornerRadius: 12))
153
+ .overlay(RoundedRectangle(cornerRadius: 12).stroke(.secondary.opacity(0.15)))
154
+ }
155
+ }
156
+
157
+ private var balanceSection: some View {
158
+ VStack(alignment: .leading, spacing: 12) {
159
+ HStack {
160
+ Text("Balance")
161
+ .font(.headline)
162
+
163
+ Spacer()
164
+
165
+ Button {
166
+ isRefreshing = true
167
+ Task {
168
+ await walletStore.refreshBalance(for: wallet)
169
+ isRefreshing = false
170
+ }
171
+ } label: {
172
+ Label("Refresh", systemImage: "arrow.clockwise")
173
+ }
174
+ .buttonStyle(.bordered)
175
+ .controlSize(.small)
176
+ .disabled(isRefreshing)
177
+ }
178
+
179
+ HStack(alignment: .firstTextBaseline) {
180
+ if isRefreshing {
181
+ ProgressView()
182
+ .controlSize(.small)
183
+ } else {
184
+ Text(wallet.cachedBalance ?? "—")
185
+ .font(.system(size: 36, weight: .semibold, design: .rounded).monospacedDigit())
186
+ }
187
+
188
+ Text(wallet.chain.symbol)
189
+ .font(.title2)
190
+ .foregroundStyle(.secondary)
191
+
192
+ Spacer()
193
+
194
+ if let updated = wallet.balanceLastUpdated {
195
+ Text("Updated \(updated, style: .relative) ago")
196
+ .font(.caption)
197
+ .foregroundStyle(.tertiary)
198
+ }
199
+ }
200
+ .padding(16)
201
+ .background(.background, in: RoundedRectangle(cornerRadius: 12))
202
+ .overlay(RoundedRectangle(cornerRadius: 12).stroke(.secondary.opacity(0.15)))
203
+ }
204
+ }
205
+
206
+ private var detailsSection: some View {
207
+ VStack(alignment: .leading, spacing: 12) {
208
+ Text("Details")
209
+ .font(.headline)
210
+
211
+ VStack(spacing: 0) {
212
+ DetailRow(label: "Network", value: wallet.chain.displayName)
213
+ Divider().padding(.leading, 16)
214
+ DetailRow(label: "Symbol", value: wallet.chain.symbol)
215
+ Divider().padding(.leading, 16)
216
+ if !wallet.derivationPath.isEmpty {
217
+ DetailRow(label: "Derivation Path", value: wallet.derivationPath)
218
+ Divider().padding(.leading, 16)
219
+ }
220
+ DetailRow(label: "Created", value: wallet.createdAt.formatted(date: .long, time: .shortened))
221
+ Divider().padding(.leading, 16)
222
+ DetailRow(label: "Recovery Phrase", value: wallet.hasMnemonicBackup ? "Backed up" : "Not available")
223
+ }
224
+ .background(.background, in: RoundedRectangle(cornerRadius: 12))
225
+ .overlay(RoundedRectangle(cornerRadius: 12).stroke(.secondary.opacity(0.15)))
226
+ }
227
+ }
228
+
229
+ private var actionsSection: some View {
230
+ VStack(alignment: .leading, spacing: 12) {
231
+ Text("Actions")
232
+ .font(.headline)
233
+
234
+ HStack(spacing: 12) {
235
+ if walletStore.hasMnemonic(for: wallet) {
236
+ Button {
237
+ appState.activeSheet = .mnemonicReveal(wallet.id)
238
+ } label: {
239
+ Label("Reveal Recovery Phrase", systemImage: "eye")
240
+ .frame(maxWidth: .infinity)
241
+ }
242
+ .buttonStyle(.bordered)
243
+ }
244
+
245
+ Button {
246
+ appState.activeSheet = .backup
247
+ } label: {
248
+ Label("Backup", systemImage: "arrow.down.doc")
249
+ .frame(maxWidth: .infinity)
250
+ }
251
+ .buttonStyle(.bordered)
252
+ }
253
+
254
+ Button(role: .destructive) {
255
+ showDeleteConfirmation = true
256
+ } label: {
257
+ Label("Delete Wallet", systemImage: "trash")
258
+ .frame(maxWidth: .infinity)
259
+ }
260
+ .buttonStyle(.bordered)
261
+ .tint(.red)
262
+ }
263
+ }
264
+ }
265
+
266
+ struct DetailRow: View {
267
+ let label: String
268
+ let value: String
269
+
270
+ var body: some View {
271
+ HStack {
272
+ Text(label)
273
+ .foregroundStyle(.secondary)
274
+ Spacer()
275
+ Text(value)
276
+ .font(.callout)
277
+ }
278
+ .padding(.horizontal, 16)
279
+ .padding(.vertical, 10)
280
+ }
281
+ }
@@ -0,0 +1,280 @@
1
+ import SwiftUI
2
+
3
+ /// Shows all wallets grouped by chain with search and filtering
4
+ struct WalletListView: View {
5
+ @EnvironmentObject var walletStore: WalletStore
6
+ @EnvironmentObject var appState: AppState
7
+ @State private var searchText = ""
8
+ @State private var filterChain: Chain?
9
+ @State private var sortOrder: WalletSortOrder = .name
10
+ @State private var showDeleteConfirmation: Wallet?
11
+
12
+ private var filteredWallets: [Wallet] {
13
+ var results = walletStore.wallets
14
+
15
+ // Filter by chain
16
+ if let chain = filterChain {
17
+ results = results.filter { $0.chain == chain }
18
+ }
19
+
20
+ // Filter by search text
21
+ if !searchText.isEmpty {
22
+ results = results.filter {
23
+ $0.name.localizedCaseInsensitiveContains(searchText) ||
24
+ $0.address.localizedCaseInsensitiveContains(searchText) ||
25
+ $0.chain.displayName.localizedCaseInsensitiveContains(searchText)
26
+ }
27
+ }
28
+
29
+ // Sort
30
+ switch sortOrder {
31
+ case .name:
32
+ results.sort { $0.name.localizedCompare($1.name) == .orderedAscending }
33
+ case .chain:
34
+ results.sort { $0.chain.rawValue < $1.chain.rawValue }
35
+ case .created:
36
+ results.sort { $0.createdAt > $1.createdAt }
37
+ }
38
+
39
+ return results
40
+ }
41
+
42
+ var body: some View {
43
+ VStack(spacing: 0) {
44
+ // Toolbar
45
+ toolbar
46
+
47
+ if walletStore.wallets.isEmpty {
48
+ emptyState
49
+ } else if filteredWallets.isEmpty {
50
+ noResultsState
51
+ } else {
52
+ ScrollView {
53
+ LazyVStack(spacing: 12) {
54
+ ForEach(filteredWallets) { wallet in
55
+ WalletRowView(wallet: wallet)
56
+ .onTapGesture {
57
+ appState.selectedDestination = .walletDetail(wallet.id)
58
+ }
59
+ .contextMenu {
60
+ walletContextMenu(wallet)
61
+ }
62
+ }
63
+ }
64
+ .padding(20)
65
+ }
66
+ }
67
+ }
68
+ .navigationTitle("All Wallets")
69
+ .confirmationDialog(
70
+ "Delete Wallet",
71
+ isPresented: Binding(
72
+ get: { showDeleteConfirmation != nil },
73
+ set: { if !$0 { showDeleteConfirmation = nil } }
74
+ ),
75
+ presenting: showDeleteConfirmation
76
+ ) { wallet in
77
+ Button("Delete \"\(wallet.name)\"", role: .destructive) {
78
+ walletStore.deleteWallet(wallet)
79
+ }
80
+ } message: { wallet in
81
+ Text("This will permanently delete the wallet and its keys from your Keychain. This cannot be undone unless you have a backup.")
82
+ }
83
+ }
84
+
85
+ private var toolbar: some View {
86
+ HStack(spacing: 12) {
87
+ // Search
88
+ HStack {
89
+ Image(systemName: "magnifyingglass")
90
+ .foregroundStyle(.secondary)
91
+ TextField("Search wallets...", text: $searchText)
92
+ .textFieldStyle(.plain)
93
+ if !searchText.isEmpty {
94
+ Button { searchText = "" } label: {
95
+ Image(systemName: "xmark.circle.fill")
96
+ .foregroundStyle(.secondary)
97
+ }
98
+ .buttonStyle(.plain)
99
+ }
100
+ }
101
+ .padding(8)
102
+ .background(.quaternary, in: RoundedRectangle(cornerRadius: 8))
103
+
104
+ // Chain filter
105
+ Picker("Network", selection: $filterChain) {
106
+ Text("All Networks").tag(Chain?.none)
107
+ Divider()
108
+ ForEach(Chain.allCases) { chain in
109
+ Label(chain.displayName, systemImage: chain.iconName)
110
+ .tag(Chain?.some(chain))
111
+ }
112
+ }
113
+ .frame(width: 160)
114
+
115
+ // Sort
116
+ Picker("Sort", selection: $sortOrder) {
117
+ ForEach(WalletSortOrder.allCases) { order in
118
+ Text(order.label).tag(order)
119
+ }
120
+ }
121
+ .frame(width: 120)
122
+
123
+ Spacer()
124
+
125
+ // Refresh all balances
126
+ Button {
127
+ Task { await walletStore.refreshAllBalances() }
128
+ } label: {
129
+ Image(systemName: "arrow.clockwise")
130
+ }
131
+ .buttonStyle(.bordered)
132
+ .controlSize(.small)
133
+ .disabled(walletStore.isLoading)
134
+ }
135
+ .padding(.horizontal, 20)
136
+ .padding(.vertical, 12)
137
+ .background(.bar)
138
+ }
139
+
140
+ private var emptyState: some View {
141
+ ContentUnavailableView {
142
+ Label("No Wallets Yet", systemImage: "wallet.pass")
143
+ } description: {
144
+ Text("Create or import a wallet to get started.")
145
+ } actions: {
146
+ HStack(spacing: 12) {
147
+ Button("Create Wallet") {
148
+ appState.activeSheet = .createWallet
149
+ }
150
+ .buttonStyle(.borderedProminent)
151
+
152
+ Button("Import Wallet") {
153
+ appState.activeSheet = .importWallet
154
+ }
155
+ .buttonStyle(.bordered)
156
+ }
157
+ }
158
+ }
159
+
160
+ private var noResultsState: some View {
161
+ ContentUnavailableView.search(text: searchText)
162
+ }
163
+
164
+ @ViewBuilder
165
+ private func walletContextMenu(_ wallet: Wallet) -> some View {
166
+ Button("Copy Address") {
167
+ NSPasteboard.general.clearContents()
168
+ NSPasteboard.general.setString(wallet.address, forType: .string)
169
+ }
170
+
171
+ if let url = wallet.explorerURL {
172
+ Button("View on Explorer") {
173
+ NSWorkspace.shared.open(url)
174
+ }
175
+ }
176
+
177
+ if walletStore.hasMnemonic(for: wallet) {
178
+ Button("Reveal Recovery Phrase") {
179
+ appState.activeSheet = .mnemonicReveal(wallet.id)
180
+ }
181
+ }
182
+
183
+ Divider()
184
+
185
+ Button("Refresh Balance") {
186
+ Task { await walletStore.refreshBalance(for: wallet) }
187
+ }
188
+
189
+ Divider()
190
+
191
+ Button("Delete Wallet", role: .destructive) {
192
+ showDeleteConfirmation = wallet
193
+ }
194
+ }
195
+ }
196
+
197
+ /// Wallet card row for the list view
198
+ struct WalletRowView: View {
199
+ let wallet: Wallet
200
+ @EnvironmentObject var walletStore: WalletStore
201
+
202
+ var body: some View {
203
+ HStack(spacing: 16) {
204
+ // Chain icon
205
+ ZStack {
206
+ RoundedRectangle(cornerRadius: 12)
207
+ .fill(
208
+ LinearGradient(colors: wallet.chain.gradientColors,
209
+ startPoint: .topLeading, endPoint: .bottomTrailing)
210
+ )
211
+ .frame(width: 44, height: 44)
212
+
213
+ Image(systemName: wallet.chain.iconName)
214
+ .font(.title3)
215
+ .foregroundStyle(.white)
216
+ }
217
+
218
+ // Wallet info
219
+ VStack(alignment: .leading, spacing: 4) {
220
+ HStack {
221
+ Text(wallet.name)
222
+ .font(.headline)
223
+
224
+ if wallet.isImported {
225
+ Text("IMPORTED")
226
+ .font(.caption2.bold())
227
+ .foregroundStyle(.secondary)
228
+ .padding(.horizontal, 6)
229
+ .padding(.vertical, 2)
230
+ .background(.secondary.opacity(0.15), in: Capsule())
231
+ }
232
+ }
233
+
234
+ Text(wallet.shortAddress)
235
+ .font(.system(.caption, design: .monospaced))
236
+ .foregroundStyle(.secondary)
237
+ }
238
+
239
+ Spacer()
240
+
241
+ // Balance
242
+ VStack(alignment: .trailing, spacing: 4) {
243
+ Text(wallet.displayBalance)
244
+ .font(.headline.monospacedDigit())
245
+
246
+ if let updated = wallet.balanceLastUpdated {
247
+ Text(updated, style: .relative)
248
+ .font(.caption2)
249
+ .foregroundStyle(.tertiary)
250
+ }
251
+ }
252
+
253
+ // Arrow
254
+ Image(systemName: "chevron.right")
255
+ .font(.caption)
256
+ .foregroundStyle(.tertiary)
257
+ }
258
+ .padding(16)
259
+ .background(.background, in: RoundedRectangle(cornerRadius: 12))
260
+ .overlay(
261
+ RoundedRectangle(cornerRadius: 12)
262
+ .stroke(Color.secondary.opacity(0.1))
263
+ )
264
+ .shadow(color: .black.opacity(0.03), radius: 4, y: 2)
265
+ }
266
+ }
267
+
268
+ enum WalletSortOrder: String, CaseIterable, Identifiable {
269
+ case name, chain, created
270
+
271
+ var id: String { rawValue }
272
+
273
+ var label: String {
274
+ switch self {
275
+ case .name: return "Name"
276
+ case .chain: return "Network"
277
+ case .created: return "Date Created"
278
+ }
279
+ }
280
+ }