taler-ios

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

Model+Deposit.swift (10121B)


      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 struct AccountChange: Codable {             // Notification
     14     enum TransitionType: String, Codable {
     15         case change = "bank-account-change"
     16     }
     17     var type: TransitionType
     18     var bankAccountId: String
     19 }
     20 
     21 // MARK: - IBAN
     22 struct ValidateIbanResult: Codable {
     23     let valid: Bool
     24 }
     25 /// A request to validate an IBAN.
     26 fileprivate struct ValidateIban: WalletBackendFormattedRequest {
     27     typealias Response = ValidateIbanResult
     28     func operation() -> String { "validateIban" }
     29     func args() -> Args { Args(iban: iban) }
     30 
     31     var iban: String
     32     struct Args: Encodable {
     33         var iban: String
     34     }
     35 }
     36 extension WalletModel {
     37     /// validate IBAN. No Networking
     38     nonisolated func validateIban(_ iban: String, viewHandles: Bool = false)
     39       async throws -> Bool {
     40         let request = ValidateIban(iban: iban)
     41         let response = try await sendRequest(request, viewHandles: viewHandles)
     42         return response.valid
     43     }
     44 }
     45 // MARK: - Deposit
     46 enum PaytoType: String, Codable {
     47     case unknown
     48     case iban
     49     case bitcoin
     50     case xTalerBank = "x-taler-bank"
     51 }
     52 
     53 struct WireTypeDetails: Codable {
     54     let paymentTargetType: PaytoType
     55     let talerBankHostnames: [String]
     56 }
     57 
     58 struct DepositWireTypesResponse: Codable {
     59     /// can be used to pre-filter payment target types to offer the user as an input option
     60 //    let wireTypes: [PaytoType]
     61     let wireTypeDetails: [WireTypeDetails]
     62 }
     63 
     64 /// A request to get wire types that can be used for a deposit operation.
     65 fileprivate struct DepositWireTypes: WalletBackendFormattedRequest {
     66     typealias Response = DepositWireTypesResponse
     67     func operation() -> String { "getDepositWireTypes" }
     68     func args() -> Args { Args(currency: currency, scopeInfo: scopeInfo) }
     69 
     70     var currency: String?
     71     var scopeInfo: ScopeInfo?
     72     struct Args: Encodable {
     73         // one of these must be set - don't set both to nil
     74         var currency: String?
     75         var scopeInfo: ScopeInfo?
     76     }
     77 }
     78 extension WalletModel {
     79     /// Get wire types that can be used for a deposit operation
     80     nonisolated func depositWireTypes(_ currency: String?, scopeInfo: ScopeInfo? = nil, viewHandles: Bool = false)
     81       async throws -> [WireTypeDetails] {
     82         let request = DepositWireTypes(currency: currency, scopeInfo: scopeInfo)
     83         let response = try await sendRequest(request, viewHandles: viewHandles)
     84         return response.wireTypeDetails
     85     }
     86 }
     87 // MARK: - max Deposit amount
     88 /// Check if initiating a deposit is possible, check fees
     89 fileprivate struct AmountResponse: Codable {
     90     let effectiveAmount: Amount?
     91     let rawAmount: Amount
     92 }
     93 fileprivate struct GetMaxDepositAmount: WalletBackendFormattedRequest {
     94     typealias Response = AmountResponse
     95     func operation() -> String { "getMaxDepositAmount" }
     96     func args() -> Args { Args(currency: scope.currency, restrictScope: scope) }
     97 
     98     var scope: ScopeInfo
     99     struct Args: Encodable {
    100         var currency: String
    101         var restrictScope: ScopeInfo
    102 //        var depositPaytoUri: String
    103     }
    104 }
    105 extension WalletModel {
    106     nonisolated func getMaxDepositAmount(_ scope: ScopeInfo, viewHandles: Bool = false)
    107       async throws -> Amount {
    108         let request = GetMaxDepositAmount(scope: scope)
    109         let response = try await sendRequest(request, viewHandles: viewHandles)
    110         return response.rawAmount
    111     }
    112 } // getMaxDepositAmount
    113 // MARK: - Deposit
    114 struct DepositFees: Codable {
    115     let coin: Amount
    116     let wire: Amount
    117     let refresh: Amount
    118 }
    119 struct CheckDepositResponse: Codable {
    120     let totalDepositCost: Amount
    121     let effectiveDepositAmount: Amount
    122     let fees: DepositFees
    123     let kycSoftLimit: Amount?
    124     let kycHardLimit: Amount?
    125     let kycExchanges: [String]?                 // Base URL of exchanges that would likely require soft KYC
    126 }
    127 /// A request to get an exchange's deposit contract terms.
    128 fileprivate struct CheckDeposit: WalletBackendFormattedRequest {
    129     typealias Response = CheckDepositResponse
    130     func operation() -> String { "checkDeposit" }
    131     func args() -> Args { Args(depositPaytoUri: depositPaytoUri,
    132                                         amount: amount,
    133                           clientCancellationId: "cancel") }
    134     var depositPaytoUri: String
    135     var amount: Amount
    136     struct Args: Encodable {
    137         var depositPaytoUri: String
    138         var amount: Amount
    139         var clientCancellationId: String?
    140     }
    141 }
    142 extension WalletModel {
    143     /// check fees for deposit. No Networking
    144     nonisolated func checkDeposit4711(_ depositPaytoUri: String, amount: Amount, viewHandles: Bool = false)
    145       async throws -> CheckDepositResponse {
    146         let request = CheckDeposit(depositPaytoUri: depositPaytoUri, amount: amount)
    147         let response = try await sendRequest(request, viewHandles: viewHandles)
    148         return response
    149     }
    150 }
    151 // MARK: -
    152 struct DepositGroupResult: Decodable {
    153     var transactionId: String
    154     var txState: TransactionState?
    155 }
    156 /// A request to deposit some coins.
    157 fileprivate struct CreateDepositGroup: WalletBackendFormattedRequest {
    158     typealias Response = DepositGroupResult
    159     func operation() -> String { "createDepositGroup" }
    160     func args() -> Args { Args(depositPaytoUri: depositPaytoUri,
    161                                  restrictScope: scope,
    162                                         amount: amount) }
    163     var depositPaytoUri: String
    164     var scope: ScopeInfo
    165     var amount: Amount
    166     struct Args: Encodable {
    167         var depositPaytoUri: String
    168         var restrictScope: ScopeInfo
    169         var amount: Amount
    170     }
    171 }
    172 extension WalletModel {
    173     /// deposit coins. Networking involved
    174     nonisolated func createDepositGroup(_ depositPaytoUri: String, scope: ScopeInfo, amount: Amount, viewHandles: Bool = false)
    175       async throws -> DepositGroupResult {
    176         let request = CreateDepositGroup(depositPaytoUri: depositPaytoUri, scope: scope, amount: amount)
    177         let response = try await sendRequest(request, viewHandles: viewHandles)
    178         return response
    179     }
    180 }
    181 // MARK: -
    182 struct BankAccountsInfo: Decodable, Hashable {
    183     var bankAccountId: String
    184     var paytoUri: String
    185     var kycCompleted: Bool
    186     var label: String?
    187 }
    188 struct BankAccounts: Decodable, Hashable {
    189     var accounts: [BankAccountsInfo]
    190 }
    191 /// A request to list known bank accounts.
    192 fileprivate struct ListBankAccounts: WalletBackendFormattedRequest {
    193     typealias Response = BankAccounts
    194     func operation() -> String { "listBankAccounts" }
    195     func args() -> Args { Args(currency: currency) }
    196     var currency: String?
    197     struct Args: Encodable {
    198         var currency: String?
    199     }
    200 }
    201 extension WalletModel {
    202     /// ask for known accounts. No networking
    203     nonisolated func listBankAccounts(_ currency: String? = nil, viewHandles: Bool = false)
    204       async throws -> [BankAccountsInfo] {
    205         let request = ListBankAccounts(currency: currency)
    206         let response = try await sendRequest(request, viewHandles: viewHandles)
    207         return response.accounts
    208     }
    209 }
    210 
    211 /// A request to get one bank account.
    212 fileprivate struct GetBankAccountById: WalletBackendFormattedRequest {
    213     typealias Response = BankAccountsInfo
    214     func operation() -> String { "getBankAccountById" }
    215     func args() -> Args { Args(bankAccountId: bankAccountId) }
    216     var bankAccountId: String
    217     struct Args: Encodable {
    218         var bankAccountId: String
    219     }
    220 }
    221 extension WalletModel {
    222     /// ask for a specific account. No networking
    223     nonisolated func getBankAccountById(_ bankAccountId: String, viewHandles: Bool = false)
    224       async throws -> BankAccountsInfo {
    225         let request = GetBankAccountById(bankAccountId: bankAccountId)
    226         let response = try await sendRequest(request, viewHandles: viewHandles)
    227         return response
    228     }
    229 }
    230 
    231 struct AddBankAccountResponse: Decodable, Hashable {
    232     var bankAccountId: String                       // Identifier of the added bank account
    233 }
    234 /// A request to add a known bank account.
    235 fileprivate struct AddBankAccount: WalletBackendFormattedRequest {
    236     typealias Response = AddBankAccountResponse
    237     func operation() -> String { "addBankAccount" }
    238     func args() -> Args { Args(paytoUri: uri,
    239                                   label: label,
    240                    replaceBankAccountId: replace) }
    241     var uri: String
    242     var label: String
    243     var replace: String?
    244     struct Args: Encodable {
    245         var paytoUri: String                        // bank account that should be added
    246         var label: String                           // Human-readable label
    247         var replaceBankAccountId: String?                // account that this new account should replace
    248     }
    249 }
    250 extension WalletModel {
    251     /// add (or update) a known account. No networking
    252     nonisolated func addBankAccount(_ uri: String,
    253                                     label: String,
    254                                   replace: String? = nil,
    255                               viewHandles: Bool = false)
    256       async throws -> String {
    257         let request = AddBankAccount(uri: uri, label: label, replace: replace)
    258         let response = try await sendRequest(request, viewHandles: viewHandles)
    259         return response.bankAccountId
    260     }
    261 }
    262 
    263 /// A request to forget a known bank account.
    264 fileprivate struct ForgetBankAccount: WalletBackendFormattedRequest {
    265     struct Response: Decodable {}   // no result - getting no error back means success
    266     func operation() -> String { "forgetBankAccount" }
    267     func args() -> Args { Args(bankAccountId: accountId) }
    268     var accountId: String
    269     struct Args: Encodable {
    270         var bankAccountId: String                        // bank account that should be forgotten
    271     }
    272 }
    273 extension WalletModel {
    274     /// add a known account. No networking
    275     nonisolated func forgetBankAccount(_ accountId: String, viewHandles: Bool = false)
    276       async throws {
    277         let request = ForgetBankAccount(accountId: accountId)
    278         _ = try await sendRequest(request, viewHandles: viewHandles)
    279     }
    280 }