sibit 0.32.0 → 0.32.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sibit +13 -141
  3. data/lib/sibit/bin.rb +148 -0
  4. data/lib/sibit/version.rb +1 -1
  5. metadata +2 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ccc8435725361e992237efffbe4f53e4f6698e8217cbac39ce0f08f8b407cb68
4
- data.tar.gz: 8a787c829a4539a6b69be2764ef90b09e61b13797375b382e6a4df69826165cf
3
+ metadata.gz: 5b5729226952c9b63bbe126e8f4471de9a69b5fe5cdd1735500901a39d957c43
4
+ data.tar.gz: 4c9fac6187bbb379fd4874e81abb26e23073878a73f6877d92566d89d666b231
5
5
  SHA512:
6
- metadata.gz: 253013c5f6b978ea0db8ea3c2800aad241e2d277fbd743f21130dff952610b37fc27366ec8e67728dba64a97efbe1750c1dd7455faf991e856e8af5d06f52e88
7
- data.tar.gz: ce524cedb5ae7420113a0d9f6526792f9c47dee863724e64adf7909b94aca0ede25cee286f203063ff65166e00c58512b6310f6ced1ee52a28bd7516ab563e9c
6
+ metadata.gz: ad897361c466fef868dc4d526dbf5fc154d0d39039be448c34dceaef22e0622a08f8e2f2f6e98f9f8192a8e4f3955e92629c9b0bf6fabc71c5e748a4255a2c88
7
+ data.tar.gz: 3f9138f939d908f8c65f07fb10a70e9c02c900b8cc0a6edf827187ea59b97394a1b055e3baf4a539cb798e3662ec9bf4b680550159c3272c1758e04118be0443
data/bin/sibit CHANGED
@@ -7,11 +7,9 @@
7
7
  $stdout.sync = true
8
8
 
9
9
  require 'backtrace'
10
- require 'ellipsized'
11
10
  require 'loog'
12
- require 'retriable_proxy'
13
- require 'thor'
14
11
  require_relative '../lib/sibit'
12
+ require_relative '../lib/sibit/bin'
15
13
  require_relative '../lib/sibit/http'
16
14
  require_relative '../lib/sibit/httpproxy'
17
15
  require_relative '../lib/sibit/bitcoinchain'
@@ -24,142 +22,16 @@ require_relative '../lib/sibit/fake'
24
22
  require_relative '../lib/sibit/firstof'
25
23
  require_relative '../lib/sibit/version'
26
24
 
27
- VERBOSE = ARGV.include?('--verbose') || ENV.fetch('SIBIT_VERBOSE', nil)
28
- LOG = VERBOSE ? Loog::VERBOSE : Loog::REGULAR
29
-
30
- # Command-line interface for Sibit.
31
- # Provides commands to interact with the Bitcoin network.
32
- class Bin < Thor
33
- class_option :proxy, type: :string, desc: 'HTTPS proxy for all requests, e.g. "localhost:3128"'
34
- class_option :attempts, type: :numeric, default: 1,
35
- desc: 'How many times should we try before failing'
36
- class_option :dry, type: :boolean, default: false,
37
- desc: "Don't send a real payment, run in a read-only mode"
38
- class_option :verbose, type: :boolean, default: false, desc: 'Print all possible debug messages'
39
- class_option :api, type: :array, default: %w[blockchain btc bitcoinchain blockchair cex],
40
- desc: 'Ordered List of APIs to use, e.g. "blockchain,btc,bitcoinchain"'
41
-
42
- def self.exit_on_failure?
43
- true
44
- end
45
-
46
- def self.handle_argument_error(command, error, args, _arity)
47
- raise error unless args.include?('--help') || args.include?('-h')
48
- new.help(command.name)
49
- end
50
-
51
- desc 'price', 'Get current price of BTC in USD'
52
- def price
53
- log.info(client.price)
54
- end
55
-
56
- desc 'fees', 'Get currently recommended transaction fees'
57
- def fees
58
- sibit = client
59
- fees = sibit.fees
60
- text = %i[S M L XL].map do |m|
61
- sat = fees[m] * 250
62
- usd = sat * sibit.price / 100_000_000
63
- "#{m}: #{sat}sat / $#{format('%<usd>.02f', usd: usd)}"
64
- end.join("\n")
65
- log.info(text)
66
- end
67
-
68
- desc 'latest', 'Get hash of the latest block'
69
- def latest
70
- log.info(client.latest)
71
- end
72
-
73
- desc 'generate', 'Generate a new private key'
74
- def generate
75
- log.info(client.generate)
76
- end
77
-
78
- desc 'create KEY', 'Create a public Bitcoin address from the private key'
79
- def create(key)
80
- log.debug("Private key provided: #{key.ellipsized(8).inspect}")
81
- log.info(client.create(key))
82
- end
83
-
84
- desc 'balance ADDRESS', 'Check the balance of the Bitcoin address'
85
- def balance(address)
86
- log.info(client.balance(address))
87
- end
88
-
89
- desc 'pay AMOUNT FEE SOURCES TARGET CHANGE',
90
- 'Send a new Bitcoin transaction (AMOUNT can be "MAX" to use full balance)'
91
- option :skip_utxo, type: :array, default: [],
92
- desc: 'List of UTXO that must be skipped while paying'
93
- option :yes, type: :boolean, default: false,
94
- desc: 'Skip confirmation prompt and send the payment immediately'
95
- def pay(amount, fee, sources, target, change)
96
- keys = sources.split(',')
97
- if amount.upcase == 'MAX'
98
- addrs = keys.map { |k| Sibit::Key.new(k).bech32 }
99
- amount = addrs.sum { |a| client.balance(a) }
100
- end
101
- amount = amount.to_i if amount.is_a?(String) && /^[0-9]+$/.match?(amount)
102
- fee = fee.to_i if /^[0-9]+$/.match?(fee)
103
- args = [amount, fee, keys, target, change]
104
- kwargs = { skip_utxo: options[:skip_utxo] }
105
- unless options[:yes] || options[:dry]
106
- client(dry: true).pay(*args, **kwargs)
107
- print 'Do you confirm this payment? (yes/no): '
108
- answer = $stdin.gets&.strip&.downcase
109
- raise Sibit::Error, 'Payment cancelled by user' unless answer == 'yes'
110
- end
111
- log.info(client.pay(*args, **kwargs))
112
- end
113
-
114
- desc 'version', 'Print program version'
115
- def version
116
- log.info(Sibit::VERSION)
117
- end
118
-
119
- private
120
-
121
- def log
122
- LOG
123
- end
124
-
125
- def client(dry: false)
126
- proxy = options[:proxy] || ENV.fetch('SIBIT_PROXY', nil)
127
- http = proxy ? Sibit::HttpProxy.new(proxy) : Sibit::Http.new
128
- log.debug("Using proxy at #{http.host}") if proxy
129
- apis = options[:api].flat_map { |a| a.split(',') }.map(&:downcase).map do |a|
130
- case a
131
- when 'blockchain'
132
- Sibit::Blockchain.new(http: http, log: log)
133
- when 'btc'
134
- Sibit::Btc.new(http: http, log: log)
135
- when 'bitcoinchain'
136
- Sibit::Bitcoinchain.new(http: http, log: log)
137
- when 'blockchair'
138
- Sibit::Blockchair.new(http: http, log: log)
139
- when 'cex'
140
- Sibit::Cex.new(http: http, log: log)
141
- when 'fake'
142
- Sibit::Fake.new
143
- else
144
- raise Sibit::Error, "Unknown API \"#{a}\""
145
- end
146
- end
147
- api = Sibit::FirstOf.new(apis, log: log, verbose: true)
148
- api = Sibit::Dry.new(api, log: log) if options[:dry] || dry
149
- api = RetriableProxy.for_object(api, on: Sibit::Error) if options[:attempts] > 1
150
- Sibit.new(log: log, api: api)
151
- end
152
- end
153
-
154
- if __FILE__ == $PROGRAM_NAME
155
- begin
156
- Bin.start(ARGV)
157
- rescue StandardError => e
158
- if VERBOSE
159
- LOG.error(Backtrace.new(e))
160
- else
161
- LOG.error(e.message)
162
- end
163
- exit(255)
164
- end
25
+ begin
26
+ Sibit::Bin.start(ARGV)
27
+ rescue StandardError => e
28
+ verbose = !ARGV.include?('--quiet') &&
29
+ (ARGV.include?('--verbose') || ENV.fetch('SIBIT_VERBOSE', nil))
30
+ log = verbose ? Loog::VERBOSE : Loog::REGULAR
31
+ if verbose
32
+ log.error(Backtrace.new(e))
33
+ else
34
+ log.error(e.message)
35
+ end
36
+ exit(255)
165
37
  end
data/lib/sibit/bin.rb ADDED
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require 'ellipsized'
7
+ require 'retriable_proxy'
8
+ require 'thor'
9
+
10
+ # Sibit main class.
11
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
12
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
13
+ # License:: MIT
14
+ class Sibit
15
+ # Command-line interface for Sibit.
16
+ #
17
+ # Provides commands to interact with the Bitcoin network.
18
+ #
19
+ # Example:
20
+ # Sibit::Bin.start(['price'])
21
+ # Sibit::Bin.start(['balance', '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'])
22
+ class Bin < Thor
23
+ class_option :proxy, type: :string, desc: 'HTTPS proxy for all requests, e.g. "localhost:3128"'
24
+ class_option :attempts, type: :numeric, default: 1,
25
+ desc: 'How many times should we try before failing'
26
+ class_option :dry, type: :boolean, default: false,
27
+ desc: "Don't send a real payment, run in a read-only mode"
28
+ class_option :verbose, type: :boolean, default: false, desc: 'Print all possible debug messages'
29
+ class_option :quiet, type: :boolean, default: false, desc: 'Print only informative messages'
30
+ class_option :api, type: :array, default: %w[blockchain btc bitcoinchain blockchair cex],
31
+ desc: 'Ordered List of APIs to use, e.g. "blockchain,btc,bitcoinchain"'
32
+
33
+ def self.exit_on_failure?
34
+ true
35
+ end
36
+
37
+ def self.handle_argument_error(command, error, args, _arity)
38
+ raise error unless args.include?('--help') || args.include?('-h')
39
+ new.help(command.name)
40
+ end
41
+
42
+ desc 'price', 'Get current price of BTC in USD'
43
+ def price
44
+ log.info(client.price)
45
+ end
46
+
47
+ desc 'fees', 'Get currently recommended transaction fees'
48
+ def fees
49
+ sibit = client
50
+ fees = sibit.fees
51
+ text = %i[S M L XL].map do |m|
52
+ sat = fees[m] * 250
53
+ usd = sat * sibit.price / 100_000_000
54
+ "#{m}: #{sat}sat / $#{format('%<usd>.02f', usd: usd)}"
55
+ end.join("\n")
56
+ log.info(text)
57
+ end
58
+
59
+ desc 'latest', 'Get hash of the latest block'
60
+ def latest
61
+ log.info(client.latest)
62
+ end
63
+
64
+ desc 'generate', 'Generate a new private key'
65
+ def generate
66
+ log.info(client.generate)
67
+ end
68
+
69
+ desc 'create KEY', 'Create a public Bitcoin address from the private key'
70
+ def create(key)
71
+ log.debug("Private key provided: #{key.ellipsized(8).inspect}")
72
+ log.info(client.create(key))
73
+ end
74
+
75
+ desc 'balance ADDRESS', 'Check the balance of the Bitcoin address'
76
+ def balance(address)
77
+ log.info(client.balance(address))
78
+ end
79
+
80
+ desc 'pay AMOUNT FEE SOURCES TARGET CHANGE',
81
+ 'Send a new Bitcoin transaction (AMOUNT can be "MAX" to use full balance)'
82
+ option :skip_utxo, type: :array, default: [],
83
+ desc: 'List of UTXO that must be skipped while paying'
84
+ option :yes, type: :boolean, default: false,
85
+ desc: 'Skip confirmation prompt and send the payment immediately'
86
+ def pay(amount, fee, sources, target, change)
87
+ keys = sources.split(',')
88
+ if amount.upcase == 'MAX'
89
+ addrs = keys.map { |k| Sibit::Key.new(k).bech32 }
90
+ amount = addrs.sum { |a| client.balance(a) }
91
+ end
92
+ amount = amount.to_i if amount.is_a?(String) && /^[0-9]+$/.match?(amount)
93
+ fee = fee.to_i if /^[0-9]+$/.match?(fee)
94
+ args = [amount, fee, keys, target, change]
95
+ kwargs = { skip_utxo: options[:skip_utxo] }
96
+ unless options[:yes] || options[:dry]
97
+ client(dry: true).pay(*args, **kwargs)
98
+ print 'Do you confirm this payment? (yes/no): '
99
+ answer = $stdin.gets&.strip&.downcase
100
+ raise Sibit::Error, 'Payment cancelled by user' unless answer == 'yes'
101
+ end
102
+ log.info(client.pay(*args, **kwargs))
103
+ end
104
+
105
+ desc 'version', 'Print program version'
106
+ def version
107
+ log.info(Sibit::VERSION)
108
+ end
109
+
110
+ private
111
+
112
+ def log
113
+ @log ||= begin
114
+ verbose = !ARGV.include?('--quiet') &&
115
+ (ARGV.include?('--verbose') || ENV.fetch('SIBIT_VERBOSE', nil))
116
+ verbose ? Loog::VERBOSE : Loog::REGULAR
117
+ end
118
+ end
119
+
120
+ def client(dry: false)
121
+ proxy = options[:proxy] || ENV.fetch('SIBIT_PROXY', nil)
122
+ http = proxy ? Sibit::HttpProxy.new(proxy) : Sibit::Http.new
123
+ log.debug("Using proxy at #{http.host}") if proxy
124
+ apis = options[:api].flat_map { |a| a.split(',') }.map(&:downcase).map do |a|
125
+ case a
126
+ when 'blockchain'
127
+ Sibit::Blockchain.new(http: http, log: log)
128
+ when 'btc'
129
+ Sibit::Btc.new(http: http, log: log)
130
+ when 'bitcoinchain'
131
+ Sibit::Bitcoinchain.new(http: http, log: log)
132
+ when 'blockchair'
133
+ Sibit::Blockchair.new(http: http, log: log)
134
+ when 'cex'
135
+ Sibit::Cex.new(http: http, log: log)
136
+ when 'fake'
137
+ Sibit::Fake.new
138
+ else
139
+ raise Sibit::Error, "Unknown API \"#{a}\""
140
+ end
141
+ end
142
+ api = Sibit::FirstOf.new(apis, log: log, verbose: true)
143
+ api = Sibit::Dry.new(api, log: log) if options[:dry] || dry
144
+ api = RetriableProxy.for_object(api, on: Sibit::Error) if options[:attempts] > 1
145
+ Sibit.new(log: log, api: api)
146
+ end
147
+ end
148
+ end
data/lib/sibit/version.rb CHANGED
@@ -9,5 +9,5 @@
9
9
  # License:: MIT
10
10
  class Sibit
11
11
  # Current version of the library.
12
- VERSION = '0.32.0' unless defined?(VERSION)
12
+ VERSION = '0.32.2' unless defined?(VERSION)
13
13
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sibit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.32.0
4
+ version: 0.32.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
@@ -178,6 +178,7 @@ files:
178
178
  - lib/sibit/base58.rb
179
179
  - lib/sibit/bech32.rb
180
180
  - lib/sibit/bestof.rb
181
+ - lib/sibit/bin.rb
181
182
  - lib/sibit/bitcoinchain.rb
182
183
  - lib/sibit/blockchain.rb
183
184
  - lib/sibit/blockchair.rb