Class: NwcRuby::Client
- Inherits:
-
Object
- Object
- NwcRuby::Client
- Defined in:
- lib/nwc_ruby/client.rb
Overview
The main public API.
client = NwcRuby::Client.from_uri(ENV["NWC_URL"])
# One-shot request/response (transparently opens a WS, sends, waits, closes):
info = client.get_info
balance = client.get_balance
invoice = client.make_invoice(amount: 1_000, description: "tip")
# Long-running listener (transparently heartbeats, reconnects, resumes):
client.subscribe_to_notifications do |notification|
puts "Got #{notification.amount_msats} msats"
end
Constant Summary collapse
- DEFAULT_TIMEOUT =
30
Instance Attribute Summary collapse
-
#connection_string ⇒ Object
readonly
Returns the value of attribute connection_string.
-
#logger ⇒ Object
readonly
Returns the value of attribute logger.
Class Method Summary collapse
Instance Method Summary collapse
- #capabilities ⇒ Object
- #get_balance ⇒ Object
- #get_info ⇒ Object
-
#info(refresh: false) ⇒ Object
Fetch and cache the kind 13194 info event.
-
#initialize(connection_string, logger: nil, request_timeout: DEFAULT_TIMEOUT) ⇒ Client
constructor
A new instance of Client.
- #list_transactions(from: nil, until_ts: nil, limit: nil, offset: nil, unpaid: nil, type: nil) ⇒ Object
- #lookup_invoice(payment_hash: nil, invoice: nil) ⇒ Object
- #make_invoice(amount:, description: nil, description_hash: nil, expiry: nil, metadata: nil) ⇒ Object
- #multi_pay_invoice(invoices:) ⇒ Object
- #multi_pay_keysend(keysends:) ⇒ Object
-
#pay_invoice(invoice:, amount: nil) ⇒ Object
– NIP-47 methods ——————————————————-.
- #pay_keysend(amount:, pubkey:, preimage: nil, tlv_records: nil) ⇒ Object
- #read_only? ⇒ Boolean
- #read_write? ⇒ Boolean
- #sign_message(message:) ⇒ Object
-
#stop_notifications! ⇒ Object
Stop a running subscribe_to_notifications listener.
-
#subscribe_to_notifications(since: Time.now.to_i, kinds: [NIP47::Methods::KIND_NOTIFICATION_NIP04, NIP47::Methods::KIND_NOTIFICATION_NIP44], sub_id: "nwc-#{SecureRandom.hex(4)}", poll_interval: nil, &block) ⇒ Object
Subscribe to payment_received / payment_sent notifications from the wallet service.
Constructor Details
#initialize(connection_string, logger: nil, request_timeout: DEFAULT_TIMEOUT) ⇒ Client
Returns a new instance of Client.
30 31 32 33 34 35 |
# File 'lib/nwc_ruby/client.rb', line 30 def initialize(connection_string, logger: nil, request_timeout: DEFAULT_TIMEOUT) @connection_string = connection_string @logger = logger || default_logger @request_timeout = request_timeout @info = nil end |
Instance Attribute Details
#connection_string ⇒ Object (readonly)
Returns the value of attribute connection_string.
24 25 26 |
# File 'lib/nwc_ruby/client.rb', line 24 def connection_string @connection_string end |
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
24 25 26 |
# File 'lib/nwc_ruby/client.rb', line 24 def logger @logger end |
Class Method Details
.from_uri(uri_string) ⇒ Object
26 27 28 |
# File 'lib/nwc_ruby/client.rb', line 26 def self.from_uri(uri_string, **) new(ConnectionString.parse(uri_string), **) end |
Instance Method Details
#capabilities ⇒ Object
47 48 49 |
# File 'lib/nwc_ruby/client.rb', line 47 def capabilities info.methods end |
#get_balance ⇒ Object
111 112 113 |
# File 'lib/nwc_ruby/client.rb', line 111 def get_balance call(NIP47::Methods::GET_BALANCE, {}) end |
#get_info ⇒ Object
115 116 117 |
# File 'lib/nwc_ruby/client.rb', line 115 def get_info call(NIP47::Methods::GET_INFO, {}) end |
#info(refresh: false) ⇒ Object
Fetch and cache the kind 13194 info event. This tells us which methods the wallet service supports and which encryption schemes it accepts.
41 42 43 44 45 |
# File 'lib/nwc_ruby/client.rb', line 41 def info(refresh: false) return @info if @info && !refresh @info = fetch_info end |
#list_transactions(from: nil, until_ts: nil, limit: nil, offset: nil, unpaid: nil, type: nil) ⇒ Object
100 101 102 103 104 105 106 107 108 109 |
# File 'lib/nwc_ruby/client.rb', line 100 def list_transactions(from: nil, until_ts: nil, limit: nil, offset: nil, unpaid: nil, type: nil) params = {} params['from'] = from if from params['until'] = until_ts if until_ts params['limit'] = limit if limit params['offset'] = offset if offset params['unpaid'] = unpaid unless unpaid.nil? params['type'] = type if type call(NIP47::Methods::LIST_TRANSACTIONS, params) end |
#lookup_invoice(payment_hash: nil, invoice: nil) ⇒ Object
91 92 93 94 95 96 97 98 |
# File 'lib/nwc_ruby/client.rb', line 91 def lookup_invoice(payment_hash: nil, invoice: nil) raise ArgumentError, 'lookup_invoice requires payment_hash or invoice' if payment_hash.nil? && invoice.nil? params = {} params['payment_hash'] = payment_hash if payment_hash params['invoice'] = invoice if invoice call(NIP47::Methods::LOOKUP_INVOICE, params) end |
#make_invoice(amount:, description: nil, description_hash: nil, expiry: nil, metadata: nil) ⇒ Object
82 83 84 85 86 87 88 89 |
# File 'lib/nwc_ruby/client.rb', line 82 def make_invoice(amount:, description: nil, description_hash: nil, expiry: nil, metadata: nil) params = { 'amount' => amount } params['description'] = description if description params['description_hash'] = description_hash if description_hash params['expiry'] = expiry if expiry params['metadata'] = if call(NIP47::Methods::MAKE_INVOICE, params) end |
#multi_pay_invoice(invoices:) ⇒ Object
67 68 69 |
# File 'lib/nwc_ruby/client.rb', line 67 def multi_pay_invoice(invoices:) call(NIP47::Methods::MULTI_PAY_INVOICE, { 'invoices' => invoices }) end |
#multi_pay_keysend(keysends:) ⇒ Object
78 79 80 |
# File 'lib/nwc_ruby/client.rb', line 78 def multi_pay_keysend(keysends:) call(NIP47::Methods::MULTI_PAY_KEYSEND, { 'keysends' => keysends }) end |
#pay_invoice(invoice:, amount: nil) ⇒ Object
– NIP-47 methods ——————————————————-
61 62 63 64 65 |
# File 'lib/nwc_ruby/client.rb', line 61 def pay_invoice(invoice:, amount: nil) params = { 'invoice' => invoice } params['amount'] = amount if amount call(NIP47::Methods::PAY_INVOICE, params) end |
#pay_keysend(amount:, pubkey:, preimage: nil, tlv_records: nil) ⇒ Object
71 72 73 74 75 76 |
# File 'lib/nwc_ruby/client.rb', line 71 def pay_keysend(amount:, pubkey:, preimage: nil, tlv_records: nil) params = { 'amount' => amount, 'pubkey' => pubkey } params['preimage'] = preimage if preimage params['tlv_records'] = tlv_records if tlv_records call(NIP47::Methods::PAY_KEYSEND, params) end |
#read_only? ⇒ Boolean
51 52 53 |
# File 'lib/nwc_ruby/client.rb', line 51 def read_only? info.read_only? end |
#read_write? ⇒ Boolean
55 56 57 |
# File 'lib/nwc_ruby/client.rb', line 55 def read_write? info.read_write? end |
#sign_message(message:) ⇒ Object
119 120 121 |
# File 'lib/nwc_ruby/client.rb', line 119 def (message:) call(NIP47::Methods::SIGN_MESSAGE, { 'message' => }) end |
#stop_notifications! ⇒ Object
Stop a running subscribe_to_notifications listener.
124 125 126 |
# File 'lib/nwc_ruby/client.rb', line 124 def stop_notifications! @notification_connection&.stop! end |
#subscribe_to_notifications(since: Time.now.to_i, kinds: [NIP47::Methods::KIND_NOTIFICATION_NIP04, NIP47::Methods::KIND_NOTIFICATION_NIP44], sub_id: "nwc-#{SecureRandom.hex(4)}", poll_interval: nil, &block) ⇒ Object
Subscribe to payment_received / payment_sent notifications from the wallet service. Blocks forever, handling heartbeat and reconnect.
client.subscribe_to_notifications do |notification|
case notification.type
when "payment_received" then credit_invoice(notification.payment_hash, notification.amount_msats)
when "payment_sent" then mark_outbound_settled(notification.payment_hash)
end
end
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/nwc_ruby/client.rb', line 146 def subscribe_to_notifications(since: Time.now.to_i, # rubocop:disable Metrics/MethodLength kinds: [NIP47::Methods::KIND_NOTIFICATION_NIP04, NIP47::Methods::KIND_NOTIFICATION_NIP44], sub_id: "nwc-#{SecureRandom.hex(4)}", poll_interval: nil, &block) raise ArgumentError, 'block required' unless block seen = {} last_seen_at = since filters = [{ 'authors' => [@connection_string.wallet_pubkey], '#p' => [@connection_string.client_pubkey], 'kinds' => kinds, 'since' => since }] conn = Transport::RelayConnection.new( url: @connection_string.relays.first, logger: @logger, poll_interval: poll_interval ) @notification_connection = conn conn.on_open do |c| @logger.info( "[nwc] subscribing sub_id=#{sub_id} client_pubkey=#{@connection_string.client_pubkey} " \ "wallet_pubkey=#{@connection_string.wallet_pubkey} kinds=#{kinds.inspect} since=#{since}" ) c.send_req(sub_id: sub_id, filters: filters) end # Periodic re-subscribe: some relays (e.g. strfry) store ephemeral # events temporarily but never push them to existing subscriptions. # Re-sending the REQ with the same sub_id forces the relay to return # any newly-stored events. conn.on_poll do |c| filters[0]['since'] = last_seen_at @logger.debug("[nwc] re-subscribing sub_id=#{sub_id} since=#{last_seen_at}") c.send_req(sub_id: sub_id, filters: filters) end conn.on_event do |_sub, event_hash| event = Event.from_hash(event_hash) next unless event.valid_signature? next unless event.pubkey == @connection_string.wallet_pubkey # Advance the poll watermark so we don't re-fetch old events. last_seen_at = event.created_at if event.created_at && event.created_at > last_seen_at begin notification = NIP47::Notification.parse(event, @connection_string.secret, @connection_string.wallet_pubkey) rescue EncryptionError => e @logger.warn("[nwc] could not decrypt notification: #{e.}") next end # Dedupe: wallets that support both encryption schemes publish both # 23196 and 23197 for the same event. key = notification.payment_hash || event.id next if seen[key] seen[key] = true # Primitive GC to keep the hash bounded. seen.shift while seen.size > 10_000 block.call(notification) end conn.run! end |