mirror of
https://github.com/luxfi/safe-ios.git
synced 2026-10-30 15:19:20 +00:00
190 lines
6.5 KiB
Swift
190 lines
6.5 KiB
Swift
//
|
|
// TransactionDataTransformer.swift
|
|
// Multisig
|
|
//
|
|
// Created by Dmitry Bespalov on 16.01.22.
|
|
// Copyright © 2022 Gnosis Ltd. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
// enhances the client gateway transaction data with the local data to display correct transaction status.
|
|
class TransactionDataTransformer {
|
|
|
|
let safe: Safe
|
|
let chain: Chain
|
|
|
|
init(safe: Safe, chain: Chain) {
|
|
self.safe = safe
|
|
self.chain = chain
|
|
assert(safe.address != nil)
|
|
assert(chain.id != nil)
|
|
}
|
|
|
|
func transformed(list: [SCGModels.TransactionSummaryItem]) -> [SCGModels.TransactionSummaryItem] {
|
|
assert(Thread.isMainThread)
|
|
|
|
let localTxs = localData(safeAddress: safe.address!, chainId: chain.id!)
|
|
|
|
guard !localTxs.isEmpty else { return list }
|
|
|
|
var hasDatabaseChanges: Bool = false
|
|
|
|
let result = list.map { item -> SCGModels.TransactionSummaryItem in
|
|
switch item {
|
|
case .transaction(let txItem):
|
|
// find local matching transaction
|
|
|
|
// FIXME: this will break when the backend id schema will change! Instead we need info from the backend.
|
|
let idParts = txItem.transaction.id.split(separator: "_")
|
|
guard idParts.count == 3, let safeTxHash = idParts.last.map(String.init), !safeTxHash.isEmpty else {
|
|
return item
|
|
}
|
|
|
|
guard let localTx = localTxs.first(where: { $0.safeTxHash == safeTxHash }) else {
|
|
return item
|
|
}
|
|
|
|
guard let localRawStatus = localTx.status, let localStatus = SCGModels.TxStatus(rawValue: localRawStatus) else {
|
|
return item
|
|
}
|
|
|
|
// compute status updates
|
|
|
|
var txItem = txItem
|
|
switch (txItem.transaction.txStatus, localStatus) {
|
|
case (.awaitingExecution, .pending):
|
|
// assume the safe tx not minded yet
|
|
txItem.transaction.txStatus = .pending
|
|
|
|
case (.awaitingExecution, .awaitingExecution):
|
|
// show that execution attempt failed
|
|
txItem.transaction.txStatus = .awaitingExecution
|
|
|
|
case (.awaitingExecution, .success):
|
|
// tx was mined but the backend hasn't updated yet. Keep it pending.
|
|
txItem.transaction.txStatus = .pending
|
|
|
|
case (.success, .pending):
|
|
// backend got success status earlier than local data updated, get the value from backend data
|
|
localTx.status = SCGModels.TxStatus.success.rawValue
|
|
hasDatabaseChanges = true
|
|
|
|
case (.failed, .pending):
|
|
// backend got failed status earlier than local data updated, get the value from backend data
|
|
localTx.status = SCGModels.TxStatus.failed.rawValue
|
|
hasDatabaseChanges = true
|
|
|
|
default:
|
|
break
|
|
}
|
|
|
|
return .transaction(txItem)
|
|
default:
|
|
return item
|
|
}
|
|
}
|
|
|
|
if hasDatabaseChanges {
|
|
App.shared.coreDataStack.saveContext()
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func localData(safeAddress: String, chainId: String) -> [CDEthTransaction] {
|
|
let context = App.shared.coreDataStack.viewContext
|
|
|
|
let fetchRequest = CDEthTransaction.fetchRequest()
|
|
fetchRequest.sortDescriptors = [
|
|
NSSortDescriptor(key: "dateUpdatedAt", ascending: false)
|
|
]
|
|
|
|
fetchRequest.predicate = NSPredicate(format: "safeAddress like[c] %@ AND chainId == %@",
|
|
safeAddress, chainId)
|
|
|
|
let cdEthTransactions: [CDEthTransaction]
|
|
|
|
do {
|
|
cdEthTransactions = try context.fetch(fetchRequest)
|
|
} catch {
|
|
return []
|
|
}
|
|
|
|
return cdEthTransactions
|
|
}
|
|
|
|
|
|
func transformed(transaction: SCGModels.TransactionDetails) -> SCGModels.TransactionDetails {
|
|
guard let safeTxHash = transaction.multisigInfo?.safeTxHash, !safeTxHash.description.isEmpty else {
|
|
return transaction
|
|
}
|
|
|
|
guard let localTx = localTransaction(safeAddress: safe.address!, chainId: chain.id!, safeTxHash: safeTxHash.description) else {
|
|
return transaction
|
|
}
|
|
|
|
guard let localRawStatus = localTx.status, let localStatus = SCGModels.TxStatus(rawValue: localRawStatus) else {
|
|
return transaction
|
|
}
|
|
var hasDatabaseChanges = false
|
|
|
|
var transaction = transaction
|
|
switch (transaction.txStatus, localStatus) {
|
|
case (.awaitingExecution, .pending):
|
|
// assume the safe tx not minded yet
|
|
transaction.txStatus = .pending
|
|
transaction.txHash = localTx.ethTxHash.map(DataString.init(hex:))
|
|
|
|
case (.awaitingExecution, .success):
|
|
// tx was mined but the backend hasn't updated yet. Keep it pending.
|
|
transaction.txStatus = .pending
|
|
transaction.txHash = localTx.ethTxHash.map(DataString.init(hex:))
|
|
|
|
case (.success, .pending):
|
|
// backend got success status earlier than local data updated, get the value from backend data
|
|
localTx.status = SCGModels.TxStatus.success.rawValue
|
|
hasDatabaseChanges = true
|
|
|
|
case (.failed, .pending):
|
|
// backend got success status earlier than local data updated, get the value from backend data
|
|
localTx.status = SCGModels.TxStatus.failed.rawValue
|
|
hasDatabaseChanges = true
|
|
|
|
default:
|
|
break
|
|
}
|
|
|
|
if hasDatabaseChanges {
|
|
App.shared.coreDataStack.saveContext()
|
|
}
|
|
|
|
return transaction
|
|
}
|
|
|
|
func localTransaction(safeAddress: String, chainId: String, safeTxHash: String) -> CDEthTransaction? {
|
|
let context = App.shared.coreDataStack.viewContext
|
|
|
|
let fetchRequest = CDEthTransaction.fetchRequest()
|
|
fetchRequest.sortDescriptors = [
|
|
NSSortDescriptor(key: "dateUpdatedAt", ascending: false)
|
|
]
|
|
fetchRequest.fetchLimit = 1
|
|
|
|
fetchRequest.predicate = NSPredicate(format: "safeAddress like[c] %@ AND chainId == %@ AND safeTxHash like[c] %@",
|
|
safeAddress, chainId, safeTxHash)
|
|
|
|
let cdEthTransactions: [CDEthTransaction]
|
|
|
|
do {
|
|
cdEthTransactions = try context.fetch(fetchRequest)
|
|
} catch {
|
|
return nil
|
|
}
|
|
|
|
return cdEthTransactions.first
|
|
|
|
}
|
|
|
|
}
|