276 lines
9.5 KiB
Swift
276 lines
9.5 KiB
Swift
//
|
|
// ResourcesServiceSetupRAM.swift
|
|
// Wallet
|
|
//
|
|
// 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) }
|
|
}
|
|
|
|
enum ResourcesSetupError: Error {
|
|
case dataError
|
|
case other(Error)
|
|
}
|
|
|
|
private enum Locals {
|
|
static let minimumRamAmount: Decimal = 0.02
|
|
}
|
|
|
|
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 == ApplicationEnvironment.shared().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(completion: @escaping (ResourcesSetupError?) -> Void) {
|
|
performance.fetch { [weak self] _ in
|
|
self?.getRamPrice { error in
|
|
self?.didReload?()
|
|
self?.update()
|
|
completion(error)
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
var isValidTokenInput = 0..<balance ~= self.amountSend.toDecimal()
|
|
self.isValidAmount = isValidTokenInput
|
|
if self.isOtherEnabled {
|
|
isValidTokenInput = isValidTokenInput && self.otherAccount != ""
|
|
}
|
|
self.isValid = isValidTokenInput && self.amountRecieve.toDecimal() > 0
|
|
}
|
|
case .sell:
|
|
self.submitText = L10n.Resources.Setup.Submit.sell(self.ramAmount.toDecimal().toString(max: 2, symbol: L10n.Resources.Performance.Unit.kb))
|
|
self.isValid = self.ramAmount.toDecimal() >= Locals.minimumRamAmount
|
|
&& Int(self.ramAmount.toDouble()*1024) <= self.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*/)
|
|
.toReadebleString(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*/)
|
|
.toReadebleString(precisionMax: 2)
|
|
update()
|
|
}
|
|
|
|
func getRamPrice(completion: @escaping (ResourcesSetupError?) -> Void) {
|
|
|
|
let environment = ApplicationEnvironment.shared().current
|
|
AF.request(environment.backend.urlPath + "/ram", headers: HTTPHeaders(environment.headers))
|
|
.response { (data) in
|
|
guard let data = data.data else {
|
|
completion(ResourcesSetupError.dataError)
|
|
return
|
|
}
|
|
do {
|
|
self.ramPrices = try JSONDecoder().decode([RAMPrice].self, from: data)
|
|
completion(nil)
|
|
} catch {
|
|
completion(ResourcesSetupError.other(error))
|
|
}
|
|
}
|
|
}
|
|
|
|
func submit(privateKey: String, _ completion: @escaping Completion.Network) {
|
|
guard let action = action, isValid else { return }
|
|
|
|
switch action {
|
|
case .buy:
|
|
guard let username = Accounts().current?.name,
|
|
let token = token else { return }
|
|
|
|
let data = Network.Model.Blockchain.Transfer(
|
|
from: username,
|
|
to: ApplicationEnvironment.shared().current.contract(.malinkaReceiver),
|
|
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: ApplicationEnvironment.shared().current.contract(.eosio),
|
|
table: "rammarket",
|
|
scope: ApplicationEnvironment.shared().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
|
|
}
|
|
|
|
}
|
|
}
|