1# frozen_string_literal: true
2
3require "bigdecimal"
4require "em_promise"
5require "json"
6require "net/http"
7require "securerandom"
8
9class Electrum
10 def initialize(rpc_uri:, rpc_username:, rpc_password:)
11 @rpc_uri = URI(rpc_uri)
12 @rpc_username = rpc_username
13 @rpc_password = rpc_password
14 end
15
16 def createnewaddress
17 rpc_call(:createnewaddress, {}).then { |r| r["result"] }
18 end
19
20 def getaddresshistory(address)
21 rpc_call(:getaddresshistory, address: address).then { |r| r["result"] }
22 end
23
24 def gettransaction(tx_hash)
25 rpc_call(:gettransaction, txid: tx_hash).then { |tx|
26 rpc_call(:deserialize, [tx["result"]])
27 }.then do |tx|
28 Transaction.new(self, tx_hash, tx["result"])
29 end
30 end
31
32 def get_tx_status(tx_hash)
33 rpc_call(:get_tx_status, txid: tx_hash).then { |r| r["result"] }
34 end
35
36 class Transaction
37 def initialize(electrum, tx_hash, tx)
38 @electrum = electrum
39 @tx_hash = tx_hash
40 @tx = tx
41 end
42
43 def confirmations
44 @electrum.get_tx_status(@tx_hash).then { |r| r["confirmations"] }
45 end
46
47 def amount_for(*addresses)
48 BigDecimal.new(
49 @tx["outputs"]
50 .select { |o| addresses.include?(o["address"]) }
51 .map { |o| o["value_sats"] }
52 .sum
53 ) * 0.00000001
54 end
55 end
56
57protected
58
59 def rpc_call(method, params)
60 post_json(
61 jsonrpc: "2.0",
62 id: SecureRandom.hex,
63 method: method.to_s,
64 params: params
65 ).then { |res| JSON.parse(res.response) }
66 end
67
68 def post_json(data)
69 EM::HttpRequest.new(
70 @rpc_uri,
71 tls: { verify_peer: true }
72 ).post(
73 head: {
74 "Authorization" => [@rpc_username, @rpc_password],
75 "Content-Type" => "application/json"
76 },
77 body: data.to_json
78 )
79 end
80end