Files
raspberry/iOS/Wallet/Sources/Resources/Service/Setup/ResourcesServiceSetupRAM.swift
2022-05-27 20:21:01 +03:00

260 lines
8.8 KiB
Swift

//
// 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
}
}
}