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