taler-ios

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

Model+P2P.swift (11691B)


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