Add default TokenScript action for opening Uniswap for supported ERC20 tokens

pull/2114/head
James Sangalli 4 years ago committed by Vladyslav shepitko
parent 570b27f086
commit 7b82bd897b
  1. 9
      AlphaWallet.xcodeproj/project.pbxproj
  2. 9
      AlphaWallet/Browser/Coordinators/DappBrowserCoordinator.swift
  3. 15
      AlphaWallet/InCoordinator.swift
  4. 1
      AlphaWallet/Localization/en.lproj/Localizable.strings
  5. 1
      AlphaWallet/Localization/es.lproj/Localizable.strings
  6. 1
      AlphaWallet/Localization/ja.lproj/Localizable.strings
  7. 1
      AlphaWallet/Localization/ko.lproj/Localizable.strings
  8. 1
      AlphaWallet/Localization/zh-Hans.lproj/Localizable.strings
  9. 37
      AlphaWallet/Tokens/Coordinators/SingleChainTokenCoordinator.swift
  10. 6
      AlphaWallet/Tokens/Coordinators/TokensCoordinator.swift
  11. 13
      AlphaWallet/Tokens/Types/TokenInstanceAction.swift
  12. 103
      AlphaWallet/Tokens/Types/UniswapERC20Token.swift
  13. 78
      AlphaWallet/Tokens/Types/UniswapHolder.swift
  14. 2
      AlphaWallet/Tokens/ViewControllers/TokenInstanceViewController.swift
  15. 7
      AlphaWallet/Tokens/ViewControllers/TokenViewController.swift
  16. 2
      AlphaWallet/Tokens/ViewControllers/TokensCardViewController.swift
  17. 55
      AlphaWallet/Tokens/ViewModels/TokenViewControllerViewModel.swift
  18. 2
      AlphaWallet/Transactions/Coordinators/TokensCardCoordinator.swift

@ -658,6 +658,7 @@
8499DAB3D06965FDC1E8FCAF /* GetInterfaceSupported165Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8499D1861F30C1360B1047CE /* GetInterfaceSupported165Coordinator.swift */; };
873F8063246E8E3E00EEE5EF /* SelectCurrencyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 873F8062246E8E3E00EEE5EF /* SelectCurrencyButton.swift */; };
874DED0C24C05E88006C8FCE /* TransactionConfirmationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874DED0A24C05E88006C8FCE /* TransactionConfirmationViewModel.swift */; };
8750F91B24EFEC0300E19DFF /* UniswapHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8750F91A24EFEC0300E19DFF /* UniswapHolder.swift */; };
8769888D24C6ED04002BF62B /* TransactionInProgressCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8769888C24C6ED04002BF62B /* TransactionInProgressCoordinator.swift */; };
8782035D2431E66600792F12 /* FilterTokensCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8782035C2431E66600792F12 /* FilterTokensCoordinator.swift */; };
8782035F2431FBC300792F12 /* ShowAddHideTokensViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8782035E2431FBC300792F12 /* ShowAddHideTokensViewModel.swift */; };
@ -677,6 +678,8 @@
87D1757A24ADAF07002130D2 /* BlockchainTagLabelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87D1757924ADAF07002130D2 /* BlockchainTagLabelViewModel.swift */; };
87D175EB24AEF8B5002130D2 /* UITableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87D175EA24AEF8B4002130D2 /* UITableView.swift */; };
87D175ED24AF1565002130D2 /* KeyboardChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87D175EC24AF1565002130D2 /* KeyboardChecker.swift */; };
87E2554A24F4E98000F025F7 /* UniswapERC20Token.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87E2554924F4E98000F025F7 /* UniswapERC20Token.swift */; };
87ED8F93248534E30005C69B /* SwitchTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87ED8F92248534E30005C69B /* SwitchTableViewCell.swift */; };
87ED8F95248535400005C69B /* SwitchTableViewCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87ED8F94248535400005C69B /* SwitchTableViewCellViewModel.swift */; };
87ED8F97248540F90005C69B /* AdvancedSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87ED8F96248540F90005C69B /* AdvancedSettingsViewController.swift */; };
@ -1405,6 +1408,7 @@
8499DFC3A9D0B1E42D74905E /* GetERC721ForTicketsBalanceCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetERC721ForTicketsBalanceCoordinator.swift; sourceTree = "<group>"; };
873F8062246E8E3E00EEE5EF /* SelectCurrencyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectCurrencyButton.swift; sourceTree = "<group>"; };
874DED0A24C05E88006C8FCE /* TransactionConfirmationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionConfirmationViewModel.swift; sourceTree = "<group>"; };
8750F91A24EFEC0300E19DFF /* UniswapHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniswapHolder.swift; sourceTree = "<group>"; };
8769888C24C6ED04002BF62B /* TransactionInProgressCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionInProgressCoordinator.swift; sourceTree = "<group>"; };
8782035C2431E66600792F12 /* FilterTokensCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterTokensCoordinator.swift; sourceTree = "<group>"; };
8782035E2431FBC300792F12 /* ShowAddHideTokensViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowAddHideTokensViewModel.swift; sourceTree = "<group>"; };
@ -1424,6 +1428,7 @@
87D1757924ADAF07002130D2 /* BlockchainTagLabelViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockchainTagLabelViewModel.swift; sourceTree = "<group>"; };
87D175EA24AEF8B4002130D2 /* UITableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableView.swift; sourceTree = "<group>"; };
87D175EC24AF1565002130D2 /* KeyboardChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardChecker.swift; sourceTree = "<group>"; };
87E2554924F4E98000F025F7 /* UniswapERC20Token.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniswapERC20Token.swift; sourceTree = "<group>"; };
87ED8F92248534E30005C69B /* SwitchTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchTableViewCell.swift; sourceTree = "<group>"; };
87ED8F94248535400005C69B /* SwitchTableViewCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchTableViewCellViewModel.swift; sourceTree = "<group>"; };
87ED8F96248540F90005C69B /* AdvancedSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsViewController.swift; sourceTree = "<group>"; };
@ -2139,6 +2144,8 @@
5E7C7957AA1BC0B5BD6E98FF /* TransactionCollection.swift */,
5E7C7B43816C35C3FE2EFFBE /* TokenInstanceAction.swift */,
5E7C74822F5F71748184F6C1 /* EventInstance.swift */,
8750F91A24EFEC0300E19DFF /* UniswapHolder.swift */,
87E2554924F4E98000F025F7 /* UniswapERC20Token.swift */,
);
path = Types;
sourceTree = "<group>";
@ -4278,6 +4285,7 @@
5E7C78B3FD5CA87E395E1861 /* OnboardingPageStyle.swift in Sources */,
5E7C797BE2C8DB7EF6F217B3 /* OnboardingPage.swift in Sources */,
87ED8FA92488E4430005C69B /* SendViewSectionHeader.swift in Sources */,
8750F91B24EFEC0300E19DFF /* UniswapHolder.swift in Sources */,
5E7C76F8CB67466725C590CE /* FungibleTokenViewCellViewModel.swift in Sources */,
5E7C78407F6DCB0EDD562DF6 /* NonFungibleTokenViewCellViewModel.swift in Sources */,
5E7C7CE5CA19183FCED8C907 /* TokensViewModel.swift in Sources */,
@ -4397,6 +4405,7 @@
5E7C7FDCAE5ED2EEE02CE661 /* OpenSeaNonFungibleTokenCardRowView.swift in Sources */,
87A3022224C021F4000DF32E /* ConfirmationTransitionController.swift in Sources */,
5E7C749B7C5CBC729B7E256F /* OpenSeaNonFungibleTokenTraitCell.swift in Sources */,
87E2554A24F4E98000F025F7 /* UniswapERC20Token.swift in Sources */,
5E7C773E3E3BBEB65C51DF2A /* UIStackView.swift in Sources */,
5E7C760D5AF93B79BB9BDB5A /* OpenSeaNonFungibleTokenAttributeCellViewModel.swift in Sources */,
873F8063246E8E3E00EEE5EF /* SelectCurrencyButton.swift in Sources */,

@ -193,7 +193,7 @@ final class DappBrowserCoordinator: NSObject, Coordinator {
navigationController.present(coordinator.navigationController, animated: true, completion: nil)
}
func open(url: URL, animated: Bool = true) {
func open(url: URL, animated: Bool = true, forceReload: Bool = false) {
//If users tap on the verified button in the import MagicLink UI, we don't want to treat it as a MagicLink to import and show the UI again. Just open in browser. This check means when we tap MagicLinks in browserOnly mode, the import UI doesn't show up; which is probably acceptable
if !browserOnly && isMagicLink(url) {
delegate?.importUniversalLink(url: url, forCoordinator: self)
@ -211,8 +211,13 @@ final class DappBrowserCoordinator: NSObject, Coordinator {
if browserOnly {
browserNavBar?.makeBrowserOnly()
}
browserViewController.goTo(url: url)
}
//FIXME: for some reasons webView doesnt reload itself when load(URLRequest) method is called. we need to force reload it
if forceReload {
browserViewController.reload()
}
}
func signMessage(with type: SignMessageType, account: EthereumAccount, callbackID: Int) {
nativeCryptoCurrencyBalanceView.hide()

@ -17,6 +17,7 @@ enum Tabs {
case wallet
case alphaWalletSettings
case transactions
case browser
var className: String {
switch self {
@ -26,6 +27,8 @@ enum Tabs {
return String(describing: TransactionsViewController.self)
case .alphaWalletSettings:
return String(describing: SettingsViewController.self)
case .browser:
return String(describing: DappsHomeViewController.self)
}
}
}
@ -445,6 +448,7 @@ class InCoordinator: NSObject, Coordinator {
guard let viewControllers = tabBarController?.viewControllers else {
return
}
for controller in viewControllers {
if let nav = controller as? UINavigationController {
if nav.viewControllers[0].className == selectTab.className {
@ -758,6 +762,17 @@ extension InCoordinator: SettingsCoordinatorDelegate {
}
extension InCoordinator: TokensCoordinatorDelegate {
func didPressErc20ExchangeOnUniswap(for holder: UniswapHolder, in coordinator: TokensCoordinator) {
guard let dappBrowserCoordinator = dappBrowserCoordinator, let url = holder.url else { return }
coordinator.navigationController.popViewController(animated: false)
showTab(.browser)
dappBrowserCoordinator.open(url: url, animated: true, forceReload: true)
}
func didPress(for type: PaymentFlow, server: RPCServer, in coordinator: TokensCoordinator) {
showPaymentFlow(for: type, server: server)
}

@ -459,3 +459,4 @@
"a.wallet.token.transaction.inProgress.title" = "Transaction in Progress";
"a.wallet.token.transaction.inProgress.subtitle" = "The transaction is sent to the Ethereum\nblockchain. It might take few minutes\nto be confirmed by miners.";
"a.wallet.token.transaction.inProgress.confirm" = "OK, great!";
"a.wallet.token.erc20ExchangeOnUniswap.button.title" = "Exchange with Uniswap";

@ -458,3 +458,4 @@
"a.wallet.token.transaction.inProgress.title" = "Transaction in Progress";
"a.wallet.token.transaction.inProgress.subtitle" = "The transaction is sent to the Ethereum\nblockchain. It might take few minutes\nto be confirmed by miners.";
"a.wallet.token.transaction.inProgress.confirm" = "OK, great!";
"a.wallet.token.erc20ExchangeOnUniswap.button.title" = "Exchange with Uniswap";

@ -459,3 +459,4 @@
"a.wallet.token.transaction.inProgress.title" = "Transaction in Progress";
"a.wallet.token.transaction.inProgress.subtitle" = "The transaction is sent to the Ethereum\nblockchain. It might take few minutes\nto be confirmed by miners.";
"a.wallet.token.transaction.inProgress.confirm" = "OK, great!";
"a.wallet.token.erc20ExchangeOnUniswap.button.title" = "Exchange with Uniswap";

@ -459,3 +459,4 @@
"a.wallet.token.transaction.inProgress.title" = "Transaction in Progress";
"a.wallet.token.transaction.inProgress.subtitle" = "The transaction is sent to the Ethereum\nblockchain. It might take few minutes\nto be confirmed by miners.";
"a.wallet.token.transaction.inProgress.confirm" = "OK, great!";
"a.wallet.token.erc20ExchangeOnUniswap.button.title" = "Exchange with Uniswap";

@ -458,3 +458,4 @@
"a.wallet.token.transaction.inProgress.title" = "Transaction in Progress";
"a.wallet.token.transaction.inProgress.subtitle" = "The transaction is sent to the Ethereum\nblockchain. It might take few minutes\nto be confirmed by miners.";
"a.wallet.token.transaction.inProgress.confirm" = "OK, great!";
"a.wallet.token.erc20ExchangeOnUniswap.button.title" = "Exchange with Uniswap";

@ -20,6 +20,7 @@ enum ContractData {
protocol SingleChainTokenCoordinatorDelegate: class, CanOpenURL {
func tokensDidChange(inCoordinator coordinator: SingleChainTokenCoordinator)
func didPressErc20ExchangeOnUniswap(for holder: UniswapHolder, in coordinator: SingleChainTokenCoordinator)
func didPress(for type: PaymentFlow, inCoordinator coordinator: SingleChainTokenCoordinator)
func didTap(transaction: Transaction, inViewController viewController: UIViewController, in coordinator: SingleChainTokenCoordinator)
func didPostTokenScriptTransaction(_ transaction: SentTransaction, in coordinator: SingleChainTokenCoordinator)
@ -524,7 +525,41 @@ extension SingleChainTokenCoordinator: TokensCardCoordinatorDelegate {
}
}
extension TransferType {
func uniswapHolder(theme: UniswapHolder.Theme) -> UniswapHolder {
switch self {
case .ERC20Token(let token, _, _):
return .init(input: .input(token.contractAddress), theme: theme)
case .ERC721Token, .ERC721ForTicketToken, .ERC875TokenOrder, .ERC875Token, .dapp, .nativeCryptocurrency:
return .init(input: .none, theme: theme)
}
}
}
extension UITraitCollection {
var uniswapTheme: UniswapHolder.Theme {
if #available(iOS 12.0, *) {
switch userInterfaceStyle {
case .dark:
return .dark
case .light, .unspecified:
return .light
}
} else {
return .light
}
}
}
extension SingleChainTokenCoordinator: TokenViewControllerDelegate {
func didTapErc20ExchangeOnUniswap(forTransferType transferType: TransferType, inViewController viewController: TokenViewController) {
let theme = viewController.traitCollection.uniswapTheme
delegate?.didPressErc20ExchangeOnUniswap(for: transferType.uniswapHolder(theme: theme), in: self)
}
func didTapSend(forTransferType transferType: TransferType, inViewController viewController: TokenViewController) {
delegate?.didPress(for: .send(type: transferType), inCoordinator: self)
}
@ -552,7 +587,7 @@ extension SingleChainTokenCoordinator: TokenViewControllerDelegate {
switch action.type {
case .tokenScript:
showTokenInstanceActionView(forAction: action, fungibleTokenObject: token, viewController: viewController)
case .erc20Send, .erc20Receive, .nftRedeem, .nftSell, .nonFungibleTransfer:
case .erc20Send, .erc20Receive, .nftRedeem, .nftSell, .nonFungibleTransfer, .erc20ExchangeOnUniswap:
//Couldn't have reached here
break
}

@ -5,6 +5,7 @@ import UIKit
import PromiseKit
protocol TokensCoordinatorDelegate: class, CanOpenURL {
func didPressErc20ExchangeOnUniswap(for holder: UniswapHolder, in coordinator: TokensCoordinator)
func didPress(for type: PaymentFlow, server: RPCServer, in coordinator: TokensCoordinator)
func didTap(transaction: Transaction, inViewController viewController: UIViewController, in coordinator: TokensCoordinator)
func openConsole(inCoordinator coordinator: TokensCoordinator)
@ -197,6 +198,11 @@ func -<T: Equatable>(left: [T], right: [T]) -> [T] {
}
extension TokensCoordinator: SingleChainTokenCoordinatorDelegate {
func didPressErc20ExchangeOnUniswap(for holder: UniswapHolder, in coordinator: SingleChainTokenCoordinator) {
delegate?.didPressErc20ExchangeOnUniswap(for: holder, in: self)
}
func tokensDidChange(inCoordinator coordinator: SingleChainTokenCoordinator) {
tokensViewController.fetch()
}

@ -12,6 +12,7 @@ struct TokenInstanceAction {
case nftSell
case nonFungibleTransfer
case tokenScript(contract: AlphaWallet.Address, title: String, viewHtml: (html: String, style: String), attributes: [AttributeId: AssetAttribute], transactionFunction: FunctionOrigin?, selection: TokenScriptSelection?)
case erc20ExchangeOnUniswap
}
var name: String {
switch type {
@ -27,11 +28,13 @@ struct TokenInstanceAction {
return R.string.localizable.aWalletTokenTransferButtonTitle()
case .tokenScript(_, let title, _, _, _, _):
return title
case .erc20ExchangeOnUniswap:
return R.string.localizable.aWalletTokenErc20ExchangeOnUniswapButtonTitle()
}
}
var attributes: [AttributeId: AssetAttribute] {
switch type {
case .erc20Send, .erc20Receive:
case .erc20Send, .erc20Receive, .erc20ExchangeOnUniswap:
return .init()
case .nftRedeem, .nftSell, .nonFungibleTransfer:
return .init()
@ -61,7 +64,7 @@ struct TokenInstanceAction {
}
var transactionFunction: FunctionOrigin? {
switch type {
case .erc20Send, .erc20Receive:
case .erc20Send, .erc20Receive, .erc20ExchangeOnUniswap:
return nil
case .nftRedeem, .nftSell, .nonFungibleTransfer:
return nil
@ -71,7 +74,7 @@ struct TokenInstanceAction {
}
var contract: AlphaWallet.Address? {
switch type {
case .erc20Send, .erc20Receive:
case .erc20Send, .erc20Receive, .erc20ExchangeOnUniswap:
return nil
case .nftRedeem, .nftSell, .nonFungibleTransfer:
return nil
@ -87,7 +90,7 @@ struct TokenInstanceAction {
//TODO we can live-reload the action view screen now if we observe for changes
func viewHtml(forTokenHolder tokenHolder: TokenHolder) -> (html: String, hash: Int) {
switch type {
case .erc20Send, .erc20Receive:
case .erc20Send, .erc20Receive, .erc20ExchangeOnUniswap:
return (html: "", hash: 0)
case .nftRedeem:
return (html: "", hash: 0)
@ -104,7 +107,7 @@ struct TokenInstanceAction {
func activeExcludingSelection(selectedTokenHolders: [TokenHolder], forWalletAddress walletAddress: AlphaWallet.Address, fungibleBalance: BigInt? = nil) -> TokenScriptSelection? {
switch type {
case .erc20Send, .erc20Receive:
case .erc20Send, .erc20Receive, .erc20ExchangeOnUniswap:
return nil
case .nftRedeem, .nftSell, .nonFungibleTransfer:
return nil

@ -0,0 +1,103 @@
//
// UniswapERC20Token.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 25.08.2020.
//
import Foundation
struct UniswapERC20Token {
let name: String
let contract: AlphaWallet.Address
let decimal: Int
}
extension UniswapERC20Token {
static func isSupport(token: TokenObject) -> Bool {
switch token.server {
case .main:
return availableTokens.contains(where: { $0.contract.sameContract(as: token.contractAddress) })
case .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_sigma1, .artis_tau1, .custom, .poa, .callisto, .xDai, .classic:
return false
}
}
private static let availableTokens: [UniswapERC20Token] = [
.init(name: "ETH", contract: Constants.nullAddress, decimal: 0),
.init(name: "USDT", contract: AlphaWallet.Address(string: "0xdAC17F958D2ee523a2206206994597C13D831ec7")!, decimal: 6),
.init(name: "LINK", contract: AlphaWallet.Address(string: "0x514910771AF9Ca656af840dff83E8264EcF986CA")!, decimal: 18),
.init(name: "BNB", contract: AlphaWallet.Address(string: "0xB8c77482e45F1F44dE1745F52C74426C631bDD52")!, decimal: 18),
.init(name: "WETH", contract: AlphaWallet.Address(string: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")!, decimal: 18),
.init(name: "0xBTC", contract: AlphaWallet.Address(string: "0xB6eD7644C69416d67B522e20bC294A9a9B405B31")!, decimal: 8),
.init(name: "aDAI", contract: AlphaWallet.Address(string: "0xc11d0D5b9e8d7741289e78a52b9D2eFBCEC14478")!, decimal: 18),
.init(name: "AMN", contract: AlphaWallet.Address(string: "0x737F98AC8cA59f2C68aD658E3C3d8C8963E40a4c")!, decimal: 18),
.init(name: "AMPL", contract: AlphaWallet.Address(string: "0xD46bA6D942050d489DBd938a2C909A5d5039A161")!, decimal: 9),
.init(name: "ANJ", contract: AlphaWallet.Address(string: "0xcD62b1C403fa761BAadFC74C525ce2B51780b184")!, decimal: 18),
.init(name: "ANT", contract: AlphaWallet.Address(string: "0x960b236A07cf122663c4303350609A66A7B288C0")!, decimal: 18),
.init(name: "AST", contract: AlphaWallet.Address(string: "0x27054b13b1B798B345b591a4d22e6562d47eA75a")!, decimal: 4),
.init(name: "BAL", contract: AlphaWallet.Address(string: "0xba100000625a3754423978a60c9317c58a424e3D")!, decimal: 18),
.init(name: "BAND", contract: AlphaWallet.Address(string: "0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55")!, decimal: 18),
.init(name: "BAT", contract: AlphaWallet.Address(string: "0x0D8775F648430679A709E98d2b0Cb6250d2887EF")!, decimal: 18),
.init(name: "BLT", contract: AlphaWallet.Address(string: "0x107c4504cd79C5d2696Ea0030a8dD4e92601B82e")!, decimal: 18),
.init(name: "BNT", contract: AlphaWallet.Address(string: "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C")!, decimal: 18),
.init(name: "BTC++", contract: AlphaWallet.Address(string: "0x0327112423F3A68efdF1fcF402F6c5CB9f7C33fd")!, decimal: 18),
.init(name: "BZRX", contract: AlphaWallet.Address(string: "0x56d811088235F11C8920698a204A5010a788f4b3")!, decimal: 18),
.init(name: "cDAI", contract: AlphaWallet.Address(string: "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643")!, decimal: 8),
.init(name: "CEL", contract: AlphaWallet.Address(string: "0xaaAEBE6Fe48E54f431b0C390CfaF0b017d09D42d")!, decimal: 4),
.init(name: "CELR", contract: AlphaWallet.Address(string: "0x4F9254C83EB525f9FCf346490bbb3ed28a81C667")!, decimal: 18),
.init(name: "CHAI", contract: AlphaWallet.Address(string: "0x06AF07097C9Eeb7fD685c692751D5C66dB49c215")!, decimal: 18),
.init(name: "cUSDC", contract: AlphaWallet.Address(string: "0x39AA39c021dfbaE8faC545936693aC917d5E7563")!, decimal: 8),
.init(name: "DAI", contract: AlphaWallet.Address(string: "0x6B175474E89094C44Da98b954EedeAC495271d0F")!, decimal: 18),
.init(name: "DATA", contract: AlphaWallet.Address(string: "0x1B5f21ee98eed48d292e8e2d3Ed82b40a9728A22")!, decimal: 18),
.init(name: "DGD", contract: AlphaWallet.Address(string: "0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A")!, decimal: 9),
.init(name: "DGX", contract: AlphaWallet.Address(string: "0x4f3AfEC4E5a3F2A6a1A411DEF7D7dFe50eE057bF")!, decimal: 9),
.init(name: "DIP", contract: AlphaWallet.Address(string: "0x97af10D3fc7C70F67711Bf715d8397C6Da79C1Ab")!, decimal: 12),
.init(name: "DONUT", contract: AlphaWallet.Address(string: "0xC03238A3cb7CA6580f3a89B32AFaC1bcFF87CaE4")!, decimal: 18),
.init(name: "EBASE", contract: AlphaWallet.Address(string: "0xa689DCEA8f7ad59fb213be4bc624ba5500458dC6")!, decimal: 18),
.init(name: "ENJ", contract: AlphaWallet.Address(string: "0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c")!, decimal: 18),
.init(name: "iDAI", contract: AlphaWallet.Address(string: "0x493C57C4763932315A328269E1ADaD09653B9081")!, decimal: 18),
.init(name: "IOTX", contract: AlphaWallet.Address(string: "0x6fB3e0A217407EFFf7Ca062D46c26E5d60a14d69")!, decimal: 18),
.init(name: "iSAI", contract: AlphaWallet.Address(string: "0x14094949152EDDBFcd073717200DA82fEd8dC960")!, decimal: 18),
.init(name: "KEY", contract: AlphaWallet.Address(string: "0x4CC19356f2D37338b9802aa8E8fc58B0373296E7")!, decimal: 18),
.init(name: "KNC", contract: AlphaWallet.Address(string: "0xdd974D5C2e2928deA5F71b9825b8b646686BD200")!, decimal: 18),
.init(name: "LEND", contract: AlphaWallet.Address(string: "0x80fB784B7eD66730e8b1DBd9820aFD29931aab03")!, decimal: 18),
.init(name: "LINK", contract: AlphaWallet.Address(string: "0x514910771AF9Ca656af840dff83E8264EcF986CA")!, decimal: 18),
.init(name: "MET", contract: AlphaWallet.Address(string: "0xa3d58c4E56fedCae3a7c43A725aeE9A71F0ece4e")!, decimal: 18),
.init(name: "MGN", contract: AlphaWallet.Address(string: "0x1B941DEd58267a06f4Ab028b446933e578389DAF")!, decimal: 18),
.init(name: "MKR", contract: AlphaWallet.Address(string: "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2")!, decimal: 18),
.init(name: "MLN", contract: AlphaWallet.Address(string: "0xec67005c4E498Ec7f55E092bd1d35cbC47C91892")!, decimal: 18),
.init(name: "MOD", contract: AlphaWallet.Address(string: "0x957c30aB0426e0C93CD8241E2c60392d08c6aC8e")!, decimal: 0),
.init(name: "MTA", contract: AlphaWallet.Address(string: "0xa3BeD4E1c75D00fa6f4E5E6922DB7261B5E9AcD2")!, decimal: 18),
.init(name: "mUSD", contract: AlphaWallet.Address(string: "0xe2f2a5C287993345a840Db3B0845fbC70f5935a5")!, decimal: 18),
.init(name: "NEXO", contract: AlphaWallet.Address(string: "0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206")!, decimal: 18),
.init(name: "NMR", contract: AlphaWallet.Address(string: "0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671")!, decimal: 18),
.init(name: "RCN", contract: AlphaWallet.Address(string: "0xF970b8E36e23F7fC3FD752EeA86f8Be8D83375A6")!, decimal: 18),
.init(name: "RDN", contract: AlphaWallet.Address(string: "0x255Aa6DF07540Cb5d3d297f0D0D4D84cb52bc8e6")!, decimal: 18),
.init(name: "REN", contract: AlphaWallet.Address(string: "0x408e41876cCCDC0F92210600ef50372656052a38")!, decimal: 18),
.init(name: "renBCH", contract: AlphaWallet.Address(string: "0x459086F2376525BdCebA5bDDA135e4E9d3FeF5bf")!, decimal: 8),
.init(name: "renBTC", contract: AlphaWallet.Address(string: "0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D")!, decimal: 8),
.init(name: "renZEC", contract: AlphaWallet.Address(string: "0x1C5db575E2Ff833E46a2E9864C22F4B22E0B37C2")!, decimal: 8),
.init(name: "REP", contract: AlphaWallet.Address(string: "0xE94327D07Fc17907b4DB788E5aDf2ed424adDff6")!, decimal: 18),
.init(name: "REPv2", contract: AlphaWallet.Address(string: "0x221657776846890989a759BA2973e427DfF5C9bB")!, decimal: 18),
.init(name: "RING", contract: AlphaWallet.Address(string: "0x9469D013805bFfB7D3DEBe5E7839237e535ec483")!, decimal: 18),
.init(name: "SOCKS", contract: AlphaWallet.Address(string: "0xeEAE80e1790c63E390cFB176536D734c28828192")!, decimal: 0),
.init(name: "SPANK", contract: AlphaWallet.Address(string: "0x42d6622deCe394b54999Fbd73D108123806f6a18")!, decimal: 18),
.init(name: "SRM", contract: AlphaWallet.Address(string: "0x476c5E26a75bd202a9683ffD34359C0CC15be0fF")!, decimal: 6),
.init(name: "STAKE", contract: AlphaWallet.Address(string: "0x0Ae055097C6d159879521C384F1D2123D1f195e6")!, decimal: 18),
.init(name: "STORJ", contract: AlphaWallet.Address(string: "0xB64ef51C888972c908CFacf59B47C1AfBC0Ab8aC")!, decimal: 8),
.init(name: "sUSD", contract: AlphaWallet.Address(string: "0x57Ab1ec28D129707052df4dF418D58a2D46d5f51")!, decimal: 18),
.init(name: "sXAU", contract: AlphaWallet.Address(string: "0x261EfCdD24CeA98652B9700800a13DfBca4103fF")!, decimal: 18),
.init(name: "USDC", contract: AlphaWallet.Address(string: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")!, decimal: 6),
.init(name: "USDS", contract: AlphaWallet.Address(string: "0x098fEEd90F28493e02f6e745a2767120E7B79A1B")!, decimal: 8),
.init(name: "USDT", contract: AlphaWallet.Address(string: "0xdAC17F958D2ee523a2206206994597C13D831ec7")!, decimal: 6),
.init(name: "USDx", contract: AlphaWallet.Address(string: "0xeb269732ab75A6fD61Ea60b06fE994cD32a83549")!, decimal: 18),
.init(name: "VERI", contract: AlphaWallet.Address(string: "0x8f3470A7388c05eE4e7AF3d01D8C722b0FF52374")!, decimal: 18),
.init(name: "WBTC", contract: AlphaWallet.Address(string: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599")!, decimal: 8),
.init(name: "WCK", contract: AlphaWallet.Address(string: "0xb69EfF754380AC7C68ffeE174b881A39dae2f58C")!, decimal: 18),
.init(name: "XCHF", contract: AlphaWallet.Address(string: "0xB4272071eCAdd69d933AdcD19cA99fe80664fc08")!, decimal: 18),
.init(name: "XIO", contract: AlphaWallet.Address(string: "0xa45Eaf6d2Ce4d1a67381d5588B865457023c23A0")!, decimal: 18),
.init(name: "ZRX", contract: AlphaWallet.Address(string: "0xE41d2489571d322189246DaFA5ebDe1F4699F498")!, decimal: 18),
]
}

@ -0,0 +1,78 @@
//
// UniswapHolder.swift
// AlphaWallet
//
// Created by Vladyslav Shepitko on 21.08.2020.
//
import UIKit
struct UniswapHolder {
private static let baseURL = "https://app.uniswap.org/#"
let input: Input
var version: Version = .v2
var theme: Theme = .dark
var method: Method = .swap
var url: URL? {
var components = URLComponents()
components.path = method.rawValue
components.queryItems = [
URLQueryItem(name: Version.key, value: version.rawValue),
URLQueryItem(name: Theme.key, value: theme.rawValue)
] + input.urlQueryItems
//NOTE: URLComponents doesn't allow path to contain # symbol
guard let pathWithQueryItems = components.url?.absoluteString else { return nil }
return URL(string: UniswapHolder.baseURL + pathWithQueryItems)
}
enum Version: String {
static let key = "use"
case v1
case v2
}
enum Theme: String {
static let key = "theme"
case dark
case light
}
enum Method: String {
case swap = "/swap"
case use
}
enum Input {
private enum Keys {
static let input = "inputCurrency"
static let output = "outputCurrency"
}
case inputOutput(from: AlphaWallet.Address, to: AddressOrEnsName)
case input(AlphaWallet.Address)
case none
var urlQueryItems: [URLQueryItem] {
switch self {
case .inputOutput(let inputAddress, let outputAddress):
return [
.init(name: Keys.input, value: inputAddress.eip55String),
.init(name: Keys.output, value: outputAddress.stringValue),
]
case .input(let address):
return [
.init(name: Keys.input, value: address.eip55String)
]
case .none:
return []
}
}
}
}

@ -162,7 +162,7 @@ class TokenInstanceViewController: UIViewController, TokenVerifiableStatusViewCo
private func handle(action: TokenInstanceAction) {
switch action.type {
case .erc20Send, .erc20Receive:
case .erc20Send, .erc20Receive, .erc20ExchangeOnUniswap:
//TODO when we support TokenScript views for ERC20s, we need to perform the action here
break
case .nftRedeem:

@ -6,6 +6,7 @@ import BigInt
import PromiseKit
protocol TokenViewControllerDelegate: class, CanOpenURL {
func didTapErc20ExchangeOnUniswap(forTransferType transferType: TransferType, inViewController viewController: TokenViewController)
func didTapSend(forTransferType transferType: TransferType, inViewController viewController: TokenViewController)
func didTapReceive(forTransferType transferType: TransferType, inViewController viewController: TokenViewController)
func didTap(transaction: Transaction, inViewController viewController: TokenViewController)
@ -212,11 +213,17 @@ class TokenViewController: UIViewController {
delegate?.didTapReceive(forTransferType: transferType, inViewController: self)
}
@objc private func erc20ExchangeOnUniswap() {
delegate?.didTapErc20ExchangeOnUniswap(forTransferType: transferType, inViewController: self)
}
@objc private func actionButtonTapped(sender: UIButton) {
guard let viewModel = viewModel else { return }
let actions = viewModel.actions
for (action, button) in zip(actions, buttonsBar.buttons) where button == sender {
switch action.type {
case .erc20ExchangeOnUniswap:
erc20ExchangeOnUniswap()
case .erc20Send:
send()
case .erc20Receive:

@ -227,7 +227,7 @@ class TokensCardViewController: UIViewController, TokenVerifiableStatusViewContr
private func handle(action: TokenInstanceAction) {
guard let tokenHolder = selectedTokenHolder else { return }
switch action.type {
case .erc20Send, .erc20Receive:
case .erc20Send, .erc20Receive, .erc20ExchangeOnUniswap:
break
case .nftRedeem:
redeem()

@ -30,6 +30,7 @@ struct TokenViewControllerViewModel {
guard let token = token else { return [] }
let xmlHandler = XMLHandler(contract: token.contractAddress, assetDefinitionStore: assetDefinitionStore)
let actionsFromTokenScript = xmlHandler.actions
if actionsFromTokenScript.isEmpty {
switch token.type {
case .erc875:
@ -38,32 +39,52 @@ struct TokenViewControllerViewModel {
return []
case .erc721ForTickets:
return []
case .nativeCryptocurrency:
//TODO .erc20Send and .erc20Receive names aren't appropriate
return [
.init(type: .erc20Send),
.init(type: .erc20Receive)
]
case .erc20:
return [
.init(type: .erc20Send),
.init(type: .erc20Receive)
]
case .erc20, .nativeCryptocurrency:
if UniswapERC20Token.isSupport(token: token) {
return [
.init(type: .erc20Send),
.init(type: .erc20Receive),
.init(type: .erc20ExchangeOnUniswap)
]
} else {
return [
.init(type: .erc20Send),
.init(type: .erc20Receive)
]
}
}
} else {
switch token.type {
case .erc875, .erc721, .erc20, .erc721ForTickets:
case .erc875, .erc721, .erc721ForTickets:
return actionsFromTokenScript
case .erc20:
if UniswapERC20Token.isSupport(token: token) {
return actionsFromTokenScript + [.init(type: .erc20ExchangeOnUniswap)]
} else {
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
if let server = xmlHandler.server, server == token.server {
return actionsFromTokenScript
if UniswapERC20Token.isSupport(token: token) {
return actionsFromTokenScript + [.init(type: .erc20ExchangeOnUniswap)]
} else {
return actionsFromTokenScript
}
} else {
//TODO .erc20Send and .erc20Receive names aren't appropriate
return [
.init(type: .erc20Send),
.init(type: .erc20Receive)
]
if UniswapERC20Token.isSupport(token: token) {
return [
.init(type: .erc20Send),
.init(type: .erc20Receive),
.init(type: .erc20ExchangeOnUniswap)
]
} else {
return [
.init(type: .erc20Send),
.init(type: .erc20Receive)
]
}
}
}
}

@ -470,7 +470,7 @@ extension TokensCardCoordinator: TokensCardViewControllerDelegate {
switch action.type {
case .tokenScript:
showTokenInstanceActionView(forAction: action, tokenHolder: tokenHolder, viewController: viewController)
case .erc20Send, .erc20Receive, .nftRedeem, .nftSell, .nonFungibleTransfer:
case .erc20Send, .erc20Receive, .nftRedeem, .nftSell, .nonFungibleTransfer, .erc20ExchangeOnUniswap:
//Couldn't have reached here
break
}

Loading…
Cancel
Save