Files

220 lines
9.4 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// EnterSafeAddressViewController.swift
// Multisig
//
// Created by Dmitry Bespalov on 14.12.20.
// Copyright © 2020 Gnosis Ltd. All rights reserved.
//
import UIKit
import SafeWeb3
class EnterSafeAddressViewController: UIViewController {
var address: Address? { addressField?.address }
var gatewayService = App.shared.clientGatewayService
var completion: (_ address: Address, _ safeVersion: String) -> Void = { _, _ in }
var chain: SCGModels.Chain!
var safeVersion: String?
var preselectedAddress: String?
lazy var trackingParameters: [String: Any] = { ["chain_id" : chain.chainId.description] }()
@IBOutlet private weak var headerLabel: UILabel!
@IBOutlet private weak var addressField: AddressField!
private var loadSafeTask: URLSessionTask?
private var nextButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Load Safe Account"
headerLabel.setStyle(.body)
addressField.setPlaceholderText("Enter Safe address")
addressField.onTap = { [weak self] in self?.didTapAddressField() }
nextButton = UIBarButtonItem(title: "Next", style: .done, target: self, action: #selector(didTapNextButton(_:)))
nextButton.isEnabled = false
navigationItem.rightBarButtonItem = nextButton
if let address = preselectedAddress {
didEnterText(address)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
Tracker.trackEvent(.safeAddAddress, parameters: trackingParameters)
}
@objc private func didTapNextButton(_ sender: Any) {
guard let address = address, let safeVersion = safeVersion else { return }
completion(address, safeVersion)
}
private func didTapAddressField() {
let alertVC = UIAlertController(title: nil, message: nil, preferredStyle: .multiplatformActionSheet)
alertVC.addAction(UIAlertAction(title: "Paste from Clipboard", style: .default, handler: { [weak self] _ in
let text = Pasteboard.string
self?.didEnterText(text)
}))
alertVC.addAction(UIAlertAction(title: "Scan QR Code", style: .default, handler: { [weak self] _ in
guard let self = self else { return }
let vc = QRCodeScannerViewController()
vc.trackingParameters = self.trackingParameters
vc.scannedValueValidator = { value in
if let _ = try? Address.addressWithPrefix(text: value) {
return .success(value)
} else {
return .failure(GSError.error(description: "Can’t use this QR code",
error: GSError.SafeAddressNotValid()))
}
}
vc.modalPresentationStyle = .overFullScreen
vc.delegate = self
vc.setup()
self.present(vc, animated: true, completion: nil)
}))
let blockchainDomainManager = BlockchainDomainManager(rpcURL: chain.authenticatedRpcUrl,
chainId: chain.id,
ensRegistryAddress: chain.ensRegistryAddress)
if blockchainDomainManager.ens != nil {
alertVC.addAction(UIAlertAction(title: "Enter ENS Name", style: .default, handler: { [weak self] _ in
guard let self = self else { return }
let ensNameVC = EnterENSNameViewController(manager: blockchainDomainManager, chain: self.chain)
ensNameVC.trackingParameters = self.trackingParameters
ensNameVC.onConfirm = { [weak self] in
guard let `self` = self else { return }
self.navigationController?.popViewController(animated: true)
self.didEnterText(ensNameVC.address?.checksummed)
}
let ensNameWrapperVC = RibbonViewController(rootViewController: ensNameVC)
ensNameWrapperVC.chain = self.chain
self.show(ensNameWrapperVC, sender: nil)
}))
}
if blockchainDomainManager.unstoppableDomainResolution != nil {
alertVC.addAction(UIAlertAction(title: "Enter Unstoppable Name", style: .default, handler: { [weak self] _ in
guard let self = self else { return }
let udNameVC = EnterUnstoppableNameViewController(manager: blockchainDomainManager, chain: self.chain)
udNameVC.trackingParameters = self.trackingParameters
udNameVC.onConfirm = { [weak self] in
guard let `self` = self else { return }
self.navigationController?.popViewController(animated: true)
self.didEnterText(udNameVC.address?.checksummed)
}
let udNameWrapperVC = RibbonViewController(rootViewController: udNameVC)
udNameWrapperVC.chain = self.chain
self.show(udNameWrapperVC, sender: nil)
}))
}
if let popoverPresentationController = alertVC.popoverPresentationController {
popoverPresentationController.sourceView = addressField
}
alertVC.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alertVC, animated: true, completion: nil)
}
private func didEnterText(_ text: String?) {
addressField.clear()
loadSafeTask?.cancel()
nextButton.isEnabled = false
guard let text = text?.trimmingCharacters(in: .whitespacesAndNewlines) else {
return
}
guard !text.isEmpty else {
addressField.setError("Safe address should not be empty")
return
}
addressField.setInputText(text)
do {
// (1) validate that the text is address
let address = try Address.addressWithPrefix(text: text)
guard (address.prefix ?? chain.shortName) == chain.shortName else {
addressField.setError(GSError.AddressMismatchNetwork())
return
}
addressField.setAddress(address, prefix: chain.shortName)
// (2) and that there's no such safe already
let exists = Safe.exists(address.checksummed, chainId: chain.id)
if exists { throw GSError.SafeAlreadyExists() }
// (3) and there exists safe at that address
addressField.setLoading(true)
loadSafeTask = gatewayService.asyncSafeInfo(safeAddress: address,
chainId: chain.id,
completion: { [weak self] result in
DispatchQueue.main.async {
self?.addressField.setLoading(false)
}
switch result {
case .failure(let error):
DispatchQueue.main.async { [weak self] in
guard let `self` = self else { return }
// ignore cancellation error due to cancelling the
// currently running task. Otherwise user will see
// meaningless message.
if (error as NSError).code == URLError.cancelled.rawValue &&
(error as NSError).domain == NSURLErrorDomain {
return
} else if error is GSError.EntityNotFound {
let message = GSError.error(description: "Can’t use this address",
error: GSError.InvalidSafeAddress()).localizedDescription
self.addressField.setError(message)
} else {
let message = GSError.error(description: "Can’t use this address", error: error)
self.addressField.setError(message)
}
}
case .success(let info):
DispatchQueue.main.async { [weak self] in
guard let `self` = self else { return }
// (4) and its mastercopy is supported
guard let version = info.version, App.shared.gnosisSafe.isSupported(version) else {
let error = GSError.error(description: "Can’t use this address",
error: GSError.UnsupportedImplementationCopy())
self.addressField.setError(error.localizedDescription)
return
}
self.safeVersion = version
self.nextButton.isEnabled = true
}
}
})
} catch {
addressField.setError(
GSError.error(description: "Can’t use this address",
error: error is EthereumAddress.Error ? GSError.SafeAddressNotValid() : error))
}
}
}
extension EnterSafeAddressViewController: QRCodeScannerViewControllerDelegate {
func scannerViewControllerDidCancel() {
dismiss(animated: true, completion: nil)
}
func scannerViewControllerDidScan(_ code: String) {
didEnterText(code)
dismiss(animated: true, completion: nil)
}
}