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