spb/os-vault Public
Self-custody, multi-chain crypto wallet for macOS. One recovery phrase, six chain families, zero API keys — nothing leaves your Mac.
Swift 96%
Shell 3.4%
Makefile 0.6%
1//2// TransactionRecord.swift3// OS Vault4//5// Author: Simon-Pierre Boucher6// Mail: contact@spboucher.ai7//89import Foundation10import BigInt1112/// A send initiated from this app, persisted locally (non-sensitive data13/// only: it is all public on-chain anyway). Full history lives on the14/// explorer; each record links there.15public struct TransactionRecord: Identifiable, Codable, Equatable {16 public enum Status: String, Codable {17 case pending, confirmed, failed18 }1920 public let id: UUID21 public let hash: String22 public let tokenSymbol: String23 public let tokenDecimals: Int24 public let amountUnits: String // BigUInt as decimal string (Codable-safe)25 public let recipient: String26 public let network: Network27 public var status: Status28 public let date: Date2930 public init(hash: String, asset: Asset, amountUnits: BigUInt,31 recipient: String, network: Network, status: Status, date: Date = Date()) {32 self.id = UUID()33 self.hash = hash34 self.tokenSymbol = asset.symbol(on: network)35 self.tokenDecimals = asset.decimals36 self.amountUnits = String(amountUnits)37 self.recipient = recipient38 self.network = network39 self.status = status40 self.date = date41 }4243 public var displayAmount: String {44 guard let units = BigUInt(amountUnits, radix: 10) else { return amountUnits }45 return TokenAmount.format(units, decimals: tokenDecimals)46 }4748 public var explorerURL: URL { network.explorerTxURL(hash) }49}5051/// JSON-file persistence for local send history.52public final class HistoryStore {53 private let fileURL: URL54 private let maxEntries = 2005556 public init(fileURL: URL? = nil) {57 if let fileURL {58 self.fileURL = fileURL59 } else {60 let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]61 self.fileURL = support.appendingPathComponent("OSVault/history.json")62 }63 }6465 public func load() -> [TransactionRecord] {66 guard let data = try? Data(contentsOf: fileURL),67 let records = try? JSONDecoder().decode([TransactionRecord].self, from: data) else {68 return []69 }70 return records71 }7273 public func save(_ records: [TransactionRecord]) {74 let trimmed = Array(records.prefix(maxEntries))75 let dir = fileURL.deletingLastPathComponent()76 try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)77 if let data = try? JSONEncoder().encode(trimmed) {78 try? data.write(to: fileURL, options: [.atomic])79 }80 }8182 public func clear() {83 try? FileManager.default.removeItem(at: fileURL)84 }85}86