Fixes crash of `null pointer` when switch bettwen wallets #3011

pull/3065/head
Vladyslav shepitko 3 years ago
parent afa6c899c6
commit e92002c698
  1. 4
      AlphaWallet/Activities/ActivitiesService.swift
  2. 128
      AlphaWallet/Activities/ViewControllers/ActivitiesViewController.swift
  3. 8
      AlphaWallet/Activities/ViewModels/ActivitiesViewModel.swift
  4. 2
      AlphaWallet/TokenScriptClient/Coordinators/EventSourceCoordinator.swift
  5. 2
      AlphaWallet/TokenScriptClient/Coordinators/EventSourceCoordinatorForActivities.swift

@ -281,7 +281,7 @@ class ActivitiesService: NSObject, ActivitiesServiceType {
if let t = tokensCache[contract] {
token = t
} else {
let tokensDatastore = tokensStorages[server]
guard let tokensDatastore = tokensStorages[safe: server] else { return nil }
guard let t = tokensDatastore.tokenThreadSafe(forContract: contract) else { return nil }
let tt = Activity.AssignedToken(tokenObject: t)
tokensCache[contract] = tt
@ -312,7 +312,7 @@ class ActivitiesService: NSObject, ActivitiesServiceType {
tokenHolders = [TokenHolder(tokens: [token], contractAddress: tokenObject.contractAddress, hasAssetDefinition: true)]
} else {
let tokensDatastore = tokensStorages[server]
guard let tokensDatastore = tokensStorages[safe: server] else { return nil }
guard let t = tokensDatastore.tokenThreadSafe(forContract: tokenObject.contractAddress) else { return nil }
tokenHolders = TokenAdaptor(token: t, assetDefinitionStore: assetDefinitionStore, eventsDataStore: eventsDataStore).getTokenHolders(forWallet: wallet)

@ -364,134 +364,6 @@ extension ActivitiesViewController {
}
extension ActivitiesViewController.functional {
static func extractTokenAndActivityName(fromTransactionRow transactionRow: TransactionRow, tokensStorages: ServerDictionary<TokensDataStore>, wallet: AlphaWallet.Address) -> (token: TokenObject, activityName: String)? {
enum TokenOperation {
case nativeCryptoTransfer(TokenObject)
case completedTransfer(TokenObject)
case pendingTransfer(TokenObject)
case completedErc20Approval(TokenObject)
case pendingErc20Approval(TokenObject)
var token: TokenObject {
switch self {
case .nativeCryptoTransfer(let token):
return token
case .completedTransfer(let token):
return token
case .pendingTransfer(let token):
return token
case .completedErc20Approval(let token):
return token
case .pendingErc20Approval(let token):
return token
}
}
}
let erc20TokenOperation: TokenOperation?
if transactionRow.operation == nil {
erc20TokenOperation = .nativeCryptoTransfer(TokensDataStore.etherToken(forServer: transactionRow.server))
} else {
switch (transactionRow.state, transactionRow.operation?.operationType) {
case (.pending, .nativeCurrencyTokenTransfer), (.pending, .erc20TokenTransfer), (.pending, .erc721TokenTransfer), (.pending, .erc875TokenTransfer):
erc20TokenOperation = transactionRow.operation?.contractAddress.flatMap { tokensStorages[transactionRow.server].token(forContract: $0) }.flatMap { TokenOperation.pendingTransfer($0) }
case (.completed, .nativeCurrencyTokenTransfer), (.completed, .erc20TokenTransfer), (.completed, .erc721TokenTransfer), (.completed, .erc875TokenTransfer):
erc20TokenOperation = transactionRow.operation?.contractAddress.flatMap { tokensStorages[transactionRow.server].token(forContract: $0) }.flatMap { TokenOperation.completedTransfer($0) }
//Explicitly listing out combinations so future changes to enums will be caught by compiler
case (.pending, .erc20TokenApprove):
erc20TokenOperation = transactionRow.operation?.contractAddress.flatMap { tokensStorages[transactionRow.server].token(forContract: $0) }.flatMap { TokenOperation.pendingErc20Approval($0) }
case (.completed, .erc20TokenApprove):
erc20TokenOperation = transactionRow.operation?.contractAddress.flatMap { tokensStorages[transactionRow.server].token(forContract: $0) }.flatMap { TokenOperation.completedErc20Approval($0) }
case (.unknown, _), (.error, _), (.failed, _), (_, .unknown), (.completed, .none), (.pending, nil):
erc20TokenOperation = .none
}
}
guard let token = erc20TokenOperation?.token else { return nil }
let activityName: String
switch erc20TokenOperation {
case .nativeCryptoTransfer, .completedTransfer, .pendingTransfer, .none:
if wallet.sameContract(as: transactionRow.from) {
activityName = "sent"
} else {
activityName = "received"
}
case .completedErc20Approval, .pendingErc20Approval:
activityName = "ownerApproved"
}
return (token: token, activityName: activityName)
}
static func createPseudoActivity(fromTransactionRow transactionRow: TransactionRow, tokensStorages: ServerDictionary<TokensDataStore>, wallet: AlphaWallet.Address) -> Activity? {
guard let (token, activityName) = extractTokenAndActivityName(fromTransactionRow: transactionRow, tokensStorages: tokensStorages, wallet: wallet) else { return nil }
var cardAttributes = [AttributeId: AssetInternalValue]()
cardAttributes["symbol"] = .string(transactionRow.server.symbol)
if let operation = transactionRow.operation, operation.symbol != nil, let value = BigUInt(operation.value) {
cardAttributes["amount"] = .uint(value)
} else {
if let value = BigUInt(transactionRow.value) {
cardAttributes["amount"] = .uint(value)
}
}
if let value = AlphaWallet.Address(string: transactionRow.from) {
cardAttributes["from"] = .address(value)
}
if let toString = transactionRow.operation?.to, let to = AlphaWallet.Address(string: toString) {
cardAttributes["to"] = .address(to)
} else {
if let value = AlphaWallet.Address(string: transactionRow.to) {
cardAttributes["to"] = .address(value)
}
}
var timestamp: GeneralisedTime = .init()
timestamp.date = transactionRow.date
cardAttributes["timestamp"] = .generalisedTime(timestamp)
let state: Activity.State
switch transactionRow.state {
case .pending:
state = .pending
case .completed:
state = .completed
case .error, .failed:
state = .failed
//TODO we don't need the other states at the moment
case .unknown:
state = .completed
}
let rowType: ActivityRowType
switch transactionRow {
case .standalone:
rowType = .standalone
case .group:
rowType = .group
case .item:
rowType = .item
}
return .init(
//We only use this ID for refreshing the display of specific activity, since the display for ETH send/receives don't ever need to be refreshed, just need a number that don't clash with other activities
id: transactionRow.blockNumber + 10000000,
rowType: rowType,
tokenObject: Activity.AssignedToken(tokenObject: token),
server: transactionRow.server,
name: activityName,
eventName: activityName,
blockNumber: transactionRow.blockNumber,
transactionId: transactionRow.id,
transactionIndex: transactionRow.transactionIndex,
//We don't use this for transactions, so it's ok
logIndex: 0,
date: transactionRow.date,
values: (token: .init(), card: cardAttributes),
view: (html: "", style: ""),
itemView: (html: "", style: ""),
isBaseCard: true,
state: state
)
}
fileprivate static func headerView(for section: Int, viewModel: ActivitiesViewModel) -> UIView {
let container = UIView()
container.backgroundColor = viewModel.headerBackgroundColor

@ -269,14 +269,14 @@ extension ActivitiesViewModel.functional {
} else {
switch (transactionRow.state, transactionRow.operation?.operationType) {
case (.pending, .nativeCurrencyTokenTransfer), (.pending, .erc20TokenTransfer), (.pending, .erc721TokenTransfer), (.pending, .erc875TokenTransfer):
erc20TokenOperation = transactionRow.operation?.contractAddress.flatMap { tokensStorages[transactionRow.server].tokenThreadSafe(forContract: $0) }.flatMap { TokenOperation.pendingTransfer($0) }
erc20TokenOperation = transactionRow.operation?.contractAddress.flatMap { tokensStorages[safe: transactionRow.server]?.tokenThreadSafe(forContract: $0) }.flatMap { TokenOperation.pendingTransfer($0) }
case (.completed, .nativeCurrencyTokenTransfer), (.completed, .erc20TokenTransfer), (.completed, .erc721TokenTransfer), (.completed, .erc875TokenTransfer):
erc20TokenOperation = transactionRow.operation?.contractAddress.flatMap { tokensStorages[transactionRow.server].tokenThreadSafe(forContract: $0) }.flatMap { TokenOperation.completedTransfer($0) }
erc20TokenOperation = transactionRow.operation?.contractAddress.flatMap { tokensStorages[safe: transactionRow.server]?.tokenThreadSafe(forContract: $0) }.flatMap { TokenOperation.completedTransfer($0) }
//Explicitly listing out combinations so future changes to enums will be caught by compiler
case (.pending, .erc20TokenApprove):
erc20TokenOperation = transactionRow.operation?.contractAddress.flatMap { tokensStorages[transactionRow.server].tokenThreadSafe(forContract: $0) }.flatMap { TokenOperation.pendingErc20Approval($0) }
erc20TokenOperation = transactionRow.operation?.contractAddress.flatMap { tokensStorages[safe: transactionRow.server]?.tokenThreadSafe(forContract: $0) }.flatMap { TokenOperation.pendingErc20Approval($0) }
case (.completed, .erc20TokenApprove):
erc20TokenOperation = transactionRow.operation?.contractAddress.flatMap { tokensStorages[transactionRow.server].tokenThreadSafe(forContract: $0) }.flatMap { TokenOperation.completedErc20Approval($0) }
erc20TokenOperation = transactionRow.operation?.contractAddress.flatMap { tokensStorages[safe: transactionRow.server]?.tokenThreadSafe(forContract: $0) }.flatMap { TokenOperation.completedErc20Approval($0) }
case (.unknown, _), (.error, _), (.failed, _), (_, .unknown), (.completed, .none), (.pending, nil):
erc20TokenOperation = .none
}

@ -65,7 +65,7 @@ class EventSourceCoordinator: EventSourceCoordinatorType {
guard !isFetching else { return }
isFetching = true
let tokensStoragesForEnabledServers = config.enabledServers.map { tokensStorages[$0] }
let tokensStoragesForEnabledServers = config.enabledServers.compactMap { tokensStorages[safe: $0] }
let fetchPromises = tokensStoragesForEnabledServers.flatMap {
$0.enabledObject.flatMap { fetchEventsByTokenId(forToken: $0) }
}

@ -79,7 +79,7 @@ class EventSourceCoordinatorForActivities: EventSourceCoordinatorForActivitiesTy
typealias EnabledTokenAddreses = [(contract: AlphaWallet.Address, tokenType: TokenType, server: RPCServer)]
private func tokensForEnabledRPCServers() -> Promise<EnabledTokenAddreses> {
return Promise { seal in
let tokensStoragesForEnabledServers = self.config.enabledServers.map { self.tokensStorages[$0] }
let tokensStoragesForEnabledServers = self.config.enabledServers.compactMap { self.tokensStorages[safe: $0] }
let data = tokensStoragesForEnabledServers.flatMap {
$0.enabledObject

Loading…
Cancel
Save