646 lines
26 KiB
Swift
646 lines
26 KiB
Swift
//
|
|
// PaycashServiceOrder.swift
|
|
// PayCash
|
|
//
|
|
// Created by Igor Danich on 02.09.2020.
|
|
// Copyright © 2020 List. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import Alamofire
|
|
import EosioSwiftEcc
|
|
import EosioSwiftAbieosSerializationProvider
|
|
import RealmSwift
|
|
import struct Resolver.Injected
|
|
|
|
extension P2P.Service {
|
|
class Order {
|
|
|
|
@Injected private var p2pBalancesService: P2PBalancesService
|
|
@Injected var p2pListsService: P2PListsService
|
|
|
|
var kind: P2P.Model.Kind
|
|
|
|
var order: P2POrder?
|
|
|
|
var contentType: P2P.Model.ContentType {
|
|
didSet {
|
|
paymentSystem = nil
|
|
tokenGet = nil
|
|
blockchain = nil
|
|
|
|
vcardNumber = ""
|
|
vcardName = ""
|
|
vcardExpiration = ""
|
|
vcardCVV = ""
|
|
|
|
country = nil
|
|
city = nil
|
|
|
|
bank = []
|
|
link = ""
|
|
|
|
account = ""
|
|
|
|
didReload?()
|
|
update()
|
|
}
|
|
}
|
|
|
|
var token: Network.Model.Token? {
|
|
didSet {
|
|
update()
|
|
}
|
|
}
|
|
|
|
var card = Cards.shared.default {
|
|
didSet {
|
|
update()
|
|
}
|
|
}
|
|
|
|
var paymentSystem: (id: Int, title: String, description: String)? {
|
|
didSet {
|
|
tokenGet = nil
|
|
update()
|
|
}
|
|
}
|
|
|
|
var tokenGet: (id: Int, title: String, description: String)? {
|
|
didSet {
|
|
rateToken1 = "1"
|
|
rateToken2 = "1"
|
|
amount = ""
|
|
limitMin = ""
|
|
limitMax = ""
|
|
blockchain = nil
|
|
update()
|
|
}
|
|
}
|
|
|
|
var blockchain: (id: Int, title: String, description: String)? {
|
|
didSet {
|
|
didReload?()
|
|
update()
|
|
}
|
|
}
|
|
|
|
var rateToken1: String = "1" {
|
|
didSet {
|
|
update()
|
|
}
|
|
}
|
|
var rateToken2: String = "1" {
|
|
didSet {
|
|
update()
|
|
}
|
|
}
|
|
var rate: Decimal {
|
|
(rateToken2.toOptionalDecimal() ?? 1.0) / ((rateToken1.toOptionalDecimal() ?? 1.0) > 0 ? (rateToken1.toOptionalDecimal() ?? 1.0) : 1.0)
|
|
}
|
|
var rateSend: String {
|
|
rateToken1 == "1" ? rateToken2 : rateToken1
|
|
}
|
|
|
|
var token1Precision: Int { 2 }
|
|
var amount: String = "" {
|
|
didSet {
|
|
limitMin = amount
|
|
limitMax = amount
|
|
didReload?()
|
|
update()
|
|
}
|
|
}
|
|
var token2Precision: Int {
|
|
p2pListsService.currencies.where({ $0.id == tokenGet?.id ?? -1 }).first?.precision ?? 0
|
|
}
|
|
var amountToken2: String {
|
|
if let amount = amount.toOptionalDecimal() {
|
|
return (amount * rate).toNormalizedString(precisionMax: token2Precision)
|
|
} else {
|
|
return ""
|
|
}
|
|
}
|
|
|
|
var limitMin = "" { didSet { update() } }
|
|
var limitMinToken2: String {
|
|
if let amountLimitMin = limitMin.toOptionalDecimal() {
|
|
return (amountLimitMin * rate).toNormalizedString(precisionMax: token2Precision)
|
|
} else {
|
|
return ""
|
|
}
|
|
}
|
|
var limitMax = "" { didSet { update() } }
|
|
var limitMaxToken2: String {
|
|
if let amountLimitMax = limitMax.toOptionalDecimal() {
|
|
return (amountLimitMax * rate).toNormalizedString(precisionMax: token2Precision)
|
|
} else {
|
|
return ""
|
|
}
|
|
}
|
|
|
|
var link = "" { didSet { update() } }
|
|
|
|
var vcardNumber = "" { didSet { update() } }
|
|
var vcardName = "" { didSet { update() } }
|
|
var vcardExpiration = "" { didSet { update() } }
|
|
var vcardCVV = "" { didSet { update() } }
|
|
|
|
var country: (id: Int, title: String, description: String)? {
|
|
didSet {
|
|
city = nil
|
|
bank = []
|
|
update()
|
|
}
|
|
}
|
|
|
|
var city: (id: Int, title: String, description: String)? {
|
|
didSet {
|
|
update()
|
|
}
|
|
}
|
|
|
|
var bank: [(id: Int, title: String, description: String)] = [] {
|
|
didSet {
|
|
update()
|
|
}
|
|
}
|
|
|
|
var account = "" { didSet { update() } }
|
|
|
|
var memo = "" { didSet { update() } }
|
|
|
|
var didUpdate: (() -> Void)?
|
|
var didReload: (() -> Void)?
|
|
|
|
// var rate: String {
|
|
// token != nil
|
|
// ? L10n.P2p.Order.Rate.text(token?.symbol ?? "", "\(token?.symbol.prefix(3) ?? "")")
|
|
// : " "
|
|
// }
|
|
|
|
var maxValue: String {
|
|
if let order = order {
|
|
return order.volume.decimalValue
|
|
.roundDown(to: token1Precision).toString(max: token1Precision, group: false)
|
|
}
|
|
|
|
let D = (token?.amount ?? 0).receive(precision: 5)
|
|
if D < 0.01 {
|
|
return "0"
|
|
}
|
|
if kind == .sell {
|
|
let V = (D/1.1).roundUp(to: 5)
|
|
let S = V.commission(percent: 0.1, precision: 5, ignoreMaxCommission: true)
|
|
return (D - S).roundDown(to: token1Precision).toString(max: token1Precision, group: false)
|
|
} else {
|
|
return (10*D).roundDown(to: token1Precision).toString(max: token1Precision, group: false)
|
|
}
|
|
}
|
|
|
|
var isFast = false
|
|
|
|
let uuid = UUID().uuidString
|
|
|
|
private var _limitMin: String { limitMin.isEmpty ? (limitMax.isEmpty ? amount : limitMax) : limitMin }
|
|
private var _limitMax: String { limitMax.isEmpty ? (limitMin.isEmpty ? amount : limitMin) : limitMax }
|
|
|
|
private var meta: String {
|
|
var info = [String:Any]()
|
|
info["uuid"] = uuid
|
|
info["type"] = contentType.rawValue
|
|
info["params"] = [String: Any]()
|
|
|
|
if let data = try? JSONSerialization.data(withJSONObject: info, options: .fragmentsAllowed) {
|
|
return String(data: data, encoding: .utf8) ?? "{}"
|
|
}
|
|
return "{}"
|
|
}
|
|
|
|
private var data: String {
|
|
return "{}"
|
|
}
|
|
|
|
var amountError: String? {
|
|
if token == nil { return nil }
|
|
if amount.toDecimal() > maxValue.toDecimal() { return L10n.P2p.Error.balance }
|
|
if let order = order {
|
|
if amount.toDecimal() > order.volume.decimalValue {
|
|
return L10n.P2p.Error.balance
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
var minError: String? {
|
|
if token == nil { return nil }
|
|
if amount.toDecimal() < _limitMin.toDecimal() { return L10n.P2p.Error.Limit.less }
|
|
return nil
|
|
}
|
|
var maxError: String? {
|
|
if token == nil { return nil }
|
|
if _limitMax.toDecimal() > amount.toDecimal() { return L10n.P2p.Error.Limit.more }
|
|
if _limitMin.toDecimal() > _limitMax.toDecimal() { return L10n.P2p.Error.Limit.less }
|
|
return nil
|
|
}
|
|
|
|
private(set) var isValidLimit = false
|
|
|
|
private(set) var isValid = false
|
|
|
|
init(kind: P2P.Model.Kind, contentType: P2P.Model.ContentType = .card,
|
|
token: Network.Model.Token? = nil, order: P2POrder? = nil) {
|
|
self.kind = kind
|
|
self.order = order
|
|
self.contentType = contentType
|
|
if let token = token {
|
|
self.token = P2P.Service.tokens.first(token)
|
|
}
|
|
}
|
|
|
|
var insurance: Decimal { amount.toDecimal().commission(percent: 0.1, precision: 5, ignoreMaxCommission: true) }
|
|
var commission: Decimal {
|
|
((kind == .buy ? Decimal(0) : amount.toDecimal()) + insurance)
|
|
.send(precision: 5).commission(precision: 5)
|
|
}
|
|
|
|
private var isVcardExpirationValid: Bool {
|
|
let month = vcardExpiration.components(separatedBy: "/").first ?? ""
|
|
let monthInt = (Int(vcardExpiration.components(separatedBy: "/").first ?? "") ?? 0)
|
|
let year = vcardExpiration.components(separatedBy: "/").last ?? ""
|
|
let yearInt = (Int(vcardExpiration.components(separatedBy: "/").last ?? "") ?? 0)
|
|
|
|
return vcardExpiration.count == 5 && month.count == 2 && year.count == 2
|
|
&& monthInt > 0 && monthInt <= 12
|
|
&& yearInt > 20
|
|
}
|
|
|
|
private func update() {
|
|
isValidLimit =
|
|
amount.toDecimal() > 0
|
|
&& rateToken1.toDecimal() > 0
|
|
&& rateToken2.toDecimal() > 0
|
|
&& limitMin.toDecimal() > 0
|
|
&& limitMax.toDecimal() > 0
|
|
&& amount.toDecimal() <= maxValue.toDecimal()
|
|
&& _limitMax.toDecimal() >= _limitMin.toDecimal()
|
|
&& _limitMax.toDecimal() <= amount.toDecimal()
|
|
&& amountToken2.toDecimal() > 0
|
|
switch contentType {
|
|
case .card:
|
|
isValid = isValidLimit
|
|
&& rateToken1.toOptionalDecimal() != nil
|
|
&& rateToken2.toOptionalDecimal() != nil
|
|
&& tokenGet != nil
|
|
&& (kind == .sell || (country != nil && !bank.isEmpty))
|
|
&& (kind == .buy || (!(card?.number ?? "").isEmpty && card?.countryId != nil && card?.bankId != nil))
|
|
case .url:
|
|
isValid = isValidLimit
|
|
&& (kind == .sell || rateToken1.toOptionalDecimal() != nil)
|
|
&& (kind == .sell || rateToken2.toOptionalDecimal() != nil)
|
|
&& tokenGet != nil && country != nil
|
|
&& (kind == .buy || !link.isEmpty)
|
|
case .cash:
|
|
isValid = isValidLimit
|
|
&& rateToken1.toOptionalDecimal() != nil
|
|
&& rateToken2.toOptionalDecimal() != nil
|
|
&& tokenGet != nil && country != nil && city != nil
|
|
case .vcard:
|
|
isValid = isValidLimit
|
|
&& rateToken1.toOptionalDecimal() != nil
|
|
&& rateToken2.toOptionalDecimal() != nil
|
|
&& tokenGet != nil && country != nil
|
|
&& (kind == .sell || (Cards.Model.cleaned(number: vcardNumber).count >= 12
|
|
&& Cards.Model.cleaned(number: vcardNumber).count <= 19
|
|
&& CCValidator.validate(creditCardNumber: Cards.Model.cleaned(number: vcardNumber), validatePrefix: false, validateLength: true, useLuhnAlgorithm: true)))
|
|
&& (kind == .sell || !vcardName.isEmpty)
|
|
&& (kind == .sell || isVcardExpirationValid)
|
|
&& (kind == .sell || vcardCVV.count == 3)
|
|
&& (kind == .sell || !bank.isEmpty)
|
|
case .emoney:
|
|
isValid = isValidLimit
|
|
&& rateToken1.toOptionalDecimal() != nil
|
|
&& rateToken2.toOptionalDecimal() != nil
|
|
&& tokenGet != nil && paymentSystem != nil
|
|
&& (kind == .buy || !account.isEmpty)
|
|
case .crypto:
|
|
isValid = isValidLimit
|
|
&& rateToken1.toOptionalDecimal() != nil
|
|
&& rateToken2.toOptionalDecimal() != nil
|
|
&& tokenGet != nil && blockchain != nil
|
|
&& (kind == .buy || !account.isEmpty)
|
|
}
|
|
if let order = order {
|
|
if amount.toDecimal() > order.volume.decimalValue {
|
|
isValid = false
|
|
}
|
|
}
|
|
didUpdate?()
|
|
}
|
|
|
|
private func prepareOrderData() -> String? {
|
|
let username = Accounts().current?.username ?? ""
|
|
let timeText = Date().toISODateString
|
|
let signature = Network.Service.Blockchain.signWithK1(privateKey: AccountViewAuthorize.getPrivateKey(), data: timeText.data(using: .utf8) ?? Data())
|
|
|
|
switch contentType {
|
|
case .card:
|
|
return """
|
|
{
|
|
"data_type": "\(contentType.rawValue)",
|
|
"signature": {
|
|
"username": "\(username)",
|
|
"text": "\(timeText)",
|
|
"signature": "\(signature)"
|
|
},
|
|
"uuid": "\(uuid)",
|
|
"payload": {
|
|
"currency_id": \(tokenGet?.id ?? -1),
|
|
"rate": "\(rateSend)",
|
|
"is_cash_rate": "\(rateToken1 == "1")",
|
|
\(kind == .buy ? "" : "\"country_id\": \(card?.countryId ?? -1),")
|
|
\(kind == .buy ? "" : "\"bank_ids\": \([card?.bankId ?? -1]),")
|
|
\(kind == .sell ? "" : "\"country_id\": \(country?.id ?? -1),")
|
|
\(kind == .sell ? "" : "\"bank_ids\": [\(bank.map(\.id).map({ "\($0)" }).joined(separator: ","))],")
|
|
\(kind == .buy ? "" : "\"card_number\": \"\(Cards.Model.cleaned(number: card?.number ?? ""))\",")
|
|
"memo": "\(escapedMemo)"
|
|
}
|
|
}
|
|
"""
|
|
case .url:
|
|
return """
|
|
{
|
|
"data_type": "\(contentType.rawValue)",
|
|
"signature": {
|
|
"username": "\(username)",
|
|
"text": "\(timeText)",
|
|
"signature": "\(signature)"
|
|
},
|
|
"uuid": "\(uuid)",
|
|
"payload": {
|
|
"currency_id": \(tokenGet?.id ?? -1),
|
|
"rate": "\(rateSend)",
|
|
"is_cash_rate": "\(rateToken1 == "1")",
|
|
"country_id": \(country?.id ?? -1),
|
|
\(kind == .buy ? "" : "\"payment_url\": \"\(link)\",")
|
|
"memo": "\(escapedMemo)"
|
|
}
|
|
}
|
|
"""
|
|
case .cash:
|
|
return """
|
|
{
|
|
"data_type": "\(contentType.rawValue)",
|
|
"signature": {
|
|
"username": "\(username)",
|
|
"text": "\(timeText)",
|
|
"signature": "\(signature)"
|
|
},
|
|
"uuid": "\(uuid)",
|
|
"payload": {
|
|
"currency_id": \(tokenGet?.id ?? -1),
|
|
"rate": "\(rateSend)",
|
|
"is_cash_rate": "\(rateToken1 == "1")",
|
|
"country_id": \(country?.id ?? -1),
|
|
"city_id": \(city?.id ?? -1),
|
|
"memo": "\(escapedMemo)"
|
|
}
|
|
}
|
|
"""
|
|
case .vcard:
|
|
return """
|
|
{
|
|
"data_type": "\(contentType.rawValue)",
|
|
"signature": {
|
|
"username": "\(username)",
|
|
"text": "\(timeText)",
|
|
"signature": "\(signature)"
|
|
},
|
|
"uuid": "\(uuid)",
|
|
"payload": {
|
|
"currency_id": \(tokenGet?.id ?? -1),
|
|
"rate": "\(rateSend)",
|
|
"is_cash_rate": "\(rateToken1 == "1")",
|
|
"country_id": \(country?.id ?? -1),
|
|
\(kind == .sell ? "" : "\"card_number\": \"\(Cards.Model.cleaned(number: vcardNumber))\",")
|
|
\(kind == .sell ? "" : "\"card_name\": \"\(vcardName)\",")
|
|
\(kind == .sell ? "" : "\"card_expiry\": \"\(vcardExpiration)\",")
|
|
\(kind == .sell ? "" : "\"cvv\": \"\(vcardCVV)\",")
|
|
\(kind == .sell ? "" : "\"bank_ids\": [\(bank.map(\.id).map({ "\($0)" }).joined(separator: ","))],")
|
|
"memo": "\(escapedMemo)"
|
|
}
|
|
}
|
|
"""
|
|
case .emoney:
|
|
return """
|
|
{
|
|
"data_type": "\(contentType.rawValue)",
|
|
"signature": {
|
|
"username": "\(username)",
|
|
"text": "\(timeText)",
|
|
"signature": "\(signature)"
|
|
},
|
|
"uuid": "\(uuid)",
|
|
"payload": {
|
|
"currency_id": \(tokenGet?.id ?? -1),
|
|
"rate": "\(rateSend)",
|
|
"is_cash_rate": "\(rateToken1 == "1")",
|
|
"payment_system_id": \(paymentSystem?.id ?? -1),
|
|
\(kind == .buy ? "" : "\"wallet_number\": \"\(account)\",")
|
|
"memo": "\(escapedMemo)"
|
|
}
|
|
}
|
|
"""
|
|
case .crypto:
|
|
return """
|
|
{
|
|
"data_type": "\(contentType.rawValue)",
|
|
"signature": {
|
|
"username": "\(username)",
|
|
"text": "\(timeText)",
|
|
"signature": "\(signature)"
|
|
},
|
|
"uuid": "\(uuid)",
|
|
"payload": {
|
|
"currency_id": \(tokenGet?.id ?? -1),
|
|
"rate": "\(rateSend)",
|
|
"is_cash_rate": "\(rateToken1 == "1")",
|
|
"blockchain_id": \(blockchain?.id ?? -1),
|
|
\(kind == .buy ? "" : "\"wallet_address\": \"\(account)\",")
|
|
"memo": "\(escapedMemo)"
|
|
}
|
|
}
|
|
"""
|
|
}
|
|
}
|
|
|
|
private var escapedMemo: String {
|
|
memo.replacingOccurrences(of: "\"", with: "\\\"")
|
|
}
|
|
|
|
func submit(privateKey: String, completion: @escaping (Error?) -> Void) {
|
|
var actions = [(contract: String, action: Network.Model.Blockchain.Action, data: Codable)]()
|
|
|
|
let value = (kind == .buy ? Decimal(0) : amount.toDecimal()) + insurance
|
|
|
|
if let order = order {
|
|
actions.append((Network.servers.current.contract(.p2p), kind == .sell ? .clssellord : .clsbuyord, ["ord_id": "\(order.id)", "token": order.symbol]))
|
|
let valueInOrder = kind == .sell ? order.volume.decimalValue + order.insurance.decimalValue : order.insurance.decimalValue
|
|
if valueInOrder > value {
|
|
if !Wallet.Service.Tokens.shared.balances.collection.contains(where: { $0.symbol == order.symbol }) {
|
|
actions.append((
|
|
Network.servers.current.contract(.cash),
|
|
.open, [
|
|
"owner": Accounts().current?.username ?? "",
|
|
"symbol": "5,\(order.symbol)",
|
|
"ram_payer": Accounts().current?.username ?? ""
|
|
]
|
|
))
|
|
}
|
|
actions.append((
|
|
Network.servers.current.contract(.p2p),
|
|
.withdraw, [
|
|
"owner" : Accounts().current?.username ?? "",
|
|
"value" : (valueInOrder - value).toCanonicalString(precision: 5, symbol: order.symbol),
|
|
]
|
|
))
|
|
}
|
|
} else {
|
|
if !(p2pBalancesService.balances.filter("symbol == '\(token?.symbol ?? "")'").count > 0) {
|
|
actions.append((Network.servers.current.contract(.p2p), .open, [
|
|
"owner" : Accounts().current?.username ?? "",
|
|
"symbol" : "5, \(token?.symbol ?? "")",
|
|
"ram_payer" : Accounts().current?.username ?? ""
|
|
]))
|
|
}
|
|
actions.append((Network.servers.current.contract(.cash), .transfer, Network.Model.Blockchain.Transfer(
|
|
from: Accounts().current?.username ?? "",
|
|
to: Network.servers.current.contract(.p2p),
|
|
quantity: (value + commission).toString(precision: 5, symbol: token?.symbol),
|
|
memo: "deposit"
|
|
)))
|
|
}
|
|
|
|
actions.append((Network.servers.current.contract(.p2p), kind == .buy ? .crtbuyord : .crtsellord, [
|
|
"user" : Accounts().current?.username ?? "",
|
|
"volume" : amount.toDecimal().toString(precision: 5, symbol: token?.symbol),
|
|
"insurance" : insurance.toString(precision: 5, symbol: token?.symbol),
|
|
"min_package" : _limitMin.toDecimal().toString(precision: 5, symbol: token?.symbol),
|
|
"max_package" : _limitMax.toDecimal().toString(precision: 5, symbol: token?.symbol),
|
|
"meta" : meta,
|
|
"data" : data
|
|
]))
|
|
|
|
Network.Service.Blockchain.signActions(actions: actions, privateKeys: [privateKey]) { [weak self] result in
|
|
switch result {
|
|
case let .success((signatures, serializedTransaction)):
|
|
let signaturesString = signatures.map({ "\"\($0)\"" }).joined(separator: ",")
|
|
let transaction = Accounts().quota.isEnabled
|
|
? "{\"serializedTransaction\":\"\(serializedTransaction)\",\"signatures\":[\(signaturesString)]}"
|
|
: "{\"compression\":0,\"packed_trx\":\"\(serializedTransaction)\",\"packed_context_free_data\":\"\",\"signatures\":[\(signaturesString)]}"
|
|
let variables = PushTransactionGraphQLRequest.Variables(
|
|
transaction: transaction,
|
|
transactionType: self?.kind == .buy ? "P2P_CREATE_BUY_ORDER" : "P2P_CREATE_SELL_ORDER",
|
|
data: self?.prepareOrderData(),
|
|
isUsingFreetx: Accounts().quota.isEnabled
|
|
)
|
|
|
|
AF.request(
|
|
Network.servers.current.paycashQraphql,
|
|
method: .post,
|
|
parameters: PushTransactionGraphQLRequest(variables: variables),
|
|
encoder: JSONParameterEncoder.default
|
|
).responseDecodable(of: GraphQLResponse<PushTransactionGraphQLRequest.ResponseData>.self) { response in
|
|
print(String(data: response.data ?? Data(), encoding: .utf8) ?? "")
|
|
completion(response.value?.data?.pushTransaction?.errorsDescription ?? response.value?.errorsDescription)
|
|
}
|
|
case let .failure(error):
|
|
completion(error)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension P2POrder {
|
|
|
|
// swiftlint:disable identifier_name
|
|
private struct TogglePause: Codable {
|
|
let ord_id: Int
|
|
let token: String
|
|
}
|
|
|
|
func pause(privateKey: String, completion: @escaping (NSError?) -> Void) {
|
|
Network.Service.Blockchain.execute(
|
|
contract: .p2p,
|
|
action: kind == .sell ? .blsellord : .blbuyord,
|
|
data: TogglePause(ord_id: id, token: symbol),
|
|
privateKeys: [privateKey]
|
|
) { [weak self] (result) in
|
|
if case .success(_) = result {
|
|
// swiftlint:disable force_try
|
|
try! self?.modify({ $0.status = .inactive })
|
|
}
|
|
completion(.error(result))
|
|
}
|
|
}
|
|
|
|
func resume(privateKey: String, completion: @escaping (NSError?) -> Void) {
|
|
Network.Service.Blockchain.execute(
|
|
contract: .p2p,
|
|
action: kind == .sell ? .unblsellord : .unblbuyord,
|
|
data: TogglePause(ord_id: id, token: symbol),
|
|
privateKeys: [privateKey]
|
|
) { [weak self] (result) in
|
|
if case .success(_) = result {
|
|
// swiftlint:disable force_try
|
|
try! self?.modify({ $0.status = .active })
|
|
}
|
|
completion(.error(result))
|
|
}
|
|
}
|
|
}
|
|
|
|
extension P2POrder {
|
|
|
|
// swiftlint:disable identifier_name
|
|
private struct Cancel: Codable {
|
|
let ord_id: Int
|
|
let token: String
|
|
}
|
|
|
|
func cancel(privateKey: String, completion: @escaping (NSError?) -> Void) {
|
|
var actions = [(
|
|
contract: Network.Service.Blockchain.Contract,
|
|
action: Network.Service.Blockchain.Action,
|
|
data: Codable
|
|
)]()
|
|
actions.append((.p2p, kind == .sell ? .clssellord : .clsbuyord, Cancel(ord_id: id, token: symbol)))
|
|
let value = kind == .sell ? volume.decimalValue + insurance.decimalValue : insurance.decimalValue
|
|
if value > kCommissionMin {
|
|
if !Wallet.Service.Tokens.shared.balances.collection.contains(where: { $0.symbol == symbol }) {
|
|
actions.append((
|
|
.cash,
|
|
.open, [
|
|
"owner": Accounts().current?.username ?? "",
|
|
"symbol": "5,\(symbol)",
|
|
"ram_payer": Accounts().current?.username ?? ""
|
|
]
|
|
))
|
|
}
|
|
actions.append((
|
|
.p2p,
|
|
.withdraw, [
|
|
"owner" : Accounts().current?.username ?? "",
|
|
"value" : value.toCanonicalString(precision: 5, symbol: symbol),
|
|
]
|
|
))
|
|
}
|
|
Network.Service.Blockchain.execute(actions: actions, privateKeys: [privateKey]) { (result) in
|
|
if case .success(_) = result {
|
|
try? self.delete()
|
|
}
|
|
completion(.error(result))
|
|
}
|
|
}
|
|
}
|