Merge pull request #1566 from AlphaWallet/change-redeem-to-token-ids

Change redeem to token ids
pull/1567/head
James Sangalli 5 years ago committed by GitHub
commit 2aaee3f2db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      AlphaWallet.xcodeproj/project.pbxproj
  2. 4
      AlphaWallet/Core/Types/AlphaWalletAddress.swift
  3. 2
      AlphaWallet/EtherClient/TrustClient/Models/RawTransaction.swift
  4. 7
      AlphaWallet/RPC/Commands/web3swift-pod/GetERC721ForTicketsBalance.swift
  5. 29
      AlphaWallet/Redeem/Helpers/CreateRedeem.swift
  6. 10
      AlphaWallet/Redeem/ViewControllers/TokenCardRedemptionViewController.swift
  7. 3
      AlphaWallet/Settings/Types/Constants.swift
  8. 2
      AlphaWallet/TokenScriptClient/Coordinators/FetchAssetDefinitionsCoordinator.swift
  9. 10
      AlphaWallet/TokenScriptClient/Models/XMLHandler.swift
  10. 31
      AlphaWallet/Tokens/Coordinators/GetERC721ForTicketsBalanceCoordinator.swift
  11. 23
      AlphaWallet/Tokens/Coordinators/GetIsERC721ForTicketsContractCoordinator.swift
  12. 20
      AlphaWallet/Tokens/Coordinators/SingleChainTokenCoordinator.swift
  13. 2
      AlphaWallet/Tokens/Coordinators/TokensCoordinator.swift
  14. 4
      AlphaWallet/Tokens/Helpers/TokenAdaptor.swift
  15. 18
      AlphaWallet/Tokens/Types/TokenInstanceAction.swift
  16. 6
      AlphaWallet/Tokens/Types/TokenObject.swift
  17. 1
      AlphaWallet/Tokens/Types/TokenType.swift
  18. 93
      AlphaWallet/Tokens/Types/TokensDataStore.swift
  19. 4
      AlphaWallet/Tokens/ViewControllers/NewTokenViewController.swift
  20. 2
      AlphaWallet/Tokens/ViewControllers/TokenInstanceActionViewController.swift
  21. 4
      AlphaWallet/Tokens/ViewControllers/TokenInstanceViewController.swift
  22. 4
      AlphaWallet/Tokens/ViewControllers/TokenViewController.swift
  23. 4
      AlphaWallet/Tokens/ViewControllers/TokensCardViewController.swift
  24. 2
      AlphaWallet/Tokens/ViewControllers/TokensViewController.swift
  25. 4
      AlphaWallet/Tokens/ViewModels/TokenInstanceViewModel.swift
  26. 8
      AlphaWallet/Tokens/ViewModels/TokenViewControllerViewModel.swift
  27. 4
      AlphaWallet/Tokens/ViewModels/TokensCardViewModel.swift
  28. 6
      AlphaWallet/Transactions/Coordinators/TokensCardCoordinator.swift
  29. 7
      AlphaWallet/Transfer/Controllers/TransactionConfigurator.swift
  30. 1
      AlphaWallet/Transfer/Coordinators/SendCoordinator.swift
  31. 9
      AlphaWallet/Transfer/Types/TransferType.swift
  32. 6
      AlphaWallet/Transfer/ViewControllers/SendViewController.swift
  33. 1
      AlphaWallet/Transfer/ViewModels/ConfigureTransactionViewModel.swift
  34. 4
      AlphaWallet/Transfer/ViewModels/ConfirmPaymentDetailsViewModel.swift
  35. 2
      AlphaWallet/Transfer/ViewModels/SendViewModel.swift
  36. 2
      AlphaWallet/UI/BalanceTitleView.swift
  37. 8
      AlphaWalletTests/Redeem/CreateRedeemTests.swift

@ -633,6 +633,9 @@
778EAF7D1FF10AF400C8E2AB /* SettingsCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 778EAF7C1FF10AF400C8E2AB /* SettingsCoordinatorTests.swift */; };
77B3BF3C201908ED00EEC15A /* ConfirmCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77B3BF3B201908ED00EEC15A /* ConfirmCoordinator.swift */; };
77E0E773201FAD06009B4B31 /* BrowserURLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E0E772201FAD05009B4B31 /* BrowserURLParser.swift */; };
79CC74432382922B00157C8F /* GetIsERC721ForTicketsContractCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79CC74422382922B00157C8F /* GetIsERC721ForTicketsContractCoordinator.swift */; };
8499D446C6ADE858FE633096 /* GetERC721ForTicketsBalanceCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8499DFC3A9D0B1E42D74905E /* GetERC721ForTicketsBalanceCoordinator.swift */; };
8499D8751F2D70FBD13F3C93 /* GetERC721ForTicketsBalance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8499DA6A661E357C9D69BA18 /* GetERC721ForTicketsBalance.swift */; };
AA26C61F20412A1E00318B9B /* TokensCardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA26C61D20412A1D00318B9B /* TokensCardViewController.swift */; };
AA26C62320412A4100318B9B /* UIViewInspectableEnhancements.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA26C62120412A4100318B9B /* UIViewInspectableEnhancements.swift */; };
AA26C62420412A4100318B9B /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA26C62220412A4100318B9B /* Double.swift */; };
@ -1322,7 +1325,10 @@
778EAF7C1FF10AF400C8E2AB /* SettingsCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCoordinatorTests.swift; sourceTree = "<group>"; };
77B3BF3B201908ED00EEC15A /* ConfirmCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmCoordinator.swift; sourceTree = "<group>"; };
77E0E772201FAD05009B4B31 /* BrowserURLParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrowserURLParser.swift; sourceTree = "<group>"; };
79CC74422382922B00157C8F /* GetIsERC721ForTicketsContractCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetIsERC721ForTicketsContractCoordinator.swift; sourceTree = "<group>"; };
7BEDA1C4253B796085A0E66C /* Pods_AlphaWalletUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AlphaWalletUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8499DA6A661E357C9D69BA18 /* GetERC721ForTicketsBalance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetERC721ForTicketsBalance.swift; sourceTree = "<group>"; };
8499DFC3A9D0B1E42D74905E /* GetERC721ForTicketsBalanceCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetERC721ForTicketsBalanceCoordinator.swift; sourceTree = "<group>"; };
AA26C61D20412A1D00318B9B /* TokensCardViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokensCardViewController.swift; sourceTree = "<group>"; };
AA26C62120412A4100318B9B /* UIViewInspectableEnhancements.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewInspectableEnhancements.swift; sourceTree = "<group>"; };
AA26C62220412A4100318B9B /* Double.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = "<group>"; };
@ -2398,6 +2404,8 @@
76F1DE7BEE799DDFB68D0F54 /* GetENSOwnerCoordinator.swift */,
5E7C7A3D7408DC690C0F601C /* SingleChainTokenCoordinator.swift */,
5E7C72AF0046EE87D00D4C56 /* GetERC20BalanceCoordinator.swift */,
79CC74422382922B00157C8F /* GetIsERC721ForTicketsContractCoordinator.swift */,
8499DFC3A9D0B1E42D74905E /* GetERC721ForTicketsBalanceCoordinator.swift */,
);
path = Coordinators;
sourceTree = "<group>";
@ -3005,6 +3013,7 @@
5E7C7DCB0BDDD30D10130AE7 /* GetIsERC875Encode.swift */,
5E7C72CD0C22247A6AF7C95E /* GetERC721BalanceEncode.swift */,
76F1DC4B9964504DA12D8D3C /* GetENSInfoEncode.swift */,
8499DA6A661E357C9D69BA18 /* GetERC721ForTicketsBalance.swift */,
);
path = "web3swift-pod";
sourceTree = "<group>";
@ -3828,6 +3837,7 @@
29C80D511FB67A110037B1E0 /* ArrayResponse.swift in Sources */,
290B2B5F1F9177860053C83E /* UIImage.swift in Sources */,
291ED08F1F6F613200E7E93A /* GetTransactionRequest.swift in Sources */,
79CC74432382922B00157C8F /* GetIsERC721ForTicketsContractCoordinator.swift in Sources */,
294EC1DA1FD8E4E60065EB20 /* GasPriceRequest.swift in Sources */,
295A59381F71C1B90092F0FC /* AccountsCoordinator.swift in Sources */,
299B5E2D1FCBC0660051361C /* BalanceProtocol.swift in Sources */,
@ -4368,6 +4378,8 @@
5E7C783EA4C21609101A0F99 /* EmptyDapps.swift in Sources */,
5E7C739611741D5E61900992 /* Eip681Parser.swift in Sources */,
5E7C77217AFE4BD40C25FD8C /* AddressOrEnsName.swift in Sources */,
8499D446C6ADE858FE633096 /* GetERC721ForTicketsBalanceCoordinator.swift in Sources */,
8499D8751F2D70FBD13F3C93 /* GetERC721ForTicketsBalance.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

@ -112,4 +112,8 @@ extension AlphaWallet.Address {
var isFifaTicketcontract: Bool {
return sameContract(as: Constants.ticketContractAddress) || sameContract(as: Constants.ticketContractAddressRopsten)
}
var isUEFATicketContract: Bool {
return sameContract(as: Constants.uefaRopsten)
}
}

@ -134,6 +134,8 @@ extension Transaction {
return .erc20TokenTransfer
case .erc721:
return .erc721TokenTransfer
case .erc721ForTickets:
return .erc721TokenTransfer
case .erc875:
return .erc875TokenTransfer
}

@ -0,0 +1,7 @@
// Copyright © 2019 Stormbird PTE. LTD.
import Foundation
struct GetERC721ForTicketsBalance {
let abi = "[{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"getBalances\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]"
let name = "getBalances"
}

@ -13,28 +13,35 @@ class CreateRedeem {
self.token = token
}
func generateTimeStamp() -> String {
private func generateTimeStamp() -> String {
let time = NSDate().timeIntervalSince1970
//rotate qr every 30 seconds for security (preventing screenshot claims)
let minsTime = Int(time / 30)
return String(minsTime)
}
func redeemMessage(tokenIndices: [UInt16]) -> (message: String, qrCode: String) {
func redeemMessage(tokenIds: [BigUInt]) -> (message: String, qrCode: String) {
let contractAddress = token.contractAddress.eip55String.lowercased()
let messageForSigning = formIndicesSelection(indices: tokenIndices)
let messageForSigning = tokensToHexStringArray(tokens: tokenIds)
+ "," + generateTimeStamp() + "," + contractAddress
let qrCodeData = formIndicesSelection(indices: tokenIndices)
let qrCodeData = tokensToHexStringArray(tokens: tokenIds)
return (messageForSigning, qrCodeData)
}
/**
* Generate a compact string representation of the indices of an
* ERC875 asset. Notice that this function is not used in this
* class. It is used to return the selectionStr to be used as a
* parameter of the constructor
*/
func formIndicesSelection(indices: [UInt16]) -> String {
func redeemMessage(indices: [UInt16]) -> (message: String, qrCode: String) {
let contractAddress = token.contractAddress.eip55String.lowercased()
let messageForSigning = formIndicesSelection(indices: indices)
+ "," + generateTimeStamp() + "," + contractAddress
let qrCodeData = formIndicesSelection(indices: indices)
return (messageForSigning, qrCodeData)
}
private func tokensToHexStringArray(tokens: [BigUInt]) -> String {
//padding to 32 bytes can be done on the ushers side
return tokens.map({ $0.serialize().hexString }).joined(separator: ",")
}
private func formIndicesSelection(indices: [UInt16]) -> String {
let firstValue = indices[0] //lowest number
let NIBBLE = 4
let zeroCount = Int(firstValue) / NIBBLE

@ -99,7 +99,15 @@ class TokenCardRedemptionViewController: UIViewController, TokenVerifiableStatus
@objc
private func configureUI() {
let redeem = CreateRedeem(token: token)
let redeemData = redeem.redeemMessage(tokenIndices: viewModel.tokenHolder.indices)
let redeemData: (message: String, qrCode: String)
switch(token.type) {
case .nativeCryptocurrency, .erc20:
return
case .erc875:
redeemData = redeem.redeemMessage(indices: viewModel.tokenHolder.indices)
case .erc721, .erc721ForTickets:
redeemData = redeem.redeemMessage(tokenIds: viewModel.tokenHolder.tokens.map({ $0.id }))
}
switch session.account.type {
case .real(let account):
let decimalSignature = SignatureHelper.signatureAsDecimal(for: redeemData.message, account: account)!

@ -55,6 +55,9 @@ public struct Constants {
// FIFA hardcoded FIFA token address
static let ticketContractAddress = AlphaWallet.Address(string: "0xA66A3F08068174e8F005112A8b2c7A507a822335")!
static let ticketContractAddressRopsten = AlphaWallet.Address(string: "0xD8e5F58DE3933E1E35f9c65eb72cb188674624F3")!
// UEFA hardcoded addresses
static let uefaRopsten = AlphaWallet.Address(string: "0x439bf1B20841Fa5498C3471C202F438147b7e447")!
// Hardcoded discovery token address
static let discoveryContractAddress = AlphaWallet.Address(string: "0x8c0edb69ebf038ba0c7a4873e40fc09725064c2e")!

@ -17,7 +17,7 @@ class FetchAssetDefinitionsCoordinator: Coordinator {
for each in tokensDataStores.values {
contracts.append(contentsOf: each.enabledObject.filter {
switch $0.type {
case .erc20, .erc721, .erc875:
case .erc20, .erc721, .erc875, .erc721ForTickets:
return true
case .nativeCryptocurrency:
return false

@ -492,12 +492,16 @@ private class PrivateXMLHandler {
case .erc20:
actions = [.erc20Send, .erc20Receive]
case .erc721:
actions = [.nonFungibleTransfer]
if contractAddress.isUEFATicketContract {
actions = [.nftRedeem, .nonFungibleTransfer]
} else {
actions = [.nonFungibleTransfer]
}
case .erc875:
if contractAddress.isFifaTicketcontract {
actions = [.erc875Redeem, .erc875Sell, .nonFungibleTransfer]
actions = [.nftRedeem, .nftSell, .nonFungibleTransfer]
} else {
actions = [.erc875Sell, .nonFungibleTransfer]
actions = [.nftSell, .nonFungibleTransfer]
}
}
return actions.map { .init(type: $0) }

@ -0,0 +1,31 @@
// Copyright © 2019 Stormbird PTE. LTD.
import Foundation
import Result
import BigInt
class GetERC721ForTicketsBalanceCoordinator {
private let server: RPCServer
init(forServer server: RPCServer) {
self.server = server
}
func getERC721ForTicketsTokenBalance(for address: AlphaWallet.Address, contract: AlphaWallet.Address, completion: @escaping (Result<[String], AnyError>) -> Void) {
let function = GetERC721ForTicketsBalance()
callSmartContract(withServer: server, contract: contract, functionName: function.name, abiString: function.abi, parameters: [address.eip55String] as [AnyObject]).done { balanceResult in
let balances = self.adapt(balanceResult["0"])
completion(.success(balances))
}.catch {
completion(.failure(AnyError($0)))
}
}
private func adapt(_ values: Any?) -> [String] {
guard let array = values as? [BigUInt] else { return [] }
return array.map { each in
let value = each.serialize().hex()
return "0x\(value)"
}
}
}

@ -0,0 +1,23 @@
// Copyright © 2019 Stormbird PTE. LTD.
import Foundation
import Result
class GetIsERC721ForTicketsContractCoordinator {
private let server: RPCServer
init(forServer server: RPCServer) {
self.server = server
}
func getIsERC721ForTicketContract(for contract: AlphaWallet.Address, completion: @escaping (Result<Bool, AnyError>) -> Void) {
//TODO check ERC165 interface hash
//TODO update with production ready contract
let defaultTicketContract = Constants.uefaRopsten
if contract.sameContract(as: defaultTicketContract) {
completion(.success(true))
} else {
completion(.success(false))
}
}
}

@ -237,6 +237,9 @@ class SingleChainTokenCoordinator: Coordinator {
case .erc721:
//Handled in TokensDataStore.refreshBalanceForERC721Tokens()
break
case .erc721ForTickets:
//Handled in TokensDataStore.refreshBalanceForNonERC721TicketTokens()
break
case .nativeCryptocurrency:
break
}
@ -339,7 +342,7 @@ class SingleChainTokenCoordinator: Coordinator {
private func makeCoordinatorReadOnlyIfNotSupportedByOpenSeaERC721(coordinator: TokensCardCoordinator, token: TokenObject) {
switch token.type {
case .nativeCryptocurrency, .erc20, .erc875:
case .nativeCryptocurrency, .erc20, .erc875, .erc721ForTickets:
break
case .erc721:
//TODO is this check still necessary?
@ -515,7 +518,7 @@ extension SingleChainTokenCoordinator: TokenViewControllerDelegate {
switch transferType {
case .ERC20Token(let erc20Token, _, _):
token = erc20Token
case .dapp, .ERC721Token, .ERC875Token, .ERC875TokenOrder:
case .dapp, .ERC721Token, .ERC875Token, .ERC875TokenOrder, .ERC721ForTicketToken:
return
case .nativeCryptocurrency:
token = TokensDataStore.etherToken(forServer: session.server)
@ -525,7 +528,7 @@ extension SingleChainTokenCoordinator: TokenViewControllerDelegate {
switch action.type {
case .tokenScript:
showTokenInstanceActionView(forAction: action, fungibleTokenObject: token, viewController: viewController)
case .erc20Send, .erc20Receive, .erc875Redeem, .erc875Sell, .nonFungibleTransfer:
case .erc20Send, .erc20Receive, .nftRedeem, .nftSell, .nonFungibleTransfer:
//Couldn't have reached here
break
}
@ -648,6 +651,17 @@ func fetchContractDataFor(address: AlphaWallet.Address, storage: TokensDataStore
callCompletionFailed()
}
}
case .erc721ForTickets:
storage.getERC721ForTicketsBalance(for: address) { result in
switch result {
case .success(let balance):
completedBalance = balance
completion(.balance(balance))
callCompletionOnAllData()
case .failure:
callCompletionFailed()
}
}
case .erc20:
storage.getDecimals(for: address) { result in
switch result {

@ -228,7 +228,7 @@ extension TokensCoordinator: TokensViewControllerDelegate {
coordinator.show(fungibleToken: token, transferType: .ERC20Token(token, destination: nil, amount: nil))
case .erc721:
coordinator.showTokenList(for: .send(type: .ERC721Token(token)), token: token)
case .erc875:
case .erc875, .erc721ForTickets:
coordinator.showTokenList(for: .send(type: .ERC875Token(token)), token: token)
}
}

@ -21,7 +21,7 @@ class TokenAdaptor {
public func getTokenHolders(forWallet account: Wallet) -> [TokenHolder] {
switch token.type {
case .nativeCryptocurrency, .erc20, .erc875:
case .nativeCryptocurrency, .erc20, .erc875, .erc721ForTickets:
return getNotSupportedByOpenSeaTokenHolders(forWallet: account)
case .erc721:
let tokenType = OpenSeaSupportedNonFungibleTokenHandling(token: token)
@ -72,7 +72,7 @@ class TokenAdaptor {
} else {
break
}
case .erc721:
case .erc721, .erc721ForTickets:
return tokens.map { getTokenHolder(for: [$0]) }
}
var tokenHolders: [TokenHolder] = []

@ -7,8 +7,8 @@ struct TokenInstanceAction {
enum ActionType {
case erc20Send
case erc20Receive
case erc875Redeem
case erc875Sell
case nftRedeem
case nftSell
case nonFungibleTransfer
case tokenScript(contract: AlphaWallet.Address, title: String, viewHtml: String, attributes: [AttributeId: AssetAttribute], transactionFunction: FunctionOrigin?)
}
@ -18,9 +18,9 @@ struct TokenInstanceAction {
return R.string.localizable.send()
case .erc20Receive:
return R.string.localizable.receive()
case .erc875Redeem:
case .nftRedeem:
return R.string.localizable.aWalletTokenRedeemButtonTitle()
case .erc875Sell:
case .nftSell:
return R.string.localizable.aWalletTokenSellButtonTitle()
case .nonFungibleTransfer:
return R.string.localizable.aWalletTokenTransferButtonTitle()
@ -32,7 +32,7 @@ struct TokenInstanceAction {
switch type {
case .erc20Send, .erc20Receive:
return .init()
case .erc875Redeem, .erc875Sell, .nonFungibleTransfer:
case .nftRedeem, .nftSell, .nonFungibleTransfer:
return .init()
case .tokenScript(_, _, _, let attributes, _):
return attributes
@ -42,7 +42,7 @@ struct TokenInstanceAction {
switch type {
case .erc20Send, .erc20Receive:
return nil
case .erc875Redeem, .erc875Sell, .nonFungibleTransfer:
case .nftRedeem, .nftSell, .nonFungibleTransfer:
return nil
case .tokenScript(_, _, _, _, let transactionFunction):
return transactionFunction
@ -52,7 +52,7 @@ struct TokenInstanceAction {
switch type {
case .erc20Send, .erc20Receive:
return nil
case .erc875Redeem, .erc875Sell, .nonFungibleTransfer:
case .nftRedeem, .nftSell, .nonFungibleTransfer:
return nil
case .tokenScript(let contract, _, _, _, _):
return contract
@ -70,9 +70,9 @@ struct TokenInstanceAction {
switch type {
case .erc20Send, .erc20Receive:
self.viewHtml = ""
case .erc875Redeem:
case .nftRedeem:
self.viewHtml = ""
case .erc875Sell:
case .nftSell:
self.viewHtml = ""
case .nonFungibleTransfer:
self.viewHtml = ""

@ -109,11 +109,11 @@ class TokenObject: Object {
}
}
var isERC721: Bool {
var isERC721AndNotForTickets: Bool {
switch type {
case .erc721:
return true
case .nativeCryptocurrency, .erc20, .erc875:
case .nativeCryptocurrency, .erc20, .erc875, .erc721ForTickets:
return false
}
}
@ -128,7 +128,7 @@ func isNonZeroBalance(_ balance: String) -> Bool {
}
func isZeroBalance(_ balance: String) -> Bool {
if balance == Constants.nullTokenId {
if balance == Constants.nullTokenId || balance == "0" {
return true
}
return false

@ -7,4 +7,5 @@ enum TokenType: String {
case erc20 = "ERC20"
case erc875 = "ERC875"
case erc721 = "ERC721"
case erc721ForTickets = "ERC721ForTickets"
}

@ -36,6 +36,10 @@ class TokensDataStore {
return GetERC875BalanceCoordinator(forServer: server)
}()
private lazy var getERC721ForTicketsBalanceCoordinator: GetERC721ForTicketsBalanceCoordinator = {
return GetERC721ForTicketsBalanceCoordinator(forServer: server)
}()
private lazy var getIsERC875ContractCoordinator: GetIsERC875ContractCoordinator = {
return GetIsERC875ContractCoordinator(forServer: server)
}()
@ -44,6 +48,10 @@ class TokensDataStore {
return GetERC721BalanceCoordinator(forServer: server)
}()
private lazy var getIsERC721ForTicketsContractCoordinator: GetIsERC721ForTicketsContractCoordinator = {
return GetIsERC721ForTicketsContractCoordinator(forServer: server)
}()
private lazy var getIsERC721ContractCoordinator: GetIsERC721ContractCoordinator = {
return GetIsERC721ContractCoordinator(forServer: server)
}()
@ -109,14 +117,14 @@ class TokensDataStore {
static func etherToken(forServer server: RPCServer) -> TokenObject {
return TokenObject(
contract: Constants.nativeCryptoAddressInDatabase,
server: server,
name: server.name,
symbol: server.symbol,
decimals: server.decimals,
value: "0",
isCustom: false,
type: .nativeCryptocurrency
contract: Constants.nativeCryptoAddressInDatabase,
server: server,
name: server.name,
symbol: server.symbol,
decimals: server.decimals,
value: "0",
isCustom: false,
type: .nativeCryptocurrency
)
}
@ -272,6 +280,13 @@ class TokensDataStore {
}
}
func getERC721ForTicketsBalance(for address: AlphaWallet.Address,
completion: @escaping (ResultResult<[String], AnyError>.t) -> Void) {
getERC721ForTicketsBalanceCoordinator.getERC721ForTicketsTokenBalance(for: account.address, contract: address) { result in
completion(result)
}
}
func getIsERC875Contract(for address: AlphaWallet.Address,
completion: @escaping (ResultResult<Bool, AnyError>.t) -> Void) {
getIsERC875ContractCoordinator.getIsERC875Contract(for: address) { result in
@ -322,12 +337,22 @@ class TokensDataStore {
}
getIsERC721ContractCoordinator.getIsERC721Contract(for: address) { [weak self] result in
guard self != nil else { return }
guard let strongSelf = self else { return }
switch result {
case .success(let isERC721):
if isERC721 {
completion(.erc721)
return
strongSelf.getIsERC721ForTicketsContractCoordinator.getIsERC721ForTicketContract(for: address) { [weak self] result in
switch result {
case .success(let isERC721ForTickets):
if isERC721ForTickets {
completion(.erc721ForTickets)
} else {
completion(.erc721)
}
case .failure:
completion(.erc721)
}
}
} else {
knownToBeNotERC721 = true
}
@ -354,14 +379,14 @@ class TokensDataStore {
//TODO While we might want to improve it such as enabledObject still returning Realm's streaming list instead of a Swift array and filtering using predicates, it doesn't affect much here, yet.
let etherToken = TokensDataStore.etherToken(forServer: server)
let updateTokens = enabledObject.filter { $0 != etherToken }
let nonERC721Tokens = updateTokens.filter { !$0.isERC721 }
let erc721Tokens = updateTokens.filter { $0.isERC721 }
refreshBalanceForNonERC721Tokens(tokens: nonERC721Tokens)
let nonERC721Tokens = updateTokens.filter { !$0.isERC721AndNotForTickets }
let erc721Tokens = updateTokens.filter { $0.isERC721AndNotForTickets }
refreshBalanceForTokensThatAreNotNonTicket721(tokens: nonERC721Tokens)
refreshBalanceForERC721Tokens(tokens: erc721Tokens)
}
private func refreshBalanceForNonERC721Tokens(tokens: [TokenObject]) {
assert(!tokens.contains { $0.isERC721 })
private func refreshBalanceForTokensThatAreNotNonTicket721(tokens: [TokenObject]) {
assert(!tokens.contains { $0.isERC721AndNotForTickets })
var count = 0
//So we refresh the UI. Possible improvement is to refresh earlier, but still refresh at the end
func incrementCountAndUpdateDelegate() {
@ -397,14 +422,24 @@ class TokensDataStore {
}
})
case .erc721:
//We'll check with OpenSea below and an ERC721 token isn't found there, then we get the balance of each token ourselves
incrementCountAndUpdateDelegate()
break
case .erc721ForTickets:
getERC721ForTicketsBalance(for: tokenObject.contractAddress, completion: { [weak self] result in
defer { incrementCountAndUpdateDelegate() }
guard let strongSelf = self else { return }
switch result {
case .success(let balance):
strongSelf.update(token: tokenObject, action: .nonFungibleBalance(balance))
case .failure:
break
}
})
}
}
}
private func refreshBalanceForERC721Tokens(tokens: [TokenObject]) {
assert(!tokens.contains { !$0.isERC721 })
assert(!tokens.contains { !$0.isERC721AndNotForTickets })
guard OpenSea.isServerSupported(server) else { return }
getTokensFromOpenSea().done { [weak self] contractToOpenSeaNonFungibles in
guard let strongSelf = self else { return }
@ -445,7 +480,7 @@ class TokensDataStore {
if let tokenObject = tokens.first(where: { $0.contractAddress.sameContract(as: contract) }) {
switch tokenObject.type {
case .nativeCryptocurrency, .erc721, .erc875:
case .nativeCryptocurrency, .erc721, .erc875, .erc721ForTickets:
break
case .erc20:
strongSelf.update(token: tokenObject, action: .type(.erc721))
@ -501,14 +536,14 @@ class TokensDataStore {
func addCustom(token: ERCToken) {
let newToken = TokenObject(
contract: token.contract,
server: token.server,
name: token.name,
symbol: token.symbol,
decimals: token.decimals,
value: "0",
isCustom: true,
type: token.type
contract: token.contract,
server: token.server,
name: token.name,
symbol: token.symbol,
decimals: token.decimals,
value: "0",
isCustom: true,
type: token.type
)
token.balance.forEach { balance in
newToken.balance.append(TokenBalance(balance: balance))
@ -732,4 +767,4 @@ class TokensDataStore {
pricesTimer.invalidate()
ethTimer.invalidate()
}
}
}

@ -243,7 +243,7 @@ class NewTokenViewController: UIViewController, CanScanQRCode {
balanceTextField.isHidden = true
decimalsTextField.label.isHidden = false
balanceTextField.label.isHidden = true
case .erc721, .erc875:
case .erc721, .erc875, .erc721ForTickets:
decimalsTextField.isHidden = true
balanceTextField.isHidden = false
decimalsTextField.label.isHidden = true
@ -272,7 +272,7 @@ class NewTokenViewController: UIViewController, CanScanQRCode {
displayError(title: R.string.localizable.decimals(), error: ValidationError(msg: R.string.localizable.warningFieldRequired()))
return false
}
case .erc721, .erc875:
case .erc721, .erc875, .erc721ForTickets:
guard !balanceTextField.value.trimmed.isEmpty else {
displayError(title: R.string.localizable.balance(), error: ValidationError(msg: R.string.localizable.warningFieldRequired()))
return false

@ -40,6 +40,8 @@ class TokenInstanceActionViewController: UIViewController, TokenVerifiableStatus
return false
case .erc875:
return false
case .erc721ForTickets:
return false
}
}

@ -173,9 +173,9 @@ class TokenInstanceViewController: UIViewController, TokenVerifiableStatusViewCo
case .erc20Send, .erc20Receive:
//TODO when we support TokenScript views for ERC20s, we need to perform the action here
break
case .erc875Redeem:
case .nftRedeem:
redeem()
case .erc875Sell:
case .nftSell:
sell()
case .nonFungibleTransfer:
transfer()

@ -163,7 +163,7 @@ class TokenViewController: UIViewController {
if let viewModel = self.viewModel {
configure(viewModel: viewModel)
}
case .ERC875Token(_), .ERC875TokenOrder(_), .ERC721Token(_), .dapp(_, _):
case .ERC875Token(_), .ERC875TokenOrder(_), .ERC721Token(_), .ERC721ForTicketToken, .dapp(_, _):
break
}
}
@ -186,7 +186,7 @@ class TokenViewController: UIViewController {
send()
case .erc20Receive:
receive()
case .erc875Redeem, .erc875Sell, .nonFungibleTransfer:
case .nftRedeem, .nftSell, .nonFungibleTransfer:
break
case .tokenScript:
delegate?.didTap(action: action, transferType: transferType, viewController: self)

@ -219,9 +219,9 @@ class TokensCardViewController: UIViewController, TokenVerifiableStatusViewContr
switch action.type {
case .erc20Send, .erc20Receive:
break
case .erc875Redeem:
case .nftRedeem:
redeem()
case .erc875Sell:
case .nftSell:
sell()
case .nonFungibleTransfer:
transfer()

@ -404,7 +404,7 @@ extension TokensViewController: UITableViewDataSource {
let cell = tableView.dequeueReusableCell(withIdentifier: FungibleTokenViewCell.identifier, for: indexPath) as! FungibleTokenViewCell
cell.configure(viewModel: .init(token: token, server: server, assetDefinitionStore: assetDefinitionStore))
return cell
case .erc721:
case .erc721, .erc721ForTickets:
let cell = tableView.dequeueReusableCell(withIdentifier: NonFungibleTokenViewCell.identifier, for: indexPath) as! NonFungibleTokenViewCell
cell.configure(viewModel: .init(token: token, server: server, assetDefinitionStore: assetDefinitionStore))
return cell

@ -15,9 +15,9 @@ struct TokenInstanceViewModel {
return actionsFromTokenScript
} else {
switch token.type {
case .erc875:
case .erc875, .erc721ForTickets:
return [
.init(type: .erc875Sell),
.init(type: .nftSell),
.init(type: .nonFungibleTransfer)
]
case .erc721:

@ -17,7 +17,7 @@ struct TokenViewControllerViewModel {
return TokensDataStore.etherToken(forServer: session.server)
case .ERC20Token(let token, _, _):
return token
case .ERC875Token, .ERC875TokenOrder, .ERC721Token, .dapp:
case .ERC875Token, .ERC875TokenOrder, .ERC721Token, .ERC721ForTicketToken, .dapp:
return nil
}
}
@ -34,6 +34,8 @@ struct TokenViewControllerViewModel {
return []
case .erc721:
return []
case .erc721ForTickets:
return []
case .nativeCryptocurrency:
//TODO .erc20Send and .erc20Receive names aren't appropriate
return [
@ -48,7 +50,7 @@ struct TokenViewControllerViewModel {
}
} else {
switch token.type {
case .erc875, .erc721, .erc20:
case .erc875, .erc721, .erc20, .erc721ForTickets:
return actionsFromTokenScript
case .nativeCryptocurrency:
//TODO we should support retrieval of XML (and XMLHandler) based on address + server. For now, this is only important for native cryptocurrency. So might be ok to check like this for now
@ -100,7 +102,7 @@ struct TokenViewControllerViewModel {
}})
.filter({ $0.operation?.contract.flatMap { token.contractAddress.sameContract(as: $0) } ?? false })
.prefix(3))
case .ERC875Token, .ERC875TokenOrder, .ERC721Token, .dapp:
case .ERC875Token, .ERC875TokenOrder, .ERC721Token, .ERC721ForTicketToken, .dapp:
self.recentTransactions = []
}
}

@ -21,9 +21,9 @@ struct TokensCardViewModel {
let actionsFromTokenScript = xmlHandler.actions
if actionsFromTokenScript.isEmpty {
switch token.type {
case .erc875:
case .erc875, .erc721ForTickets:
return [
.init(type: .erc875Sell),
.init(type: .nftSell),
.init(type: .nonFungibleTransfer)
]
case .erc721:

@ -389,7 +389,7 @@ extension TokensCardCoordinator: TokensCardViewControllerDelegate {
case .erc721:
let vc = makeTransferTokensCardViaWalletAddressViewController(token: token, for: tokenHolder, paymentFlow: type)
viewController.navigationController?.pushViewController(vc, animated: true)
case .erc875:
case .erc875, .erc721ForTickets:
showEnterQuantityViewControllerForTransfer(token: token, for: tokenHolder, forPaymentFlow: type, in: viewController)
case .nativeCryptocurrency, .erc20:
break
@ -418,7 +418,7 @@ extension TokensCardCoordinator: TokensCardViewControllerDelegate {
switch action.type {
case .tokenScript:
showTokenInstanceActionView(forAction: action, tokenHolder: tokenHolder, viewController: viewController)
case .erc20Send, .erc20Receive, .erc875Redeem, .erc875Sell, .nonFungibleTransfer:
case .erc20Send, .erc20Receive, .nftRedeem, .nftSell, .nonFungibleTransfer:
//Couldn't have reached here
break
}
@ -439,7 +439,7 @@ extension TokensCardCoordinator: TokenInstanceViewControllerDelegate {
case .erc721:
let vc = makeTransferTokensCardViaWalletAddressViewController(token: token, for: tokenHolder, paymentFlow: paymentFlow)
viewController.navigationController?.pushViewController(vc, animated: true)
case .erc875:
case .erc875, .erc721ForTickets:
showEnterQuantityViewControllerForTransfer(token: token, for: tokenHolder, forPaymentFlow: paymentFlow, in: viewController)
case .nativeCryptocurrency, .erc20:
break

@ -81,6 +81,8 @@ class TransactionConfigurator {
return token.contractAddress
case .ERC721Token(let token):
return token.contractAddress
case .ERC721ForTicketToken(let token):
return token.contractAddress
}
}()
let request = EstimateGasRequest(
@ -197,8 +199,7 @@ class TransactionConfigurator {
} catch {
completion(.failure(AnyError(Web3Error(description: "malformed tx"))))
}
case .ERC721Token(let token):
case .ERC721Token(let token), .ERC721ForTicketToken(let token):
do {
let function: Function
let parameters: [Any]
@ -245,6 +246,7 @@ class TransactionConfigurator {
case .ERC875Token: return 0
case .ERC875TokenOrder: return transaction.value
case .ERC721Token: return 0
case .ERC721ForTicketToken: return 0
}
}()
let address: AlphaWallet.Address? = {
@ -254,6 +256,7 @@ class TransactionConfigurator {
case .ERC875Token(let token): return token.contractAddress
case .ERC875TokenOrder(let token): return token.contractAddress
case .ERC721Token(let token): return token.contractAddress
case .ERC721ForTicketToken(let token): return token.contractAddress
}
}()
let signTransaction = UnsignedTransaction(

@ -94,6 +94,7 @@ class SendCoordinator: Coordinator {
case .ERC875Token: break
case .ERC875TokenOrder: break
case .ERC721Token: break
case .ERC721ForTicketToken: break
case .dapp: break
}
controller.delegate = self

@ -20,6 +20,8 @@ enum TransferType {
return .ERC875Token(token)
case .erc721:
return .ERC721Token(token)
case .erc721ForTickets:
return .ERC721ForTicketToken(token)
}
}()
}
@ -29,6 +31,7 @@ enum TransferType {
case ERC875Token(TokenObject)
case ERC875TokenOrder(TokenObject)
case ERC721Token(TokenObject)
case ERC721ForTicketToken(TokenObject)
case dapp(TokenObject, DAppRequester)
}
@ -47,6 +50,8 @@ extension TransferType {
return token.symbol
case .ERC721Token(let token):
return token.symbol
case .ERC721ForTicketToken(let token):
return token.symbol
}
}
@ -64,6 +69,8 @@ extension TransferType {
return token.server
case .ERC721Token(let token):
return token.server
case .ERC721ForTicketToken(let token):
return token.server
}
}
@ -79,6 +86,8 @@ extension TransferType {
return token.contractAddress
case .ERC721Token(let token):
return token.contractAddress
case .ERC721ForTicketToken(let token):
return token.contractAddress
case .dapp(let token, _):
return token.contractAddress
}

@ -187,7 +187,7 @@ class SendViewController: UIViewController, CanScanQRCode {
}
amountTextField.alternativeAmountLabel.isHidden = true
amountTextField.isFiatButtonHidden = true
case .ERC875Token, .ERC875TokenOrder, .ERC721Token, .dapp:
case .ERC875Token, .ERC875TokenOrder, .ERC721Token, .ERC721ForTicketToken, .dapp:
amountTextField.alternativeAmountLabel.isHidden = true
amountTextField.isFiatButtonHidden = true
}
@ -225,6 +225,8 @@ class SendViewController: UIViewController, CanScanQRCode {
return EtherNumberFormatter.full.number(from: amountString, decimals: token.decimals)
case .ERC721Token(let token):
return EtherNumberFormatter.full.number(from: amountString, decimals: token.decimals)
case .ERC721ForTicketToken(let token):
return EtherNumberFormatter.full.number(from: amountString, decimals: token.decimals)
}
}()
guard let value = parsedValue else {
@ -294,7 +296,7 @@ class SendViewController: UIViewController, CanScanQRCode {
headerViewModel.contractAddress = token.contractAddress
configure(viewModel: self.viewModel, shouldConfigureBalance: false)
case .ERC875Token, .ERC875TokenOrder, .ERC721Token, .dapp:
case .ERC875Token, .ERC875TokenOrder, .ERC721Token, .ERC721ForTicketToken, .dapp:
break
}
}

@ -33,6 +33,7 @@ struct ConfigureTransactionViewModel {
case .ERC875Token: return true
case .ERC875TokenOrder: return true
case .ERC721Token: return true
case .ERC721ForTicketToken: return true
}
}
}

@ -118,6 +118,10 @@ struct ConfirmPaymentDetailsViewModel {
return amountAttributedText(
string: fullFormatter.string(from: transaction.value, decimals: token.decimals)
)
case .ERC721ForTicketToken(let token):
return amountAttributedText(
string: fullFormatter.string(from: transaction.value, decimals: token.decimals)
)
}
}

@ -35,6 +35,8 @@ struct SendViewModel {
return token
case .ERC721Token(let token):
return token
case .ERC721ForTicketToken(let token):
return token
case .dapp:
return nil
}

@ -106,6 +106,8 @@ extension BalanceTitleView {
case .ERC875TokenOrder: break
case .ERC721Token(let token):
view.viewModel = BalanceTokenViewModel(token: token)
case .ERC721ForTicketToken(let token):
view.viewModel = BalanceTokenViewModel(token: token)
}
session.refresh(.ethBalance)
return view

@ -12,11 +12,11 @@ class CreateRedeemTests: XCTestCase {
//when loading qr only include signature in decimal and the indices
func testGenerateRedeem() {
var indices = [UInt16]()
indices.append(1)
indices.append(2)
var token = [BigUInt]()
token.append(1)
token.append(2)
let account = keyStore.createAccount()
let message = CreateRedeem(token: TokenObject()).redeemMessage(tokenIndices: indices).0
let message = CreateRedeem(token: TokenObject()).redeemMessage(tokenIds: token).0
print(message)
let data = message.data(using: String.Encoding.utf8)

Loading…
Cancel
Save