// // ResourcesServiceSetupRAM.swift // PayCash // // Created by Igor Danich on 19.08.2020. // Copyright © 2020 List. All rights reserved. // import Foundation import Alamofire import BigInt extension Resources.Service.Setup { class RAM: Common.Service.Provider { var didUpdate: (() -> Void)? var didReload: (() -> Void)? var title: String { Resources.Model.Performance.Kind.ram.title } enum Action: String, CommonMenuMappable { case buy case sell var title: String { "resources.setup.action.\(rawValue)".localized } func toMenu() -> Common.Model.Menu { .menu(uuid: rawValue, title: title) } } let actions: [Action] = [.buy, .sell] private var _action: Action? var action: Action? { get { _action } set { _action = newValue;clear() } } enum Buy: String, CommonMenuMappable { case eos = "resources.setup.buy.eos" case kb = "resources.setup.buy.kb" func toMenu() -> Common.Model.Menu { .menu(uuid: rawValue, title: rawValue.localized) } } let buys: [Buy] = [.eos, .kb] private var _buy = Buy.kb var buy: Buy { get { _buy } set { _buy = newValue _ramAmount = "" update() } } var token: Network.Model.Token? { didSet { amountSend = "" amountRecieve = "" balance = token?.amount update() } } var amountSend: String = "" var amountRecieve: String = "" var ramPrices: [RAMPrice] = [] var balances: [Network.Model.Token] { Wallet.Service.Tokens.shared.balances.collection } var balance: Decimal? var balanceText: String { Wallet.Service.Tokens.shared.balances.collection.first(where: { $0.contract == Network.servers.current.contract(.eosioToken) && $0.symbol.uppercased() == "EOS" })?.description ?? "" } private var _isOtherEnabled = false var isOtherEnabled: Bool { get { _isOtherEnabled } set { _isOtherEnabled = newValue _buy = .kb _ramAmount = "" update() } } private var _otherAccount = "" var otherAccount: String { get { _otherAccount } set { _otherAccount = newValue;update() } } private(set) var isValidAmount = false private(set) var isValid = false private var _ramAmount = "" var ramAmount: String { get { _ramAmount } set { _ramAmount = newValue;update() } } private(set) var submitText = "" var ramPrecision: Int { action == nil ? 0 : (action == .buy ? (buy == .eos ? 4 : 2) : 2) } let performance: Resources.Service.Performance init(account: String) { performance = .init(account: account) } func fetch() { performance.fetch { _ in self.getRamPrice { self.didReload?() self.update() } } } func clear(force: Bool = false) { if force { _action = nil performance.clear() } _isOtherEnabled = false _otherAccount = "" _ramAmount = "" _buy = .kb isValid = false submitText = "" token = nil didReload?() update() } private func update() { submitText = "" isValid = false isValidAmount = false switch action { case .buy: submitText = L10n.Resources.Setup.Submit.buy(amountRecieve.toDecimal().toString(max: 2, symbol: L10n.Resources.Performance.Unit.kb)) if let balance = balance { if isOtherEnabled { isValid = (amountSend.toDecimal() > 0 && amountSend.toDecimal() <= balance) && (otherAccount != "") } else { isValid = amountSend.toDecimal() > 0 && amountSend.toDecimal() <= balance } isValidAmount = amountSend.toDecimal() > 0 && amountSend.toDecimal() <= balance } case .sell: submitText = L10n.Resources.Setup.Submit.sell(ramAmount.toDecimal().toString(max: 2, symbol: L10n.Resources.Performance.Unit.kb)) isValid = ramAmount.toDecimal() > 0 && Int(ramAmount.toDouble()*1024) <= (performance[.ram].available) case .none: break } didUpdate?() } func update(receive: String) { amountRecieve = receive guard let selectedToken = self.token, let ramPrice = ramPrices.first(where: { $0.token == selectedToken.symbol }), let price = ramPrice.price.toOptionalDecimal(), price > 0 else { return } amountSend = (receive.toDecimal() * price * 1024 /*bytes in kb*/) .toNormalizedString(precisionMax: ramPrice.precision) update() } func update(send: String) { amountSend = send guard let selectedToken = self.token, let ramPrice = ramPrices.first(where: { $0.token == selectedToken.symbol }), let price = ramPrice.price.toOptionalDecimal(), price > 0 else { return } amountRecieve = (send.toDecimal() / price / 1024 /*bytes in kb*/) .toNormalizedString(precisionMax: 2) update() } func getRamPrice(completion: @escaping ()->()) { AF.request(Network.servers.current.paycashQraphql + "/ram") .response { (data) in do { self.ramPrices = try JSONDecoder().decode([RAMPrice].self, from: data.data!) completion() } catch { completion() } } } func submit(privateKey: String, _ completion: @escaping Completion.Network) { guard let action = action, isValid else { return } switch action { case .buy: guard let username = Accounts().current?.username, let token = token else { return } let data = Network.Model.Blockchain.Transfer( from: username, to: "pcash", quantity: amountSend.toDecimal().toCanonicalString(precision: token.precision, symbol: token.symbol), memo: "buyram:\(isOtherEnabled ? otherAccount : username)" ) Network.Service.Blockchain.execute( contract: token.contract, action: .transfer, data: data, privateKeys: [privateKey] ) { (result) in completion(.error(result)) } case .sell: Network.Service.Blockchain.execute(contract: .eosio, action: .sellram, data: [ "account" : performance.account, "bytes" : "\(Int(ramAmount.toDouble()*1024))" ], privateKeys: [privateKey]) { (result) in completion(.error(result)) } } } func getPricePerKilobyte(kbs: Decimal) async -> Decimal? { let rammarket = await Network.table.fetch( code: Network.servers.current.contract(.eosio), table: "rammarket", scope: Network.servers.current.contract(.eosio), limit: 1, type: RAMMarketPrices.self ) let eosBalance = rammarket.first?.quote.balance.amount.toDecimal() ?? 0 let ramBalance = rammarket.first?.base.balance.amount.toDecimal() ?? 0 return ramBalance > 0 ? eosBalance / ramBalance /* / 1024 */ : 0 } private struct RAMMarketPrices: Decodable { let quote: RAMMarketPrice let base: RAMMarketPrice struct RAMMarketPrice: Decodable { let balance: String } } struct RAMPrice: Decodable { let code: String let token: String let precision: Int let price: String } } }