1# frozen_string_literal: true
2
3require "bigdecimal"
4require "json"
5require "net/http"
6require "securerandom"
7
8class Electrum
9 class NoTransaction < StandardError; end
10
11 attr_reader :currency
12
13 def initialize(rpc_uri:, rpc_username:, rpc_password:, currency:)
14 @rpc_uri = URI(rpc_uri)
15 @rpc_username = rpc_username
16 @rpc_password = rpc_password
17 @currency = currency
18 end
19
20 def getaddresshistory(address)
21 rpc_call(:getaddresshistory, address: address)["result"]
22 end
23
24 def gettransaction(tx_hash)
25 Transaction.new(self, tx_hash, rpc_call(
26 :deserialize,
27 [rpc_call(:gettransaction, txid: tx_hash)["result"]]
28 )["result"])
29 end
30
31 def get_tx_status(tx_hash)
32 rpc_call(:get_tx_status, txid: tx_hash)["result"]
33 end
34
35 def listaddresses
36 rpc_call(:listaddresses, receiving: true)["result"]
37 end
38
39 def notify(address, url)
40 rpc_call(:notify, address: address, URL: url)["result"]
41 end
42
43 class Transaction
44 attr_reader :tx_hash
45
46 def initialize(electrum, tx_hash, tx)
47 raise NoTransaction, "No tx for #{@currency} #{tx_hash}" unless tx
48
49 @electrum = electrum
50 @tx_hash = tx_hash
51 @tx = tx
52 end
53
54 def confirmations
55 @electrum.get_tx_status(@tx_hash)["confirmations"]
56 end
57
58 def amount_for(*addresses)
59 BigDecimal(
60 @tx["outputs"]
61 .select { |o| addresses.include?(o["address"]) }
62 .map { |o| o["value_sats"] }
63 .sum
64 ) * 0.00000001
65 end
66 end
67
68protected
69
70 def rpc_call(method, params)
71 JSON.parse(post_json(
72 jsonrpc: "2.0",
73 id: SecureRandom.hex,
74 method: method.to_s,
75 params: params
76 ).body)
77 end
78
79 def post_json_req(data)
80 req = Net::HTTP::Post.new(
81 @rpc_uri,
82 "Content-Type" => "application/json"
83 )
84 req.basic_auth(@rpc_username, @rpc_password)
85 req.body = data.to_json
86 req
87 end
88
89 def post_json(data)
90 Net::HTTP.start(
91 @rpc_uri.hostname,
92 @rpc_uri.port,
93 use_ssl: @rpc_uri.scheme == "https"
94 ) do |http|
95 http.request(post_json_req(data))
96 end
97 end
98end