electrum.rb

 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