taler-ios

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

Model+P2P.swift (10900B)


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