WalletConnect+.swift (4241B)
1 // 2 // WalletConnect+.swift 3 // damus 4 // 5 // Created by Daniel D’Aquino on 2023-11-27. 6 // 7 8 import Foundation 9 10 func make_wallet_pay_invoice_request(invoice: String) -> WalletRequest<PayInvoiceRequest> { 11 let data = PayInvoiceRequest(invoice: invoice) 12 return WalletRequest(method: "pay_invoice", params: data) 13 } 14 15 func make_wallet_balance_request() -> WalletRequest<EmptyRequest> { 16 return WalletRequest(method: "get_balance", params: nil) 17 } 18 19 struct EmptyRequest: Codable { 20 } 21 22 struct PayInvoiceRequest: Codable { 23 let invoice: String 24 } 25 26 func make_wallet_connect_request<T>(req: WalletRequest<T>, to_pk: Pubkey, keypair: FullKeypair) -> NostrEvent? { 27 let tags = [to_pk.tag] 28 let created_at = UInt32(Date().timeIntervalSince1970) 29 guard let content = encode_json(req) else { 30 return nil 31 } 32 return create_encrypted_event(content, to_pk: to_pk, tags: tags, keypair: keypair, created_at: created_at, kind: 23194) 33 } 34 35 func subscribe_to_nwc(url: WalletConnectURL, pool: RelayPool) { 36 var filter = NostrFilter(kinds: [.nwc_response]) 37 filter.authors = [url.pubkey] 38 filter.limit = 0 39 let sub = NostrSubscribe(filters: [filter], sub_id: "nwc") 40 41 pool.send(.subscribe(sub), to: [url.relay], skip_ephemeral: false) 42 } 43 44 @discardableResult 45 func nwc_pay(url: WalletConnectURL, pool: RelayPool, post: PostBox, invoice: String, delay: TimeInterval? = 5.0, on_flush: OnFlush? = nil) -> NostrEvent? { 46 let req = make_wallet_pay_invoice_request(invoice: invoice) 47 guard let ev = make_wallet_connect_request(req: req, to_pk: url.pubkey, keypair: url.keypair) else { 48 return nil 49 } 50 51 try? pool.add_relay(.nwc(url: url.relay)) 52 subscribe_to_nwc(url: url, pool: pool) 53 post.send(ev, to: [url.relay], skip_ephemeral: false, delay: delay, on_flush: on_flush) 54 return ev 55 } 56 57 58 func nwc_success(state: DamusState, resp: FullWalletResponse) { 59 // find the pending zap and mark it as pending-confirmed 60 for kv in state.zaps.our_zaps { 61 let zaps = kv.value 62 63 for zap in zaps { 64 guard case .pending(let pzap) = zap, 65 case .nwc(let nwc_state) = pzap.state, 66 case .postbox_pending(let nwc_req) = nwc_state.state, 67 nwc_req.id == resp.req_id 68 else { 69 continue 70 } 71 72 if nwc_state.update_state(state: .confirmed) { 73 // notify the zaps model of an update so it can mark them as paid 74 state.events.get_cache_data(NoteId(pzap.target.id)).zaps_model.objectWillChange.send() 75 print("NWC success confirmed") 76 } 77 78 return 79 } 80 } 81 } 82 83 func send_donation_zap(pool: RelayPool, postbox: PostBox, nwc: WalletConnectURL, percent: Int, base_msats: Int64) async { 84 let percent_f = Double(percent) / 100.0 85 let donations_msats = Int64(percent_f * Double(base_msats)) 86 87 let payreq = LNUrlPayRequest(allowsNostr: true, commentAllowed: nil, nostrPubkey: "", callback: "https://sendsats.lol/@damus") 88 guard let invoice = await fetch_zap_invoice(payreq, zapreq: nil, msats: donations_msats, zap_type: .non_zap, comment: nil) else { 89 // we failed... oh well. no donation for us. 90 print("damus-donation failed to fetch invoice") 91 return 92 } 93 94 print("damus-donation donating...") 95 nwc_pay(url: nwc, pool: pool, post: postbox, invoice: invoice, delay: nil) 96 } 97 98 func nwc_error(zapcache: Zaps, evcache: EventCache, resp: FullWalletResponse) { 99 // find a pending zap with the nwc request id associated with this response and remove it 100 for kv in zapcache.our_zaps { 101 let zaps = kv.value 102 103 for zap in zaps { 104 guard case .pending(let pzap) = zap, 105 case .nwc(let nwc_state) = pzap.state, 106 case .postbox_pending(let req) = nwc_state.state, 107 req.id == resp.req_id 108 else { 109 continue 110 } 111 112 // remove the pending zap if there was an error 113 let reqid = ZapRequestId(from_pending: pzap) 114 remove_zap(reqid: reqid, zapcache: zapcache, evcache: evcache) 115 return 116 } 117 } 118 }