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,137 @@
1
+ import SwiftUI
2
+
3
+ /// Manages the multi-step wallet creation flow
4
+ @MainActor
5
+ final class CreateWalletViewModel: ObservableObject {
6
+
7
+ enum Step: Int, CaseIterable {
8
+ case selectChain = 0
9
+ case generating = 1
10
+ case showMnemonic = 2
11
+ case confirmMnemonic = 3
12
+ case nameWallet = 4
13
+ case complete = 5
14
+
15
+ var title: String {
16
+ switch self {
17
+ case .selectChain: return "Select Network"
18
+ case .generating: return "Generating Wallet"
19
+ case .showMnemonic: return "Recovery Phrase"
20
+ case .confirmMnemonic: return "Confirm Backup"
21
+ case .nameWallet: return "Name Your Wallet"
22
+ case .complete: return "Wallet Created"
23
+ }
24
+ }
25
+ }
26
+
27
+ @Published var currentStep: Step = .selectChain
28
+ @Published var selectedChain: Chain = .ethereum
29
+ @Published var walletName: String = ""
30
+ @Published var mnemonic: String = ""
31
+ @Published var mnemonicWords: [String] = []
32
+ @Published var confirmationInput: String = ""
33
+ @Published var confirmWordIndices: [Int] = [] // Which words to verify
34
+ @Published var createdWallet: Wallet?
35
+ @Published var isProcessing: Bool = false
36
+ @Published var errorMessage: String?
37
+
38
+ /// Words the user needs to confirm (randomly selected from the mnemonic)
39
+ var wordsToConfirm: [(index: Int, word: String)] {
40
+ confirmWordIndices.compactMap { idx in
41
+ guard idx < mnemonicWords.count else { return nil }
42
+ return (index: idx, word: mnemonicWords[idx])
43
+ }
44
+ }
45
+
46
+ /// Whether the user can proceed from the current step
47
+ var canProceed: Bool {
48
+ switch currentStep {
49
+ case .selectChain: return true
50
+ case .generating: return false
51
+ case .showMnemonic: return true
52
+ case .confirmMnemonic: return isConfirmationValid
53
+ case .nameWallet: return !walletName.trimmingCharacters(in: .whitespaces).isEmpty
54
+ case .complete: return true
55
+ }
56
+ }
57
+
58
+ /// Validate mnemonic confirmation
59
+ var isConfirmationValid: Bool {
60
+ let inputWords = confirmationInput
61
+ .trimmingCharacters(in: .whitespacesAndNewlines)
62
+ .lowercased()
63
+ .components(separatedBy: .whitespaces)
64
+ .filter { !$0.isEmpty }
65
+
66
+ guard inputWords.count == confirmWordIndices.count else { return false }
67
+
68
+ for (i, idx) in confirmWordIndices.enumerated() {
69
+ guard i < inputWords.count, idx < mnemonicWords.count else { return false }
70
+ if inputWords[i] != mnemonicWords[idx].lowercased() { return false }
71
+ }
72
+ return true
73
+ }
74
+
75
+ func goToNext() {
76
+ guard let nextStep = Step(rawValue: currentStep.rawValue + 1) else { return }
77
+ withAnimation(.easeInOut(duration: 0.3)) {
78
+ currentStep = nextStep
79
+ }
80
+ }
81
+
82
+ func goBack() {
83
+ guard let prevStep = Step(rawValue: currentStep.rawValue - 1) else { return }
84
+ withAnimation(.easeInOut(duration: 0.3)) {
85
+ currentStep = prevStep
86
+ }
87
+ }
88
+
89
+ /// Generate a new wallet using the CLI bridge
90
+ func generateWallet(store: WalletStore) async {
91
+ isProcessing = true
92
+ errorMessage = nil
93
+ currentStep = .generating
94
+
95
+ do {
96
+ let name = walletName.isEmpty ? "\(selectedChain.displayName) Wallet" : walletName
97
+ let result = try await store.createWallet(chain: selectedChain, name: name)
98
+
99
+ createdWallet = result.wallet
100
+ if let m = result.mnemonic {
101
+ mnemonic = m
102
+ mnemonicWords = m.components(separatedBy: " ")
103
+ selectRandomConfirmationWords()
104
+ }
105
+
106
+ isProcessing = false
107
+ currentStep = .showMnemonic
108
+ } catch {
109
+ isProcessing = false
110
+ errorMessage = error.localizedDescription
111
+ currentStep = .selectChain
112
+ }
113
+ }
114
+
115
+ /// Select 3 random word indices for confirmation
116
+ private func selectRandomConfirmationWords() {
117
+ guard mnemonicWords.count >= 3 else { return }
118
+ var indices = Set<Int>()
119
+ while indices.count < 3 {
120
+ indices.insert(Int.random(in: 0..<mnemonicWords.count))
121
+ }
122
+ confirmWordIndices = indices.sorted()
123
+ }
124
+
125
+ func reset() {
126
+ currentStep = .selectChain
127
+ selectedChain = .ethereum
128
+ walletName = ""
129
+ mnemonic = ""
130
+ mnemonicWords = []
131
+ confirmationInput = ""
132
+ confirmWordIndices = []
133
+ createdWallet = nil
134
+ isProcessing = false
135
+ errorMessage = nil
136
+ }
137
+ }
@@ -0,0 +1,179 @@
1
+ import SwiftUI
2
+ import UniformTypeIdentifiers
3
+
4
+ /// Manages the multi-step wallet import flow
5
+ @MainActor
6
+ final class ImportWalletViewModel: ObservableObject {
7
+
8
+ enum Step: Int, CaseIterable {
9
+ case selectChain = 0
10
+ case selectMethod = 1
11
+ case enterInput = 2
12
+ case nameWallet = 3
13
+ case importing = 4
14
+ case complete = 5
15
+
16
+ var title: String {
17
+ switch self {
18
+ case .selectChain: return "Select Network"
19
+ case .selectMethod: return "Import Method"
20
+ case .enterInput: return "Enter Details"
21
+ case .nameWallet: return "Name Your Wallet"
22
+ case .importing: return "Importing"
23
+ case .complete: return "Import Complete"
24
+ }
25
+ }
26
+ }
27
+
28
+ @Published var currentStep: Step = .selectChain
29
+ @Published var selectedChain: Chain = .ethereum
30
+ @Published var selectedMethod: ImportMethod = .mnemonic
31
+ @Published var walletName: String = ""
32
+ @Published var inputText: String = "" // Mnemonic or private key
33
+ @Published var passwordInput: String = "" // For keystore files
34
+ @Published var selectedFileURL: URL? // For file-based imports
35
+ @Published var importedWallet: Wallet?
36
+ @Published var isProcessing: Bool = false
37
+ @Published var errorMessage: String?
38
+ @Published var showFileImporter: Bool = false
39
+
40
+ /// Available import methods for the selected chain
41
+ var availableMethods: [ImportMethod] {
42
+ selectedChain.supportedImportMethods
43
+ }
44
+
45
+ /// Whether the current input is valid enough to proceed
46
+ var isInputValid: Bool {
47
+ switch selectedMethod {
48
+ case .mnemonic:
49
+ return CryptoService.shared.isValidMnemonic(inputText)
50
+ case .privateKey:
51
+ let cleaned = inputText.trimmingCharacters(in: .whitespacesAndNewlines)
52
+ if selectedChain == .ethereum {
53
+ return CryptoService.shared.isValidEthPrivateKey(cleaned)
54
+ }
55
+ return cleaned.count >= 32
56
+ case .pemFile, .jwkFile, .keystoreJSON:
57
+ return selectedFileURL != nil
58
+ }
59
+ }
60
+
61
+ var canProceed: Bool {
62
+ switch currentStep {
63
+ case .selectChain: return true
64
+ case .selectMethod: return true
65
+ case .enterInput: return isInputValid
66
+ case .nameWallet: return !walletName.trimmingCharacters(in: .whitespaces).isEmpty
67
+ case .importing: return false
68
+ case .complete: return true
69
+ }
70
+ }
71
+
72
+ /// File types accepted for the current import method
73
+ var allowedFileTypes: [UTType] {
74
+ switch selectedMethod {
75
+ case .jwkFile: return [.json]
76
+ case .pemFile: return [UTType(filenameExtension: "pem") ?? .data]
77
+ case .keystoreJSON: return [.json]
78
+ default: return []
79
+ }
80
+ }
81
+
82
+ /// Placeholder text for the input field
83
+ var inputPlaceholder: String {
84
+ switch selectedMethod {
85
+ case .mnemonic: return "Enter your 12 or 24-word recovery phrase, separated by spaces..."
86
+ case .privateKey:
87
+ if selectedChain == .ethereum { return "Enter your hex-encoded private key (0x...)" }
88
+ return "Enter your private key..."
89
+ default: return ""
90
+ }
91
+ }
92
+
93
+ func goToNext() {
94
+ guard let nextStep = Step(rawValue: currentStep.rawValue + 1) else { return }
95
+ withAnimation(.easeInOut(duration: 0.3)) {
96
+ currentStep = nextStep
97
+ }
98
+ }
99
+
100
+ func goBack() {
101
+ guard let prevStep = Step(rawValue: currentStep.rawValue - 1) else { return }
102
+ withAnimation(.easeInOut(duration: 0.3)) {
103
+ currentStep = prevStep
104
+ }
105
+ }
106
+
107
+ /// Execute the import
108
+ func importWallet(store: WalletStore) async {
109
+ isProcessing = true
110
+ errorMessage = nil
111
+ currentStep = .importing
112
+
113
+ do {
114
+ let name = walletName.isEmpty ? "\(selectedChain.displayName) Import" : walletName
115
+
116
+ switch selectedMethod {
117
+ case .mnemonic:
118
+ importedWallet = try await store.importFromMnemonic(
119
+ chain: selectedChain,
120
+ mnemonic: inputText.trimmingCharacters(in: .whitespacesAndNewlines),
121
+ name: name
122
+ )
123
+
124
+ case .privateKey:
125
+ importedWallet = try await store.importFromPrivateKey(
126
+ chain: selectedChain,
127
+ privateKey: inputText.trimmingCharacters(in: .whitespacesAndNewlines),
128
+ name: name
129
+ )
130
+
131
+ case .jwkFile:
132
+ guard let url = selectedFileURL else { throw ImportError.noFileSelected }
133
+ importedWallet = try await store.importFromJWK(fileURL: url, name: name)
134
+
135
+ case .pemFile:
136
+ guard let url = selectedFileURL else { throw ImportError.noFileSelected }
137
+ importedWallet = try await store.importFromPEM(fileURL: url, name: name)
138
+
139
+ case .keystoreJSON:
140
+ guard let url = selectedFileURL else { throw ImportError.noFileSelected }
141
+ importedWallet = try await store.importFromKeystore(
142
+ fileURL: url,
143
+ password: passwordInput,
144
+ name: name
145
+ )
146
+ }
147
+
148
+ isProcessing = false
149
+ currentStep = .complete
150
+ } catch {
151
+ isProcessing = false
152
+ errorMessage = error.localizedDescription
153
+ currentStep = .enterInput
154
+ }
155
+ }
156
+
157
+ func reset() {
158
+ currentStep = .selectChain
159
+ selectedChain = .ethereum
160
+ selectedMethod = .mnemonic
161
+ walletName = ""
162
+ inputText = ""
163
+ passwordInput = ""
164
+ selectedFileURL = nil
165
+ importedWallet = nil
166
+ isProcessing = false
167
+ errorMessage = nil
168
+ }
169
+
170
+ enum ImportError: LocalizedError {
171
+ case noFileSelected
172
+
173
+ var errorDescription: String? {
174
+ switch self {
175
+ case .noFileSelected: return "No file was selected for import"
176
+ }
177
+ }
178
+ }
179
+ }
@@ -0,0 +1,286 @@
1
+ import SwiftUI
2
+ import Combine
3
+
4
+ /// Central state manager for all wallet operations.
5
+ /// Acts as the single source of truth for wallet data in the app.
6
+ @MainActor
7
+ final class WalletStore: ObservableObject {
8
+
9
+ @Published var wallets: [Wallet] = []
10
+ @Published var isLoading: Bool = false
11
+ @Published var errorMessage: String?
12
+ @Published var selectedWalletId: UUID?
13
+
14
+ private let fileService = FileService.shared
15
+ private let keychain = KeychainService.shared
16
+ private let cliBridge = CLIBridge()
17
+ private let backupService = BackupService.shared
18
+
19
+ // MARK: - Computed Properties
20
+
21
+ var selectedWallet: Wallet? {
22
+ guard let id = selectedWalletId else { return nil }
23
+ return wallets.first { $0.id == id }
24
+ }
25
+
26
+ var walletsByChain: [Chain: [Wallet]] {
27
+ Dictionary(grouping: wallets, by: \.chain)
28
+ }
29
+
30
+ var totalWalletCount: Int { wallets.count }
31
+
32
+ var icpWallets: [Wallet] { wallets.filter { $0.chain == .icp } }
33
+ var ethWallets: [Wallet] { wallets.filter { $0.chain == .ethereum } }
34
+ var arweaveWallets: [Wallet] { wallets.filter { $0.chain == .arweave } }
35
+
36
+ // MARK: - Persistence
37
+
38
+ func loadWallets() {
39
+ do {
40
+ wallets = try fileService.loadWallets()
41
+ } catch {
42
+ errorMessage = "Failed to load wallets: \(error.localizedDescription)"
43
+ wallets = []
44
+ }
45
+ }
46
+
47
+ func saveWallets() {
48
+ do {
49
+ try fileService.saveWallets(wallets)
50
+ } catch {
51
+ errorMessage = "Failed to save wallets: \(error.localizedDescription)"
52
+ }
53
+ }
54
+
55
+ // MARK: - Create Wallet
56
+
57
+ /// Create a new wallet via the CLI bridge
58
+ func createWallet(chain: Chain, name: String) async throws -> WalletCreationResult {
59
+ isLoading = true
60
+ defer { isLoading = false }
61
+
62
+ let result = try await cliBridge.generateWallet(chain: chain, name: name)
63
+
64
+ let walletId = UUID()
65
+ let wallet = Wallet(
66
+ id: walletId,
67
+ name: name,
68
+ chain: chain,
69
+ address: result.address,
70
+ isImported: false,
71
+ derivationPath: chain.derivationPath,
72
+ hasMnemonicBackup: result.mnemonic != nil
73
+ )
74
+
75
+ // Save secrets to Keychain
76
+ if let mnemonic = result.mnemonic {
77
+ try keychain.saveMnemonic(mnemonic, forWalletId: walletId)
78
+ }
79
+ if let privateKey = result.privateKey {
80
+ try keychain.savePrivateKey(privateKey, forWalletId: walletId)
81
+ }
82
+
83
+ // Add to wallet list and persist
84
+ wallets.append(wallet)
85
+ saveWallets()
86
+
87
+ return WalletCreationResult(
88
+ wallet: wallet,
89
+ mnemonic: result.mnemonic,
90
+ privateKeyHex: result.privateKey,
91
+ jwkData: nil
92
+ )
93
+ }
94
+
95
+ // MARK: - Import Wallet
96
+
97
+ /// Import a wallet from a mnemonic phrase
98
+ func importFromMnemonic(chain: Chain, mnemonic: String, name: String) async throws -> Wallet {
99
+ isLoading = true
100
+ defer { isLoading = false }
101
+
102
+ let result = try await cliBridge.importFromMnemonic(chain: chain, mnemonic: mnemonic, name: name)
103
+
104
+ let walletId = UUID()
105
+ let wallet = Wallet(
106
+ id: walletId,
107
+ name: name,
108
+ chain: chain,
109
+ address: result.address,
110
+ isImported: true,
111
+ derivationPath: chain.derivationPath,
112
+ hasMnemonicBackup: true
113
+ )
114
+
115
+ try keychain.saveMnemonic(mnemonic, forWalletId: walletId)
116
+ if let privateKey = result.privateKey {
117
+ try keychain.savePrivateKey(privateKey, forWalletId: walletId)
118
+ }
119
+
120
+ wallets.append(wallet)
121
+ saveWallets()
122
+ return wallet
123
+ }
124
+
125
+ /// Import a wallet from a private key
126
+ func importFromPrivateKey(chain: Chain, privateKey: String, name: String) async throws -> Wallet {
127
+ isLoading = true
128
+ defer { isLoading = false }
129
+
130
+ let result = try await cliBridge.importFromPrivateKey(chain: chain, privateKey: privateKey, name: name)
131
+
132
+ let walletId = UUID()
133
+ let wallet = Wallet(
134
+ id: walletId,
135
+ name: name,
136
+ chain: chain,
137
+ address: result.address,
138
+ isImported: true,
139
+ derivationPath: chain.derivationPath,
140
+ hasMnemonicBackup: false
141
+ )
142
+
143
+ try keychain.savePrivateKey(privateKey, forWalletId: walletId)
144
+
145
+ wallets.append(wallet)
146
+ saveWallets()
147
+ return wallet
148
+ }
149
+
150
+ /// Import an Arweave wallet from a JWK file
151
+ func importFromJWK(fileURL: URL, name: String) async throws -> Wallet {
152
+ isLoading = true
153
+ defer { isLoading = false }
154
+
155
+ let jwkData = try FileService.shared.readJWKFile(at: fileURL)
156
+ let result = try await cliBridge.importFromJWK(filePath: fileURL.path, name: name)
157
+
158
+ let walletId = UUID()
159
+ let wallet = Wallet(
160
+ id: walletId,
161
+ name: name,
162
+ chain: .arweave,
163
+ address: result.address,
164
+ isImported: true,
165
+ derivationPath: "",
166
+ hasMnemonicBackup: false
167
+ )
168
+
169
+ try keychain.saveJWK(jwkData, forWalletId: walletId)
170
+
171
+ wallets.append(wallet)
172
+ saveWallets()
173
+ return wallet
174
+ }
175
+
176
+ /// Import an ICP identity from a PEM file
177
+ func importFromPEM(fileURL: URL, name: String) async throws -> Wallet {
178
+ isLoading = true
179
+ defer { isLoading = false }
180
+
181
+ let pemContent = try FileService.shared.readPEMFile(at: fileURL)
182
+ let result = try await cliBridge.importFromPEM(filePath: fileURL.path, name: name)
183
+
184
+ let walletId = UUID()
185
+ let wallet = Wallet(
186
+ id: walletId,
187
+ name: name,
188
+ chain: .icp,
189
+ address: result.address,
190
+ isImported: true,
191
+ derivationPath: Chain.icp.derivationPath,
192
+ hasMnemonicBackup: false
193
+ )
194
+
195
+ try keychain.savePrivateKey(pemContent, forWalletId: walletId)
196
+
197
+ wallets.append(wallet)
198
+ saveWallets()
199
+ return wallet
200
+ }
201
+
202
+ /// Import an Ethereum wallet from a keystore JSON
203
+ func importFromKeystore(fileURL: URL, password: String, name: String) async throws -> Wallet {
204
+ isLoading = true
205
+ defer { isLoading = false }
206
+
207
+ _ = try FileService.shared.readKeystoreFile(at: fileURL)
208
+ let result = try await cliBridge.importFromKeystore(filePath: fileURL.path, password: password, name: name)
209
+
210
+ let walletId = UUID()
211
+ let wallet = Wallet(
212
+ id: walletId,
213
+ name: name,
214
+ chain: .ethereum,
215
+ address: result.address,
216
+ isImported: true,
217
+ derivationPath: Chain.ethereum.derivationPath,
218
+ hasMnemonicBackup: false
219
+ )
220
+
221
+ if let privateKey = result.privateKey {
222
+ try keychain.savePrivateKey(privateKey, forWalletId: walletId)
223
+ }
224
+
225
+ wallets.append(wallet)
226
+ saveWallets()
227
+ return wallet
228
+ }
229
+
230
+ // MARK: - Wallet Management
231
+
232
+ /// Delete a wallet and all its associated secrets
233
+ func deleteWallet(_ wallet: Wallet) {
234
+ keychain.deleteAllSecrets(forWalletId: wallet.id)
235
+ wallets.removeAll { $0.id == wallet.id }
236
+ if selectedWalletId == wallet.id {
237
+ selectedWalletId = nil
238
+ }
239
+ saveWallets()
240
+ }
241
+
242
+ /// Rename a wallet
243
+ func renameWallet(_ wallet: Wallet, to newName: String) {
244
+ guard let index = wallets.firstIndex(where: { $0.id == wallet.id }) else { return }
245
+ wallets[index].name = newName
246
+ saveWallets()
247
+ }
248
+
249
+ /// Refresh balance for a single wallet
250
+ func refreshBalance(for wallet: Wallet) async {
251
+ guard let index = wallets.firstIndex(where: { $0.id == wallet.id }) else { return }
252
+
253
+ do {
254
+ let balance = try await cliBridge.getBalance(chain: wallet.chain, address: wallet.address)
255
+ wallets[index].cachedBalance = balance
256
+ wallets[index].balanceLastUpdated = Date()
257
+ saveWallets()
258
+ } catch {
259
+ // Silently fail balance refresh — don't disrupt the UI
260
+ }
261
+ }
262
+
263
+ /// Refresh balances for all wallets
264
+ func refreshAllBalances() async {
265
+ isLoading = true
266
+ defer { isLoading = false }
267
+
268
+ await withTaskGroup(of: Void.self) { group in
269
+ for wallet in wallets {
270
+ group.addTask { [weak self] in
271
+ await self?.refreshBalance(for: wallet)
272
+ }
273
+ }
274
+ }
275
+ }
276
+
277
+ /// Check if we have the mnemonic for a given wallet
278
+ func hasMnemonic(for wallet: Wallet) -> Bool {
279
+ keychain.hasMnemonic(forWalletId: wallet.id)
280
+ }
281
+
282
+ /// Retrieve mnemonic for a wallet (requires confirmation)
283
+ func getMnemonic(for wallet: Wallet) -> String? {
284
+ try? keychain.getMnemonic(forWalletId: wallet.id)
285
+ }
286
+ }