// // ExchangeCryptocashViewController.swift // PayCash // // Created by Saveliy Stavitsky on 9/3/22. // Copyright © 2022 AM. All rights reserved. // import UIKit final class ExchangeCryptocashViewController: UIViewController { typealias FetchCompletion = () -> Void private var tokenUSDT = Network.Model.Token( symbol: "USDT", precision: 4, amount: 100, contract: "" ) private var tokenOut = Network.Model.Token( symbol: "USDCASH", precision: 5, amount: 100, contract: ApplicationEnvironment.shared().current.contract(.cash) ) private var p2pTokenscollection = [Network.Model.Token]() private var walletP2pTokenscollection: [Network.Model.Token] { self.p2pTokenscollection.compactMap { Wallet.Service.Tokens.shared.balances.collection.first($0) } } private var selectedToken: Network.Model.Token? { didSet { self.amountTextField.isHidden = !self.selectedToken.isExist let messages = [ L10n.ExchangeCryptocashViewController.Amount.error2, L10n.ExchangeCryptocashViewController.Amount.error3 ] if messages.contains(where: { $0 == self.amountTextField.error }) { self.amountTextField.error = nil } self.view.endEditing(true) self.amountTextField.value = "" self.infoAmountsStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } if let selectedToken = self.selectedToken { self.amountTextField.lzText = L10n.ExchangeCryptocashViewController.Amount.info + (p2pTokenscollection.first(selectedToken)?.certCoef ?? 0) .toReadebleString(precisionMax: selectedToken.precision, symbol: selectedToken.symbol, group: true) let lImage = UIImageView(token: selectedToken) lImage.addHeightWidthConstraints(height: 24, width: 24) self.amountTextField.accessoryView = lImage self.amountTextField.precision = selectedToken.precision self.amountTextField.becomeFirstResponder() } } } private var deposits = [Deposit]() private func calcMLNKViaDeposits(amount: Decimal) -> Decimal { var tempAmount = amount * tokenUSDT.amount var tempMLNK = Decimal(0) for deposit in deposits { if tempAmount <= 0 { break } if deposit.usdt_in.amount.toDecimal() <= tempAmount { tempMLNK += deposit.mlnk_in.amount.toDecimal() tempAmount -= deposit.usdt_in.amount.toDecimal() } else { tempMLNK += tempAmount * (deposit.mlnk_in.amount.toDecimal() / deposit.usdt_in.amount.toDecimal()) tempAmount -= tempAmount } } return tempMLNK } @IBOutlet private weak var tokeGiveView: CommonViewCard! @IBOutlet private weak var explanationStackView: UIStackView! @IBOutlet private weak var amountTextField: CommonTextField! @IBOutlet private weak var infoAmountsStackView: UIStackView! @IBOutlet private weak var warningView: CommonViewWarning! @IBOutlet var swapButton: CommonButtonAction! override func viewDidLoad() { super.viewDidLoad() self.title = L10n.ExchangeCryptocashViewController.title self.navigationItem.leftBarButtonItem = .pop(self) for textField in [self.amountTextField] { textField?.delegate = self textField?.keyboardType = .decimalPad } self.amountTextField.lzPlaceholder = L10n.ExchangeCryptocashViewController.Amount.placeholder self.amountTextField.lzTitle = L10n.ExchangeCryptocashViewController.Amount.title self.swapButton.isEnabled = false self.swapButton.isUserInteractionEnabled = false self.warningView.lzText = L10n.ExchangeCryptocashViewController.warning } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) Accounts().isLocked = true self.fetchData(animated) } private func fetchData(_ animated: Bool) { self.fetchSwap { self.fetchSwapBack(animated: animated) { [weak self] in self?.fetchDeposit(animated: animated) } } } private func fetchSwap(_ completion: @escaping FetchCompletion) { struct Income: Codable { let income: Network.Model.TokenSmall } Network.table.fetch( code: ApplicationEnvironment.shared().current.contract(.cash), table: "swap1", scope: ApplicationEnvironment.shared().current.contract(.cash), type: Income.self ) { [weak self] in if let token = $0.map({ $0.income }).first?.asToken { self?.tokenUSDT = token } completion() } } private func fetchSwapBack(animated: Bool, _ completion: @escaping FetchCompletion) { struct CashToken: Codable { private let cash: String var symbol: String { String(cash.split(separator: " ").last!) } var precision: Int { cash.amount.precision } var coef: Decimal { cash.amount.toDecimal() } func asNetworkToken() -> Network.Model.Token { .init(symbol: symbol, precision: precision, amount: 0, contract: ApplicationEnvironment.shared().current.contract(.cash), certCoef: coef, certName: "") } } Network.table.fetch( code: ApplicationEnvironment.shared().current.contract(.cash), table: "swapback", scope: ApplicationEnvironment.shared().current.contract(.cash), type: CashToken.self) { [weak self] rows in guard let self = self else { return } self.p2pTokenscollection = rows.map({ $0.asNetworkToken() }) if !self.p2pTokenscollection.isEmpty { self.tokeGiveView.action = .cards( tokens: self.walletP2pTokenscollection, selected: self.selectedToken, empty: .menu( image: Asset.commonToken.image.tinted(with: Asset.deepWater.color), title: L10n.ExchangeCryptocashViewController.Cryptocash.placeholder ), title: L10n.ExchangeCryptocashViewController.Cryptocash.placeholder, { self.selectedToken = self.walletP2pTokenscollection.object(menu: $0) } ) } else { self.navigationController?.popViewController(animated: animated) } completion() } } private func fetchDeposit(animated: Bool) { Network.table.fetch( code: ApplicationEnvironment.shared().current.contract(.cash), table: "deposits", scope: ApplicationEnvironment.shared().current.contract(.cash), keyType: "i128", indexPosition: "2", type: Deposit.self ) { [weak self] deposits in self?.deposits = deposits if deposits.isEmpty { self?.navigationController?.popViewController(animated: animated) } } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) Accounts().isLocked = false } @IBAction private func onAccountType(_ : AnyObject?) { guard let selectedToken = self.selectedToken, let coef = p2pTokenscollection.first(selectedToken)?.certCoef, amountTextField.value.toDecimal() / coef == (amountTextField.value.toDecimal() / coef).roundUp(to: 0), amountTextField.value.toDecimal() > 0 else { return } infoAmountsStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } if let coef = p2pTokenscollection.first(selectedToken)?.certCoef { infoAmountsStackView.insertArrangedSubview( .field( title: L10n.ExchangeCryptocashViewController.Get.title, text: (tokenUSDT.amount * (amountTextField.value.toDecimal() / coef)).toString(max: tokenUSDT) ), at: 0) infoAmountsStackView.insertArrangedSubview( .field( title: L10n.ExchangeCryptocashViewController.Return.title, text: amountTextField.value.toDecimal().toString(max: selectedToken) ), at: 0) } } @IBAction func swapPressed(_ sender: Any) { view.endEditing(true) guard let selectedToken = self.selectedToken, let coef = p2pTokenscollection.first(selectedToken)?.certCoef else { return } let views: [UIView] = [ .field( title: L10n.ExchangeCryptocashViewController.Popup1.title1, text: amountTextField.value.toDecimal().toString(max: selectedToken) ), .field( title: L10n.ExchangeCryptocashViewController.Popup1.title2, text: (tokenUSDT.amount * (amountTextField.value.toDecimal() / coef)).toString(max: tokenUSDT) ) ] Popup.show(title: L10n.ExchangeCryptocashViewController.Popup1.title, views: views, submit: L10n.Common.Button.confirm, in: self) { ctrl in AccountViewAuthorize.showGetPrivateKey(in: self) { privateKey in Loader.show(in: ctrl) var actions = [(contract: String, action: Network.Model.Blockchain.Action, data: Codable)]() let returnToken = self.tokenUSDT if !Wallet.Service.Tokens.shared.balances.collection.contains(returnToken) { actions.append( (contract: returnToken.contract, action: .open, data: [ "owner": Accounts().current?.name ?? "", "symbol": "\(returnToken.precision),\(returnToken.symbol)", "ram_payer": Accounts().current?.name ?? "" ]) ) } actions.append( (contract: selectedToken.contract, action: .transfer, data: [ "from": Accounts().current?.name ?? "", "to": ApplicationEnvironment.shared().current.contract(.cash), "quantity": "\(self.amountTextField.value.toDecimal().toString(precision: selectedToken))", "memo": returnToken.symbol.lowercased() ])) Network.Service.Blockchain.execute(actions: actions, privateKeys: [privateKey]) { (result) in ctrl.dismiss(animated: true) if case let .success(trxId) = result { self.showCompletion(transactionId: trxId) delayed(3) { Wallet.Service.Tokens.shared.balances.fetch() } } else { Alert.notify(result.error) } } } } } private func showCompletion(transactionId: String) { guard let selectedToken = self.selectedToken, let coef = p2pTokenscollection.first(selectedToken)?.certCoef else { return } let views: [UIView] = [ .field( title: L10n.ExchangeCryptocashViewController.Popup2.title1, text: amountTextField.value.toDecimal().toString(max: selectedToken) ), .field( title: L10n.ExchangeCryptocashViewController.Popup2.title2, text: (tokenUSDT.amount * (amountTextField.value.toDecimal() / coef)).toString(max: tokenUSDT) ), .field( title: L10n.Wallet.History.Details.id, text: transactionId, rightButtonAvailbale: true, rightButtonImage: Asset.commonCopy.image, rightButtonAction: { Alert.copy(transactionId) } ) ] Popup.show(title: L10n.ExchangeCryptocashViewController.Popup2.title, views: views, submit: L10n.Common.Button.checkOnBloksIO, submitStyle: .outline, cancel: L10n.Common.Button.done, in: self, { ctrl in guard let checkTransactionUrl = URL(transactionId: transactionId) else { return } UIApplication.shared.open(checkTransactionUrl, options: [:], completionHandler: nil) }, { self.navigationController?.popViewController(animated: true) }) } } extension ExchangeCryptocashViewController: CommonTextFieldDelegate { func textFieldDidChange(_ textField: CommonTextField) { switch textField { case amountTextField: textField.value = textField.value.replacingOccurrences(of: ",", with: ".") let newPosition = textField.textField.endOfDocument textField.textField.selectedTextRange = textField.textField.textRange(from: newPosition, to: newPosition) infoAmountsStackView.arrangedSubviews.forEach { $0.removeFromSuperview() } if let selectedToken = self.selectedToken, let coef = p2pTokenscollection.first(selectedToken)?.certCoef, amountTextField.value.toDecimal() / coef == (amountTextField.value.toDecimal() / coef).roundUp(to: 0), amountTextField.value.toDecimal() > (selectedToken.symbol.uppercased() == "USDCASH" ? 9999 : 0), amountTextField.value.toDecimal() <= selectedToken.amount { swapButton.isEnabled = true swapButton.isUserInteractionEnabled = true infoAmountsStackView.insertArrangedSubview( .field( title: L10n.ExchangeCryptocashViewController.Get.title, text: (tokenUSDT.amount * (amountTextField.value.toDecimal() / coef)).toString(max: tokenUSDT) ), at: 0) infoAmountsStackView.insertArrangedSubview( .field( title: L10n.ExchangeCryptocashViewController.Return.title, text: amountTextField.value.toDecimal().toString(max: selectedToken) ), at: 0) } else { swapButton.isEnabled = false swapButton.isUserInteractionEnabled = false if (selectedToken?.amount ?? 0) < amountTextField.value.toDecimal() { amountTextField.error = L10n.ExchangeCryptocashViewController.Amount.error3 } else if amountTextField.value.toDecimal() > 0 { amountTextField.error = amountTextField.lzText } else { amountTextField.error = nil } } default: break } } }