electrum.rb

 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