secant-ios-wallet/modules/Sources/Features/Scan/ScanChecker.swift

133 lines
4.1 KiB
Swift

//
// ScanChecker.swift
// modules
//
// Created by Lukáš Korba on 2024-11-20.
//
import ComposableArchitecture
import URIParser
import ZcashSDKEnvironment
import ZcashPaymentURI
import KeystoneHandler
import KeystoneSDK
public protocol ScanChecker: Equatable {
var id: Int { get }
func checkQRCode(_ qrCode: String) -> Scan.Action?
}
public struct ZcashAddressScanChecker: ScanChecker, Equatable {
public let id = 0
public func checkQRCode(_ qrCode: String) -> Scan.Action? {
@Dependency(\.uriParser) var uriParser
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
if uriParser.isValidURI(qrCode, zcashSDKEnvironment.network.networkType) {
return .foundAddress(qrCode.redacted)
} else {
return nil
}
}
}
public struct RequestZecScanChecker: ScanChecker, Equatable {
public let id = 1
public func checkQRCode(_ qrCode: String) -> Scan.Action? {
@Dependency(\.uriParser) var uriParser
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
if let parserResult = uriParser.checkRP(qrCode) {
return .foundRequestZec(parserResult)
} else {
return nil
}
}
}
public struct KeystoneScanChecker: ScanChecker, Equatable {
public let id = 2
public func checkQRCode(_ qrCode: String) -> Scan.Action? {
@Dependency(\.keystoneHandler) var keystoneHandler
if let result = keystoneHandler.decodeQR(qrCode) {
if result.progress < 100 {
return ScanCheckerWrapper.reportCheck(qrCode, progress: result.progress)
}
if let resultUR = result.ur, result.progress == 100 {
if let zcashAccounts = try? KeystoneSDK().parseZcashAccounts(ur: resultUR) {
return .foundAccounts(zcashAccounts)
} else {
return nil
}
}
}
return nil
}
}
public struct KeystonePcztScanChecker: ScanChecker, Equatable {
public let id = 3
public func checkQRCode(_ qrCode: String) -> Scan.Action? {
@Dependency(\.keystoneHandler) var keystoneHandler
if let result = keystoneHandler.decodeQR(qrCode) {
if result.progress < 100 {
return ScanCheckerWrapper.reportCheck(qrCode, progress: result.progress)
}
if let resultUR = result.ur, result.progress == 100 {
if let zcashPCZT = try? KeystoneZcashSDK().parseZcashPczt(ur: resultUR) {
return .foundPCZT(zcashPCZT)
} else {
return nil
}
}
}
return nil
}
}
public struct ScanCheckerWrapper: Equatable {
let checker: any ScanChecker
public static let zcashAddressScanChecker = ScanCheckerWrapper(ZcashAddressScanChecker())
public static let requestZecScanChecker = ScanCheckerWrapper(RequestZecScanChecker())
public static let keystoneScanChecker = ScanCheckerWrapper(KeystoneScanChecker())
public static let keystonePCZTScanChecker = ScanCheckerWrapper(KeystonePcztScanChecker())
static public func == (lhs: ScanCheckerWrapper, rhs: ScanCheckerWrapper) -> Bool {
return lhs.checker.id == rhs.checker.id
}
public init(_ checker: any ScanChecker) {
self.checker = checker
}
static func reportCheck(_ qrCode: String, progress: Int) -> Scan.Action {
var firstNumber: Int?
var secondNumber: Int?
let pattern = #"/\d+-(\d+)/"#
if let match = qrCode.range(of: pattern, options: .regularExpression) {
let substring = qrCode[match]
if let secNumber = substring.split(separator: "-").last?.split(separator: "/").first {
secondNumber = Int(secNumber)
}
if let firNumber = substring.split(separator: "-").first?.split(separator: "/").last {
firstNumber = Int(firNumber)
}
}
return .animatedQRProgress(progress, firstNumber, secondNumber)
}
}