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