// // TransactionRecord.swift // OS Vault // // Author: Simon-Pierre Boucher // Mail: contact@spboucher.ai // import Foundation import BigInt /// A send initiated from this app, persisted locally (non-sensitive data /// only: it is all public on-chain anyway). Full history lives on the /// explorer; each record links there. public struct TransactionRecord: Identifiable, Codable, Equatable { public enum Status: String, Codable { case pending, confirmed, failed } public let id: UUID public let hash: String public let tokenSymbol: String public let tokenDecimals: Int public let amountUnits: String // BigUInt as decimal string (Codable-safe) public let recipient: String public let network: Network public var status: Status public let date: Date public init(hash: String, asset: Asset, amountUnits: BigUInt, recipient: String, network: Network, status: Status, date: Date = Date()) { self.id = UUID() self.hash = hash self.tokenSymbol = asset.symbol(on: network) self.tokenDecimals = asset.decimals self.amountUnits = String(amountUnits) self.recipient = recipient self.network = network self.status = status self.date = date } public var displayAmount: String { guard let units = BigUInt(amountUnits, radix: 10) else { return amountUnits } return TokenAmount.format(units, decimals: tokenDecimals) } public var explorerURL: URL { network.explorerTxURL(hash) } } /// JSON-file persistence for local send history. public final class HistoryStore { private let fileURL: URL private let maxEntries = 200 public init(fileURL: URL? = nil) { if let fileURL { self.fileURL = fileURL } else { let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] self.fileURL = support.appendingPathComponent("OSVault/history.json") } } public func load() -> [TransactionRecord] { guard let data = try? Data(contentsOf: fileURL), let records = try? JSONDecoder().decode([TransactionRecord].self, from: data) else { return [] } return records } public func save(_ records: [TransactionRecord]) { let trimmed = Array(records.prefix(maxEntries)) let dir = fileURL.deletingLastPathComponent() try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) if let data = try? JSONEncoder().encode(trimmed) { try? data.write(to: fileURL, options: [.atomic]) } } public func clear() { try? FileManager.default.removeItem(at: fileURL) } }