taler-ios

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

Model+P2P.swift (11368B)


      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 = 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 = 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 = 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 = 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 = 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) }
    199 
    200     var talerUri: String
    201     struct Args: Encodable {
    202         var talerUri: String
    203     }
    204 }
    205 extension WalletModel {
    206     nonisolated func preparePeerPushCredit(_ talerUri: String,
    207                                           viewHandles: Bool = false)
    208       async throws -> PreparePeerPushCreditResponse {
    209         let request = PreparePeerPushCredit(talerUri: talerUri)
    210         let response = try await sendRequest(request, viewHandles: viewHandles)
    211         return response
    212     }
    213 } // preparePeerPushCredit
    214 // - - - - - -
    215 /// Accept an incoming peer push payment
    216 fileprivate struct AcceptPeerPushCredit: WalletBackendFormattedRequest {
    217     struct Response: Decodable {}   // no result - getting no error back means success
    218     var operation: String { "confirmPeerPushCredit" } // should be "acceptPeerPushCredit"
    219     func args() -> Args { Args(transactionId: transactionId) }
    220 
    221     var transactionId: String
    222     struct Args: Encodable {
    223         var transactionId: String
    224     }
    225 }
    226 extension WalletModel {
    227     nonisolated func acceptPeerPushCredit(_ transactionId: String,
    228                                               viewHandles: Bool = false)
    229       async throws -> Decodable {
    230         let request = AcceptPeerPushCredit(transactionId: transactionId)
    231         let response = try await sendRequest(request, viewHandles: viewHandles)
    232         return response
    233     }
    234 } // acceptPeerPushCredit
    235 // MARK: - PeerPullDebit
    236 /// Prepare an incoming peer push request(invoice), pay coins
    237 struct PreparePeerPullDebitResponse: Codable {
    238     let contractTerms: PeerContractTerms
    239     let amountRaw: Amount
    240     let amountEffective: Amount
    241     // TODO: the dialog transaction is already created in the local DB - must either accept or delete
    242     let transactionId: String
    243     let txState: TransactionState
    244 //    let exchangeBaseUrl: String       use scope instead
    245     let scopeInfo: ScopeInfo
    246 }
    247 fileprivate struct PreparePeerPullDebit: WalletBackendFormattedRequest {
    248     typealias Response = PreparePeerPullDebitResponse
    249     var operation: String { "preparePeerPullDebit" }
    250     func args() -> Args { Args(talerUri: talerUri) }
    251 
    252     var talerUri: String
    253     struct Args: Encodable {
    254         var talerUri: String
    255     }
    256 }
    257 extension WalletModel {
    258     nonisolated func preparePeerPullDebit(_ talerUri: String,
    259                                          viewHandles: Bool = false)
    260       async throws -> PreparePeerPullDebitResponse {
    261         let request = PreparePeerPullDebit(talerUri: talerUri)
    262         let response = try await sendRequest(request, viewHandles: viewHandles)
    263         return response
    264     }
    265 } // preparePeerPullDebit
    266 // - - - - - -
    267 /// Confirm incoming peer push request(invoice) and pay
    268 fileprivate struct ConfirmPeerPullDebit: WalletBackendFormattedRequest {
    269     struct Response: Decodable {}   // no result - getting no error back means success
    270     var operation: String { "confirmPeerPullDebit" }
    271     func args() -> Args { Args(transactionId: transactionId) }
    272 
    273     var transactionId: String
    274     struct Args: Encodable {
    275         var transactionId: String
    276     }
    277 }
    278 extension WalletModel {
    279     nonisolated func confirmPeerPullDebit(_ transactionId: String,
    280                                               viewHandles: Bool = false)
    281       async throws -> Decodable {
    282         let request = ConfirmPeerPullDebit(transactionId: transactionId)
    283         let response = try await sendRequest(request, viewHandles: viewHandles)
    284         return response
    285     }
    286 } // confirmPeerPullDebit