straight 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +5 -1
- data/README.md +20 -10
- data/VERSION +1 -1
- data/lib/straight.rb +10 -1
- data/lib/straight/blockchain_adapter.rb +2 -13
- data/lib/straight/blockchain_adapters/biteasy_adapter.rb +74 -0
- data/lib/straight/blockchain_adapters/blockchain_info_adapter.rb +37 -17
- data/lib/straight/blockchain_adapters/mycelium_adapter.rb +144 -0
- data/lib/straight/exchange_rate_adapter.rb +20 -1
- data/lib/straight/exchange_rate_adapters/average_rate_adapter.rb +54 -0
- data/lib/straight/exchange_rate_adapters/bitpay_adapter.rb +5 -2
- data/lib/straight/exchange_rate_adapters/bitstamp_adapter.rb +2 -1
- data/lib/straight/exchange_rate_adapters/btce_adapter.rb +18 -0
- data/lib/straight/exchange_rate_adapters/coinbase_adapter.rb +2 -4
- data/lib/straight/exchange_rate_adapters/kraken_adapter.rb +18 -0
- data/lib/straight/exchange_rate_adapters/localbitcoins_adapter.rb +17 -0
- data/lib/straight/exchange_rate_adapters/okcoin_adapter.rb +18 -0
- data/lib/straight/gateway.rb +20 -9
- data/lib/straight/order.rb +46 -13
- data/spec/lib/blockchain_adapters/{helloblock_io_spec.rb → biteasy_adapter_spec.rb} +23 -18
- data/spec/lib/blockchain_adapters/{blockchain_info_spec.rb → blockchain_info_adapter_spec.rb} +8 -3
- data/spec/lib/blockchain_adapters/mycelium_adapter_spec.rb +54 -0
- data/spec/lib/exchange_rate_adapter_spec.rb +6 -1
- data/spec/lib/exchange_rate_adapters/average_rate_adapter_spec.rb +43 -0
- data/spec/lib/exchange_rate_adapters/bitpay_adapter_spec.rb +14 -1
- data/spec/lib/exchange_rate_adapters/bitstamp_adapter_spec.rb +14 -1
- data/spec/lib/exchange_rate_adapters/btce_adapter_spec.rb +27 -0
- data/spec/lib/exchange_rate_adapters/coinbase_adapter_spec.rb +14 -1
- data/spec/lib/exchange_rate_adapters/kraken_adapter_spec.rb +27 -0
- data/spec/lib/exchange_rate_adapters/localbitcoins_adapter_spec.rb +27 -0
- data/spec/lib/exchange_rate_adapters/okcoin_adapter_spec.rb +27 -0
- data/spec/lib/gateway_spec.rb +23 -5
- data/spec/lib/order_spec.rb +18 -2
- data/straight.gemspec +95 -0
- metadata +33 -6
- data/lib/straight/blockchain_adapters/helloblock_io_adapter.rb +0 -53
@@ -3,7 +3,8 @@ module Straight
|
|
3
3
|
|
4
4
|
class Adapter
|
5
5
|
|
6
|
-
|
6
|
+
include Singleton
|
7
|
+
|
7
8
|
class FetchingFailed < Exception; end
|
8
9
|
class CurrencyNotSupported < Exception; end
|
9
10
|
|
@@ -39,6 +40,24 @@ module Straight
|
|
39
40
|
nil # this should be changed in descendant classes
|
40
41
|
end
|
41
42
|
|
43
|
+
# This method will get value we are interested in from hash and
|
44
|
+
# prevent failing with 'undefined method [] for Nil' if at some point hash doesn't have such key value pair
|
45
|
+
def get_rate_value_from_hash(rates_hash, *keys)
|
46
|
+
keys.inject(rates_hash) do |intermediate, key|
|
47
|
+
if intermediate.respond_to?(:[])
|
48
|
+
intermediate[key]
|
49
|
+
else
|
50
|
+
raise CurrencyNotSupported
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# We dont want to have false positive rate, because nil.to_f is 0.0
|
56
|
+
# This method checks that rate value is not nil
|
57
|
+
def rate_to_f(rate)
|
58
|
+
rate ? rate.to_f : raise(CurrencyNotSupported)
|
59
|
+
end
|
60
|
+
|
42
61
|
end
|
43
62
|
|
44
63
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Straight
|
2
|
+
module ExchangeRate
|
3
|
+
|
4
|
+
class AverageRateAdapter < Adapter
|
5
|
+
|
6
|
+
# Takes exchange rate adapters instances or classes as arguments
|
7
|
+
def self.instance(*adapters)
|
8
|
+
instance = super()
|
9
|
+
instance.instance_variable_set(:@adapters, adapters.map { |adapter| adapter.respond_to?(:instance) ? adapter.instance : adapter })
|
10
|
+
instance
|
11
|
+
end
|
12
|
+
|
13
|
+
def fetch_rates!
|
14
|
+
failed_fetches = 0
|
15
|
+
@adapters.each do |adapter|
|
16
|
+
begin
|
17
|
+
adapter.fetch_rates!
|
18
|
+
rescue Exception => e
|
19
|
+
failed_fetches += 1
|
20
|
+
raise e if failed_fetches == @adapters.size
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def rate_for(currency_code)
|
26
|
+
rates = []
|
27
|
+
@adapters.each do |adapter|
|
28
|
+
begin
|
29
|
+
rates << adapter.rate_for(currency_code)
|
30
|
+
rescue CurrencyNotSupported
|
31
|
+
rates << nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
unless rates.select(&:nil?).size == @adapters.size
|
36
|
+
rates.compact!
|
37
|
+
rates.inject {|sum, rate| sum + rate} / rates.size
|
38
|
+
else
|
39
|
+
raise CurrencyNotSupported
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_rate_value_from_hash(rates_hash, *keys)
|
44
|
+
raise "This method is not supposed to be used in #{self.class}."
|
45
|
+
end
|
46
|
+
|
47
|
+
def rate_to_f(rate)
|
48
|
+
raise "This method is not supposed to be used in #{self.class}."
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -7,8 +7,11 @@ module Straight
|
|
7
7
|
|
8
8
|
def rate_for(currency_code)
|
9
9
|
super
|
10
|
-
@rates.each do |
|
11
|
-
|
10
|
+
@rates.each do |rt|
|
11
|
+
if rt['code'] == currency_code
|
12
|
+
rate = get_rate_value_from_hash(rt, 'rate')
|
13
|
+
return rate_to_f(rate)
|
14
|
+
end
|
12
15
|
end
|
13
16
|
raise CurrencyNotSupported
|
14
17
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Straight
|
2
|
+
module ExchangeRate
|
3
|
+
|
4
|
+
class BtceAdapter < Adapter
|
5
|
+
|
6
|
+
FETCH_URL = 'https://btc-e.com/api/2/btc_usd/ticker'
|
7
|
+
|
8
|
+
def rate_for(currency_code)
|
9
|
+
super
|
10
|
+
raise CurrencyNotSupported if !FETCH_URL.include?("btc_#{currency_code.downcase}")
|
11
|
+
rate = get_rate_value_from_hash(@rates, 'ticker', 'last')
|
12
|
+
rate_to_f(rate)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -7,10 +7,8 @@ module Straight
|
|
7
7
|
|
8
8
|
def rate_for(currency_code)
|
9
9
|
super
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
raise CurrencyNotSupported
|
10
|
+
rate = get_rate_value_from_hash(@rates, "btc_to_#{currency_code.downcase}")
|
11
|
+
rate_to_f(rate)
|
14
12
|
end
|
15
13
|
|
16
14
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Straight
|
2
|
+
module ExchangeRate
|
3
|
+
|
4
|
+
class KrakenAdapter < Adapter
|
5
|
+
|
6
|
+
FETCH_URL = 'https://api.kraken.com/0/public/Ticker?pair=xbtusd'
|
7
|
+
|
8
|
+
def rate_for(currency_code)
|
9
|
+
super
|
10
|
+
rate = get_rate_value_from_hash(@rates, 'result', 'XXBTZ' + currency_code.upcase, 'c')
|
11
|
+
rate = rate.kind_of?(Array) ? rate.first : raise(CurrencyNotSupported)
|
12
|
+
rate_to_f(rate)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Straight
|
2
|
+
module ExchangeRate
|
3
|
+
|
4
|
+
class LocalbitcoinsAdapter < Adapter
|
5
|
+
|
6
|
+
FETCH_URL = 'https://localbitcoins.com/bitcoinaverage/ticker-all-currencies/'
|
7
|
+
|
8
|
+
def rate_for(currency_code)
|
9
|
+
super
|
10
|
+
rate = get_rate_value_from_hash(@rates, currency_code.upcase, 'rates', 'last')
|
11
|
+
rate_to_f(rate)
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Straight
|
2
|
+
module ExchangeRate
|
3
|
+
|
4
|
+
class OkcoinAdapter < Adapter
|
5
|
+
|
6
|
+
FETCH_URL = 'https://www.okcoin.com/api/ticker.do?ok=1'
|
7
|
+
|
8
|
+
def rate_for(currency_code)
|
9
|
+
super
|
10
|
+
raise CurrencyNotSupported if currency_code != 'USD'
|
11
|
+
rate = get_rate_value_from_hash(@rates, 'ticker', 'last')
|
12
|
+
rate_to_f(rate)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
data/lib/straight/gateway.rb
CHANGED
@@ -43,9 +43,8 @@ module Straight
|
|
43
43
|
|
44
44
|
# Determines the algorithm for consequitive checks of the order status.
|
45
45
|
DEFAULT_STATUS_CHECK_SCHEDULE = -> (period, iteration_index) do
|
46
|
-
return false if period > 640
|
47
46
|
iteration_index += 1
|
48
|
-
if iteration_index
|
47
|
+
if iteration_index >= 20
|
49
48
|
period *= 2
|
50
49
|
iteration_index = 0
|
51
50
|
end
|
@@ -56,7 +55,7 @@ module Straight
|
|
56
55
|
# call super() somehwere inside those methods.
|
57
56
|
#
|
58
57
|
# In short, the idea is to let the class we're being prepended to do its magic
|
59
|
-
# after
|
58
|
+
# after our methods are finished.
|
60
59
|
module Prependable
|
61
60
|
end
|
62
61
|
|
@@ -85,7 +84,8 @@ module Straight
|
|
85
84
|
# the one a user of this class is going to properly increment) that is used to generate a
|
86
85
|
# an BIP32 bitcoin address deterministically.
|
87
86
|
def address_for_keychain_id(id)
|
88
|
-
|
87
|
+
# The 'm/0/n' notation is used by both Electrum and Mycelium
|
88
|
+
keychain.node_for_path("m/0/#{id.to_s}").to_address
|
89
89
|
end
|
90
90
|
|
91
91
|
def fetch_transaction(tid, address: nil)
|
@@ -101,7 +101,7 @@ module Straight
|
|
101
101
|
end
|
102
102
|
|
103
103
|
def keychain
|
104
|
-
@keychain ||= MoneyTree::Node.from_serialized_address(
|
104
|
+
@keychain ||= MoneyTree::Node.from_serialized_address(pubkey)
|
105
105
|
end
|
106
106
|
|
107
107
|
# This is a callback method called from each order
|
@@ -130,6 +130,13 @@ module Straight
|
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
|
+
def current_exchange_rate(currency=self.default_currency)
|
134
|
+
currency = currency.to_s.upcase
|
135
|
+
try_adapters(@exchange_rate_adapters) do |a|
|
136
|
+
a.rate_for(currency)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
133
140
|
private
|
134
141
|
|
135
142
|
# Calls the block with each adapter until one of them does not fail.
|
@@ -163,12 +170,16 @@ module Straight
|
|
163
170
|
@default_currency = 'BTC'
|
164
171
|
@blockchain_adapters = [
|
165
172
|
Blockchain::BlockchainInfoAdapter.mainnet_adapter,
|
166
|
-
Blockchain::
|
173
|
+
Blockchain::MyceliumAdapter.mainnet_adapter
|
167
174
|
]
|
168
175
|
@exchange_rate_adapters = [
|
169
|
-
ExchangeRate::BitpayAdapter.
|
170
|
-
ExchangeRate::CoinbaseAdapter.
|
171
|
-
ExchangeRate::BitstampAdapter.
|
176
|
+
ExchangeRate::BitpayAdapter.instance,
|
177
|
+
ExchangeRate::CoinbaseAdapter.instance,
|
178
|
+
ExchangeRate::BitstampAdapter.instance,
|
179
|
+
ExchangeRate::BtceAdapter.instance,
|
180
|
+
ExchangeRate::KrakenAdapter.instance,
|
181
|
+
ExchangeRate::LocalbitcoinsAdapter.instance,
|
182
|
+
ExchangeRate::OkcoinAdapter.instance
|
172
183
|
]
|
173
184
|
@status_check_schedule = DEFAULT_STATUS_CHECK_SCHEDULE
|
174
185
|
end
|
data/lib/straight/order.rb
CHANGED
@@ -44,6 +44,8 @@ module Straight
|
|
44
44
|
expired: 5 # too much time passed since creating an order
|
45
45
|
}
|
46
46
|
|
47
|
+
attr_reader :old_status
|
48
|
+
|
47
49
|
class IncorrectAmount < Exception; end
|
48
50
|
|
49
51
|
# If you are defining methods in this module, it means you most likely want to
|
@@ -61,7 +63,18 @@ module Straight
|
|
61
63
|
# If as_sym is set to true, then each status is returned as Symbol, otherwise
|
62
64
|
# an equivalent Integer from STATUSES is returned.
|
63
65
|
def status(as_sym: false, reload: false)
|
64
|
-
|
66
|
+
|
67
|
+
if defined?(super)
|
68
|
+
begin
|
69
|
+
@status = super
|
70
|
+
# if no method with arguments found in the class
|
71
|
+
# we're prepending to, then let's use a standard getter
|
72
|
+
# with no argument.
|
73
|
+
rescue ArgumentError
|
74
|
+
@status = super()
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
65
78
|
# Prohibit status update if the order was paid in some way.
|
66
79
|
# This is just a caching workaround so we don't query
|
67
80
|
# the blockchain needlessly. The actual safety switch is in the setter.
|
@@ -105,11 +118,16 @@ module Straight
|
|
105
118
|
# The order in which these statements currently are prevents that error, because
|
106
119
|
# by the time a callback checks the status it's already set.
|
107
120
|
@status_changed = (@status != new_status)
|
121
|
+
@old_status = @status
|
108
122
|
@status = new_status
|
109
|
-
gateway.order_status_changed(self) if
|
123
|
+
gateway.order_status_changed(self) if status_changed?
|
110
124
|
super if defined?(super)
|
111
125
|
end
|
112
126
|
|
127
|
+
def status_changed?
|
128
|
+
@status_changed
|
129
|
+
end
|
130
|
+
|
113
131
|
end
|
114
132
|
|
115
133
|
module Includable
|
@@ -144,22 +162,32 @@ module Straight
|
|
144
162
|
# order.start_periodic_status_check
|
145
163
|
# end
|
146
164
|
#
|
147
|
-
|
148
|
-
|
165
|
+
# `duration` argument (value is in seconds) allows you to
|
166
|
+
# control in what time an order expires. In other words, we
|
167
|
+
# keep checking for new transactions until the time passes.
|
168
|
+
# Then we stop and set Order's status to STATUS[:expired]. See
|
169
|
+
# #check_status_on_schedule for the implementation details.
|
170
|
+
def start_periodic_status_check(duration: 600)
|
171
|
+
check_status_on_schedule(duration: duration)
|
149
172
|
end
|
150
173
|
|
151
174
|
# Recursion here! Keeps calling itself according to the schedule until
|
152
175
|
# either the status changes or the schedule tells it to stop.
|
153
|
-
def check_status_on_schedule(period: 10, iteration_index: 0)
|
176
|
+
def check_status_on_schedule(period: 10, iteration_index: 0, duration: 600, time_passed: 0)
|
154
177
|
self.status(reload: true)
|
155
|
-
|
156
|
-
if
|
157
|
-
|
158
|
-
|
159
|
-
period
|
160
|
-
|
161
|
-
|
162
|
-
|
178
|
+
time_passed += period
|
179
|
+
if duration >= time_passed # Stop checking if status is >= 2
|
180
|
+
if self.status < 2
|
181
|
+
schedule = gateway.status_check_schedule.call(period, iteration_index)
|
182
|
+
sleep period
|
183
|
+
check_status_on_schedule(
|
184
|
+
period: schedule[:period],
|
185
|
+
iteration_index: schedule[:iteration_index],
|
186
|
+
duration: duration,
|
187
|
+
time_passed: time_passed
|
188
|
+
)
|
189
|
+
end
|
190
|
+
elsif self.status < 2
|
163
191
|
self.status = STATUSES[:expired]
|
164
192
|
end
|
165
193
|
end
|
@@ -172,6 +200,11 @@ module Straight
|
|
172
200
|
{ status: status, amount: amount, address: address, tid: tid }
|
173
201
|
end
|
174
202
|
|
203
|
+
def amount_in_btc(as: :number)
|
204
|
+
a = Satoshi.new(amount, from_unit: :satoshi, to_unit: :btc)
|
205
|
+
as == :string ? a.to_unit(as: :string) : a.to_unit
|
206
|
+
end
|
207
|
+
|
175
208
|
end
|
176
209
|
|
177
210
|
end
|
@@ -1,14 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
RSpec.describe Straight::Blockchain::
|
3
|
+
RSpec.describe Straight::Blockchain::BiteasyAdapter do
|
4
4
|
|
5
|
-
subject(:adapter) { Straight::Blockchain::
|
6
|
-
|
7
|
-
it "fetches all transactions for the current address" do
|
8
|
-
address = "3B1QZ8FpAaHBgkSB5gFt76ag5AW9VeP8xp"
|
9
|
-
expect(adapter).to receive(:straighten_transaction).with(anything, address: address).at_least(:once)
|
10
|
-
expect(adapter.fetch_transactions_for(address)).not_to be_empty
|
11
|
-
end
|
5
|
+
subject(:adapter) { Straight::Blockchain::BiteasyAdapter.mainnet_adapter }
|
12
6
|
|
13
7
|
it "fetches the balance for a given address" do
|
14
8
|
address = "3B1QZ8FpAaHBgkSB5gFt76ag5AW9VeP8xp"
|
@@ -20,24 +14,35 @@ RSpec.describe Straight::Blockchain::HelloblockIoAdapter do
|
|
20
14
|
expect(adapter.fetch_transaction(tid)[:total_amount]).to eq(832947)
|
21
15
|
end
|
22
16
|
|
23
|
-
it "
|
24
|
-
|
25
|
-
expect(adapter.
|
17
|
+
it "calculates total_amount of a transaction for the given address only" do
|
18
|
+
t = { 'data' => {'outputs' => [{ 'value' => 1, 'to_address' => 'address1'}, { 'value' => 2, 'to_address' => 'address2'}] } }
|
19
|
+
expect(adapter.send(:straighten_transaction, t, address: 'address1')[:total_amount]).to eq(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "fetches all transactions for the current address" do
|
23
|
+
address = "3B1QZ8FpAaHBgkSB5gFt76ag5AW9VeP8xp"
|
24
|
+
expect(adapter).to receive(:straighten_transaction).with(anything, address: address).at_least(:once)
|
25
|
+
expect(adapter.fetch_transactions_for(address)).not_to be_empty
|
26
26
|
end
|
27
27
|
|
28
|
-
it "
|
28
|
+
it "calculates the number of confirmations for each transaction" do
|
29
29
|
tid = 'ae0d040f48d75fdc46d9035236a1782164857d6f0cca1f864640281115898560'
|
30
30
|
expect(adapter.fetch_transaction(tid)[:confirmations]).to be > 0
|
31
31
|
end
|
32
32
|
|
33
|
-
it "
|
34
|
-
|
35
|
-
expect(
|
33
|
+
it "gets a transaction id among other data" do
|
34
|
+
tid = 'ae0d040f48d75fdc46d9035236a1782164857d6f0cca1f864640281115898560'
|
35
|
+
expect(adapter.fetch_transaction(tid)[:tid]).to eq(tid)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "raises an exception when something goes wrong with fetching data" do
|
39
|
+
expect( -> { adapter.send(:api_request, "/a-404-request") }).to raise_error(Straight::Blockchain::Adapter::RequestError)
|
36
40
|
end
|
37
41
|
|
38
|
-
it "
|
39
|
-
|
40
|
-
|
42
|
+
it "uses the same Singleton instance" do
|
43
|
+
a = Straight::Blockchain::BiteasyAdapter.mainnet_adapter
|
44
|
+
b = Straight::Blockchain::BiteasyAdapter.mainnet_adapter
|
45
|
+
expect(a).to eq(b)
|
41
46
|
end
|
42
47
|
|
43
48
|
end
|
data/spec/lib/blockchain_adapters/{blockchain_info_spec.rb → blockchain_info_adapter_spec.rb}
RENAMED
@@ -31,7 +31,7 @@ RSpec.describe Straight::Blockchain::BlockchainInfoAdapter do
|
|
31
31
|
end
|
32
32
|
|
33
33
|
it "caches blockchain.info latestblock requests" do
|
34
|
-
expect(adapter).to receive(:
|
34
|
+
expect(adapter).to receive(:api_request).once.and_return('{ "height": 1 }')
|
35
35
|
adapter.send(:calculate_confirmations, { "block_height" => 1 }, force_latest_block_reload: true)
|
36
36
|
adapter.send(:calculate_confirmations, { "block_height" => 1 })
|
37
37
|
adapter.send(:calculate_confirmations, { "block_height" => 1 })
|
@@ -40,8 +40,7 @@ RSpec.describe Straight::Blockchain::BlockchainInfoAdapter do
|
|
40
40
|
end
|
41
41
|
|
42
42
|
it "raises an exception when something goes wrong with fetching datd" do
|
43
|
-
|
44
|
-
expect( -> { adapter.http_request("http://blockchain.info/a-timed-out-request") }).to raise_error(Straight::Blockchain::Adapter::RequestError)
|
43
|
+
expect( -> { adapter.send(:api_request, "/a-404-request") }).to raise_error(Straight::Blockchain::Adapter::RequestError)
|
45
44
|
end
|
46
45
|
|
47
46
|
it "calculates total_amount of a transaction for the given address only" do
|
@@ -49,4 +48,10 @@ RSpec.describe Straight::Blockchain::BlockchainInfoAdapter do
|
|
49
48
|
expect(adapter.send(:straighten_transaction, t, address: 'address1')[:total_amount]).to eq(1)
|
50
49
|
end
|
51
50
|
|
51
|
+
it "uses the same Singleton instance" do
|
52
|
+
a = Straight::Blockchain::BlockchainInfoAdapter.mainnet_adapter
|
53
|
+
b = Straight::Blockchain::BlockchainInfoAdapter.mainnet_adapter
|
54
|
+
expect(a).to eq(b)
|
55
|
+
end
|
56
|
+
|
52
57
|
end
|