Response.swift (5694B)
1 // 2 // Response.swift 3 // damus 4 // 5 // Created by Daniel D’Aquino on 2025-03-10. 6 // 7 8 import Combine 9 10 extension WalletConnect { 11 /// Models a response from the NWC provider 12 struct Response: Decodable { 13 let result_type: Response.Result.ResultType 14 let error: WalletResponseErr? 15 let result: Response.Result? 16 17 private enum CodingKeys: CodingKey { 18 case result_type, error, result 19 } 20 21 init(from decoder: Decoder) throws { 22 let container = try decoder.container(keyedBy: CodingKeys.self) 23 let result_type_str = try container.decode(String.self, forKey: .result_type) 24 25 guard let result_type = Response.Result.ResultType(rawValue: result_type_str) else { 26 throw DecodingError.typeMismatch(Response.Result.ResultType.self, .init(codingPath: decoder.codingPath, debugDescription: "result_type \(result_type_str) is unknown")) 27 } 28 29 self.result_type = result_type 30 self.error = try container.decodeIfPresent(WalletResponseErr.self, forKey: .error) 31 32 guard self.error == nil else { 33 self.result = nil 34 return 35 } 36 37 switch result_type { 38 case .pay_invoice: 39 let res = try container.decode(Result.PayInvoiceResponse.self, forKey: .result) 40 self.result = .pay_invoice(res) 41 case .get_balance: 42 let res = try container.decode(Result.GetBalanceResponse.self, forKey: .result) 43 self.result = .get_balance(res) 44 case .list_transactions: 45 let res = try container.decode(Result.ListTransactionsResponse.self, forKey: .result) 46 self.result = .list_transactions(res) 47 } 48 } 49 } 50 51 struct FullWalletResponse { 52 let req_id: NoteId 53 let response: Response 54 55 init(from event: NostrEvent, nwc: WalletConnect.ConnectURL) throws(InitializationError) { 56 guard event.pubkey == nwc.pubkey else { throw .incorrectAuthorPubkey } 57 58 guard let referencedNoteId = event.referenced_ids.first else { throw .missingRequestIdReference } 59 60 self.req_id = referencedNoteId 61 62 var json = "" 63 do { 64 json = try NIP04.decryptContent( 65 recipientPrivateKey: nwc.keypair.privkey, 66 senderPubkey: nwc.pubkey, 67 content: event.content, 68 encoding: .base64 69 ) 70 } 71 catch { throw .failedToDecrypt(error) } 72 73 do { 74 let response: WalletConnect.Response = try decode_json_throwing(json) 75 self.response = response 76 } 77 catch { throw .failedToDecodeJSON(error) } 78 } 79 80 enum InitializationError: Error { 81 case incorrectAuthorPubkey 82 case missingRequestIdReference 83 case failedToDecodeJSON(any Error) 84 case failedToDecrypt(any Error) 85 } 86 } 87 88 struct WalletResponseErr: Codable, Error { 89 let code: Code? 90 let message: String? 91 92 enum Code: String, Codable { 93 /// The client is sending commands too fast. It should retry in a few seconds. 94 case rateLimited = "RATE_LIMITED" 95 /// The command is not known or is intentionally not implemented. 96 case notImplemented = "NOT_IMPLEMENTED" 97 /// The wallet does not have enough funds to cover a fee reserve or the payment amount. 98 case insufficientBalance = "INSUFFICIENT_BALANCE" 99 /// The wallet has exceeded its spending quota. 100 case quotaExceeded = "QUOTA_EXCEEDED" 101 /// This public key is not allowed to do this operation. 102 case restricted = "RESTRICTED" 103 /// This public key has no wallet connected. 104 case unauthorized = "UNAUTHORIZED" 105 /// An internal error. 106 case internalError = "INTERNAL" 107 /// Other error. 108 case other = "OTHER" 109 } 110 111 enum CodingKeys: String, CodingKey { 112 case code, message 113 } 114 115 init(from decoder: Decoder) throws { 116 let container = try decoder.container(keyedBy: CodingKeys.self) 117 118 // Attempt to decode the code as a String 119 if let codeString = try container.decodeIfPresent(String.self, forKey: .code), 120 let validCode = Code(rawValue: codeString) { 121 self.code = validCode 122 } else { 123 // If the code is either missing or not one of the allowed cases, set it to nil 124 self.code = nil 125 } 126 127 self.message = try container.decodeIfPresent(String.self, forKey: .message) 128 } 129 } 130 } 131 132 extension WalletConnect.Response { 133 /// The response data resulting from an NWC request 134 enum Result { 135 case pay_invoice(PayInvoiceResponse) 136 case get_balance(GetBalanceResponse) 137 case list_transactions(ListTransactionsResponse) 138 139 enum ResultType: String { 140 case pay_invoice 141 case get_balance 142 case list_transactions 143 } 144 145 struct PayInvoiceResponse: Decodable { 146 let preimage: String 147 } 148 149 struct GetBalanceResponse: Decodable { 150 let balance: Int64 151 } 152 153 struct ListTransactionsResponse: Decodable { 154 let transactions: [WalletConnect.Transaction] 155 } 156 } 157 }