taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

Model+P2P.swift (11031B)


      1 /*
      2  * This file is part of GNU Taler, ©2022-25 Taler Systems S.A.
      3  * See LICENSE.md
      4  */
      5 /**
      6  * @author Marc Stibane
      7  */
      8 import Foundation
      9 import taler_swift
     10 import AnyCodable
     11 //import SymLog
     12 
     13 // MARK: common structures
     14 struct PeerContractTerms: Codable {
     15     let amount: Amount
     16     let summary: String
     17     // Optional only for InitiatePeerPushDebit:
     18     // if nil then defaultExpiration is taken
     19     let purse_expiration: Timestamp?
     20     let icon_id: String?
     21 }
     22 // MARK: - max PeerPushDebit amount
     23 /// Check if initiating a peer push payment is possible, check fees
     24 fileprivate struct AmountResponse: Codable {
     25     let effectiveAmount: Amount?
     26     let rawAmount: Amount
     27 }
     28 fileprivate struct GetMaxPeerPushDebitAmount: WalletBackendFormattedRequest {
     29     typealias Response = AmountResponse
     30     func operation() -> String { "getMaxPeerPushDebitAmount" }
     31     func args() -> Args { Args(currency: scope.currency,
     32                           restrictScope: scope) }
     33     var scope: ScopeInfo
     34     struct Args: Encodable {
     35         var currency: String
     36         var restrictScope: ScopeInfo
     37     }
     38 }
     39 extension WalletModel {
     40     nonisolated func getMaxPeerPushDebitAmount(_ scope: ScopeInfo,
     41                                            viewHandles: Bool = false)
     42       async throws -> Amount {
     43         let request = GetMaxPeerPushDebitAmount(scope: scope)
     44         let response = try await sendRequest(request, viewHandles: viewHandles)
     45         return response.rawAmount
     46     }
     47 } // getMaxPeerPushAmount
     48 // MARK: - check PeerPushDebit
     49 struct CheckPeerPushDebitResponse: Codable {
     50     let exchangeBaseUrl: String?                // API "2:0:1"
     51     let amountRaw: Amount
     52     let amountEffective: Amount
     53     let defaultExpiration: Duration
     54     let maxExpirationDate: Timestamp?           // API "2:0:1" TODO: limit expiration (30 days or 7 days)
     55 }
     56 fileprivate struct CheckPeerPushDebit: WalletBackendFormattedRequest {
     57     typealias Response = CheckPeerPushDebitResponse
     58     func operation() -> String { "checkPeerPushDebit" }
     59     func args() -> Args { Args(amount: amount,
     60                         restrictScope: scope,
     61                  clientCancellationId: "cancel") }
     62     var amount: Amount
     63     var scope: ScopeInfo
     64     struct Args: Encodable {
     65         var amount: Amount
     66         var restrictScope: ScopeInfo
     67         var clientCancellationId: String?
     68     }
     69 }
     70 extension WalletModel {
     71     nonisolated func checkPeerPushDebit(_ amount: Amount,
     72                                            scope: ScopeInfo,
     73                                      viewHandles: Bool = false)
     74       async throws -> CheckPeerPushDebitResponse {
     75         let request = CheckPeerPushDebit(amount: amount, scope: scope)
     76         let response = try await sendRequest(request, viewHandles: viewHandles)
     77         return response
     78     }
     79 } // checkPeerPushDebit
     80 // MARK: - Initiate PeerPushDebit
     81 /// Initiate an outgoing peer push payment, send coins
     82 struct InitiatePeerPushDebitResponse: Codable {
     83     let contractPriv: String
     84     let mergePriv: String
     85     let pursePub: String
     86     let exchangeBaseUrl: String
     87     let talerUri: String?
     88     let transactionId: String
     89 }
     90 fileprivate struct InitiatePeerPushDebit: WalletBackendFormattedRequest {
     91     typealias Response = InitiatePeerPushDebitResponse
     92     func operation() -> String { "initiatePeerPushDebit" }
     93     func args() -> Args { Args(restrictScope: scope,
     94                         partialContractTerms: terms) }
     95     var scope: ScopeInfo
     96     var terms: PeerContractTerms
     97     struct Args: Encodable {
     98         var restrictScope: ScopeInfo
     99         var partialContractTerms: PeerContractTerms
    100     }
    101 }
    102 extension WalletModel {
    103     nonisolated func initiatePeerPushDebit(scope: ScopeInfo,
    104                                            terms: PeerContractTerms,
    105                                      viewHandles: Bool = false)
    106       async throws -> InitiatePeerPushDebitResponse {
    107         let request = InitiatePeerPushDebit(scope: scope, terms: terms)
    108         let response = try await sendRequest(request, viewHandles: viewHandles)
    109         return response
    110     }
    111 } // initiatePeerPushDebit
    112 // MARK: - PeerPullCredit
    113 /// Check fees before sending a request(invoice) to another wallet
    114 struct CheckPeerPullCreditResponse: Codable {
    115     let scopeInfo: ScopeInfo?
    116     let exchangeBaseUrl: String?
    117     let amountRaw: Amount
    118     let amountEffective: Amount
    119     var numCoins: Int?                  // Number of coins this amountEffective will create
    120 }
    121 fileprivate struct CheckPeerPullCredit: WalletBackendFormattedRequest {
    122     typealias Response = CheckPeerPullCreditResponse
    123     func operation() -> String { "checkPeerPullCredit" }
    124     func args() -> Args { Args(amount: amount,
    125                         restrictScope: scope,
    126                       exchangeBaseUrl: scope?.url,
    127                  clientCancellationId: "cancel") }
    128     var amount: Amount
    129     var scope: ScopeInfo?
    130     struct Args: Encodable {
    131         var amount: Amount
    132         var restrictScope: ScopeInfo?
    133         var exchangeBaseUrl: String?
    134         var clientCancellationId: String?
    135     }
    136 }
    137 extension WalletModel {
    138     nonisolated func checkPeerPullCredit(_ amount: Amount,
    139                                             scope: ScopeInfo,
    140                                       viewHandles: Bool = false)
    141       async throws -> CheckPeerPullCreditResponse {
    142         let request = CheckPeerPullCredit(amount: amount, scope: scope)
    143         let response = try await sendRequest(request, viewHandles: viewHandles)
    144         return response
    145     }
    146 } // checkPeerPullCredit
    147 // - - - - - -
    148 /// Initiate an outgoing peer pull payment, send a request(invoice)
    149 struct InitiatePeerPullCreditResponse: Codable {
    150     let talerUri: String
    151     let transactionId: String
    152 }
    153 fileprivate struct InitiatePeerPullCredit: WalletBackendFormattedRequest {
    154     typealias Response = InitiatePeerPullCreditResponse
    155     func operation() -> String { "initiatePeerPullCredit" }
    156     func args() -> Args { Args(exchangeBaseUrl: exchangeBaseUrl,
    157                           partialContractTerms: partialContractTerms) }
    158     var exchangeBaseUrl: String?
    159     var partialContractTerms: PeerContractTerms
    160     struct Args: Encodable {
    161         var exchangeBaseUrl: String?
    162         var partialContractTerms: PeerContractTerms
    163     }
    164 }
    165 extension WalletModel {
    166     nonisolated func initiatePeerPullCredit(_ baseURL: String?,
    167                                                 terms: PeerContractTerms,
    168                                           viewHandles: Bool = false)
    169       async throws -> InitiatePeerPullCreditResponse {
    170         let request = InitiatePeerPullCredit(exchangeBaseUrl: baseURL,
    171                                         partialContractTerms: terms)
    172         let response = try await sendRequest(request, viewHandles: viewHandles)
    173         return response
    174     }
    175 } // initiatePeerPullCredit
    176 // MARK: - PeerPushCredit
    177 /// Prepare an incoming peer push payment, receive coins
    178 struct PreparePeerPushCreditResponse: Codable {
    179     let contractTerms: PeerContractTerms
    180     let amountRaw: Amount
    181     let amountEffective: Amount
    182     // TODO: the dialog transaction is already created in the local DB - must either accept or delete
    183     let transactionId: String
    184     let txState: TransactionState
    185     let exchangeBaseUrl: String         // need (exchange.tosStatus == .accepted) for incoming coins
    186     let scopeInfo: ScopeInfo
    187 }
    188 fileprivate struct PreparePeerPushCredit: WalletBackendFormattedRequest {
    189     typealias Response = PreparePeerPushCreditResponse
    190     func operation() -> String { "preparePeerPushCredit" }
    191     func args() -> Args { Args(talerUri: talerUri) }
    192 
    193     var talerUri: String
    194     struct Args: Encodable {
    195         var talerUri: String
    196     }
    197 }
    198 extension WalletModel {
    199     nonisolated func preparePeerPushCredit(_ talerUri: String,
    200                                           viewHandles: Bool = false)
    201       async throws -> PreparePeerPushCreditResponse {
    202         let request = PreparePeerPushCredit(talerUri: talerUri)
    203         let response = try await sendRequest(request, viewHandles: viewHandles)
    204         return response
    205     }
    206 } // preparePeerPushCredit
    207 // - - - - - -
    208 /// Accept an incoming peer push payment
    209 fileprivate struct AcceptPeerPushCredit: WalletBackendFormattedRequest {
    210     struct Response: Decodable {}   // no result - getting no error back means success
    211     func operation() -> String { "confirmPeerPushCredit" } // should be "acceptPeerPushCredit"
    212     func args() -> Args { Args(transactionId: transactionId) }
    213 
    214     var transactionId: String
    215     struct Args: Encodable {
    216         var transactionId: String
    217     }
    218 }
    219 extension WalletModel {
    220     nonisolated func acceptPeerPushCredit(_ transactionId: String,
    221                                               viewHandles: Bool = false)
    222       async throws -> Decodable {
    223         let request = AcceptPeerPushCredit(transactionId: transactionId)
    224         let response = try await sendRequest(request, viewHandles: viewHandles)
    225         return response
    226     }
    227 } // acceptPeerPushCredit
    228 // MARK: - PeerPullDebit
    229 /// Prepare an incoming peer push request(invoice), pay coins
    230 struct PreparePeerPullDebitResponse: Codable {
    231     let contractTerms: PeerContractTerms
    232     let amountRaw: Amount
    233     let amountEffective: Amount
    234     // TODO: the dialog transaction is already created in the local DB - must either accept or delete
    235     let transactionId: String
    236     let txState: TransactionState
    237 //    let exchangeBaseUrl: String       use scope instead
    238     let scopeInfo: ScopeInfo
    239 }
    240 fileprivate struct PreparePeerPullDebit: WalletBackendFormattedRequest {
    241     typealias Response = PreparePeerPullDebitResponse
    242     func operation() -> String { "preparePeerPullDebit" }
    243     func args() -> Args { Args(talerUri: talerUri) }
    244 
    245     var talerUri: String
    246     struct Args: Encodable {
    247         var talerUri: String
    248     }
    249 }
    250 extension WalletModel {
    251     nonisolated func preparePeerPullDebit(_ talerUri: String,
    252                                          viewHandles: Bool = false)
    253       async throws -> PreparePeerPullDebitResponse {
    254         let request = PreparePeerPullDebit(talerUri: talerUri)
    255         let response = try await sendRequest(request, viewHandles: viewHandles)
    256         return response
    257     }
    258 } // preparePeerPullDebit
    259 // - - - - - -
    260 /// Confirm incoming peer push request(invoice) and pay
    261 fileprivate struct ConfirmPeerPullDebit: WalletBackendFormattedRequest {
    262     struct Response: Decodable {}   // no result - getting no error back means success
    263     func operation() -> String { "confirmPeerPullDebit" }
    264     func args() -> Args { Args(transactionId: transactionId) }
    265 
    266     var transactionId: String
    267     struct Args: Encodable {
    268         var transactionId: String
    269     }
    270 }
    271 extension WalletModel {
    272     nonisolated func confirmPeerPullDebit(_ transactionId: String,
    273                                               viewHandles: Bool = false)
    274       async throws -> Decodable {
    275         let request = ConfirmPeerPullDebit(transactionId: transactionId)
    276         let response = try await sendRequest(request, viewHandles: viewHandles)
    277         return response
    278     }
    279 } // confirmPeerPullDebit