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