straight 0.2.3 → 1.0.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/.travis.yml +5 -0
- data/Gemfile +7 -5
- data/Gemfile.lock +17 -7
- data/README.md +3 -0
- data/Rakefile +9 -0
- data/VERSION +1 -1
- data/lib/straight.rb +9 -1
- data/lib/straight/address_providers/base.rb +28 -0
- data/lib/straight/address_providers/bip32.rb +22 -0
- data/lib/straight/blockchain_adapter.rb +17 -1
- data/lib/straight/blockchain_adapters/biteasy_adapter.rb +14 -13
- data/lib/straight/blockchain_adapters/blockchain_info_adapter.rb +15 -14
- data/lib/straight/blockchain_adapters/insight_adapter.rb +76 -0
- data/lib/straight/blockchain_adapters/mycelium_adapter.rb +74 -49
- data/lib/straight/exchange_rate_adapter.rb +2 -2
- data/lib/straight/exchange_rate_adapters/average_rate_adapter.rb +1 -1
- data/lib/straight/exchange_rate_adapters/okcoin_adapter.rb +1 -1
- data/lib/straight/faraday_monkeypatch.rb +22 -0
- data/lib/straight/gateway.rb +71 -34
- data/lib/straight/order.rb +43 -32
- data/straight.gemspec +20 -27
- metadata +30 -26
- data/spec/lib/blockchain_adapters/biteasy_adapter_spec.rb +0 -48
- data/spec/lib/blockchain_adapters/blockchain_info_adapter_spec.rb +0 -57
- data/spec/lib/blockchain_adapters/mycelium_adapter_spec.rb +0 -58
- data/spec/lib/exchange_rate_adapter_spec.rb +0 -55
- data/spec/lib/exchange_rate_adapters/average_rate_adapter_spec.rb +0 -43
- data/spec/lib/exchange_rate_adapters/bitpay_adapter_spec.rb +0 -27
- data/spec/lib/exchange_rate_adapters/bitstamp_adapter_spec.rb +0 -27
- data/spec/lib/exchange_rate_adapters/btce_adapter_spec.rb +0 -27
- data/spec/lib/exchange_rate_adapters/coinbase_adapter_spec.rb +0 -27
- data/spec/lib/exchange_rate_adapters/kraken_adapter_spec.rb +0 -27
- data/spec/lib/exchange_rate_adapters/localbitcoins_adapter_spec.rb +0 -27
- data/spec/lib/exchange_rate_adapters/okcoin_adapter_spec.rb +0 -27
- data/spec/lib/gateway_spec.rb +0 -98
- data/spec/lib/order_spec.rb +0 -128
- data/spec/spec_helper.rb +0 -1
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe Straight::ExchangeRate::CoinbaseAdapter do
|
4
|
-
|
5
|
-
before(:each) do
|
6
|
-
@exchange_adapter = Straight::ExchangeRate::CoinbaseAdapter.instance
|
7
|
-
end
|
8
|
-
|
9
|
-
it "finds the rate for currency code" do
|
10
|
-
expect(@exchange_adapter.rate_for('USD')).to be_kind_of(Float)
|
11
|
-
expect( -> { @exchange_adapter.rate_for('FEDcoin') }).to raise_error(Straight::ExchangeRate::Adapter::CurrencyNotSupported)
|
12
|
-
end
|
13
|
-
|
14
|
-
it "raises exception if rate is nil" do
|
15
|
-
json_response_1 = '{}'
|
16
|
-
json_response_2 = '{"btc_to_urd":"224.41","usd_to_xpf":"105.461721","bsd_to_btc":"0.004456"}'
|
17
|
-
json_response_3 = '{"btc_to_usd":null,"usd_to_xpf":"105.461721","bsd_to_btc":"0.004456"}'
|
18
|
-
uri_mock = double('uri mock')
|
19
|
-
allow(uri_mock).to receive(:read).with(read_timeout: 4).and_return(json_response_1, json_response_2, json_response_3)
|
20
|
-
allow(URI).to receive(:parse).and_return(uri_mock)
|
21
|
-
3.times do
|
22
|
-
@exchange_adapter.fetch_rates!
|
23
|
-
expect( -> { @exchange_adapter.rate_for('USD') }).to raise_error(Straight::ExchangeRate::Adapter::CurrencyNotSupported)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe Straight::ExchangeRate::KrakenAdapter do
|
4
|
-
|
5
|
-
before(:each) do
|
6
|
-
@exchange_adapter = Straight::ExchangeRate::KrakenAdapter.instance
|
7
|
-
end
|
8
|
-
|
9
|
-
it "finds the rate for currency code" do
|
10
|
-
expect(@exchange_adapter.rate_for('USD')).to be_kind_of(Float)
|
11
|
-
expect( -> { @exchange_adapter.rate_for('FEDcoin') }).to raise_error(Straight::ExchangeRate::Adapter::CurrencyNotSupported)
|
12
|
-
end
|
13
|
-
|
14
|
-
it "raises exception if rate is nil" do
|
15
|
-
json_response_1 = '{"error":[],"result":{}}'
|
16
|
-
json_response_2 = '{"error":[],"result":{"XXBTZUSD":{"a":["244.95495","1"],"b":["227.88761","1"],"abc":["228.20494","0.10000000"],"v":["5.18645337","24.04033530"],"p":["234.92296","234.41356"],"t":[45,74],"l":["228.20494","228.20494"],"h":["239.36069","239.36069"],"o":"231.82976"}}}'
|
17
|
-
json_response_3 = '{"error":[],"result":{"XXBTZUSD":{"a":["244.95495","1"],"b":["227.88761","1"],"c":[null,"0.10000000"],"v":["5.18645337","24.04033530"],"p":["234.92296","234.41356"],"t":[45,74],"l":["228.20494","228.20494"],"h":["239.36069","239.36069"],"o":"231.82976"}}}'
|
18
|
-
uri_mock = double('uri mock')
|
19
|
-
allow(uri_mock).to receive(:read).with(read_timeout: 4).and_return(json_response_1, json_response_2, json_response_3)
|
20
|
-
allow(URI).to receive(:parse).and_return(uri_mock)
|
21
|
-
3.times do
|
22
|
-
@exchange_adapter.fetch_rates!
|
23
|
-
expect( -> { @exchange_adapter.rate_for('USD') }).to raise_error(Straight::ExchangeRate::Adapter::CurrencyNotSupported)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe Straight::ExchangeRate::LocalbitcoinsAdapter do
|
4
|
-
|
5
|
-
before(:each) do
|
6
|
-
@exchange_adapter = Straight::ExchangeRate::LocalbitcoinsAdapter.instance
|
7
|
-
end
|
8
|
-
|
9
|
-
it "finds the rate for currency code" do
|
10
|
-
expect(@exchange_adapter.rate_for('USD')).to be_kind_of(Float)
|
11
|
-
expect( -> { @exchange_adapter.rate_for('FEDcoin') }).to raise_error(Straight::ExchangeRate::Adapter::CurrencyNotSupported)
|
12
|
-
end
|
13
|
-
|
14
|
-
it "rases exception if rate is nil" do
|
15
|
-
json_response_1 = '{"USD": {}}'
|
16
|
-
json_response_2 = '{"USD": {"volume_btc": "2277.85", "rates": {"bambo": "263.78"}, "avg_1h": 287.6003904801631, "avg_24h": 253.58144543993674, "avg_12h": 252.29202866050034}}'
|
17
|
-
json_response_3 = '{"USD": {"volume_btc": "2277.85", "rates": {"last": null}, "avg_1h": 287.6003904801631, "avg_24h": 253.58144543993674, "avg_12h": 252.29202866050034}}'
|
18
|
-
uri_mock = double('uri mock')
|
19
|
-
allow(uri_mock).to receive(:read).with(read_timeout: 4).and_return(json_response_1, json_response_2, json_response_3)
|
20
|
-
allow(URI).to receive(:parse).and_return(uri_mock)
|
21
|
-
3.times do
|
22
|
-
@exchange_adapter.fetch_rates!
|
23
|
-
expect( -> { @exchange_adapter.rate_for('USD') }).to raise_error(Straight::ExchangeRate::Adapter::CurrencyNotSupported)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe Straight::ExchangeRate::OkcoinAdapter do
|
4
|
-
|
5
|
-
before(:each) do
|
6
|
-
@exchange_adapter = Straight::ExchangeRate::OkcoinAdapter.instance
|
7
|
-
end
|
8
|
-
|
9
|
-
it "finds the rate for currency code" do
|
10
|
-
expect(@exchange_adapter.rate_for('USD')).to be_kind_of(Float)
|
11
|
-
expect( -> { @exchange_adapter.rate_for('FEDcoin') }).to raise_error(Straight::ExchangeRate::Adapter::CurrencyNotSupported)
|
12
|
-
end
|
13
|
-
|
14
|
-
it "rases exception if rate is nil" do
|
15
|
-
json_response_1 = '{"date":"1422679981","ticker":{}}'
|
16
|
-
json_response_2 = '{"date":"1422679981","ticker":{"buy":"227.27","high":"243.55","bambo":"226.89","low":"226.0","sell":"227.74","vol":"16065.2085"}}'
|
17
|
-
json_response_3 = '{"date":"1422679981","ticker":{"buy":"227.27","high":"243.55","last":null,"low":"226.0","sell":"227.74","vol":"16065.2085"}}'
|
18
|
-
uri_mock = double('uri mock')
|
19
|
-
allow(uri_mock).to receive(:read).with(read_timeout: 4).and_return(json_response_1, json_response_2, json_response_3)
|
20
|
-
allow(URI).to receive(:parse).and_return(uri_mock)
|
21
|
-
3.times do
|
22
|
-
@exchange_adapter.fetch_rates!
|
23
|
-
expect( -> { @exchange_adapter.rate_for('USD') }).to raise_error(Straight::ExchangeRate::Adapter::CurrencyNotSupported)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
data/spec/lib/gateway_spec.rb
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe Straight::Gateway do
|
4
|
-
|
5
|
-
before(:each) do
|
6
|
-
@mock_adapter = double("mock blockchain adapter")
|
7
|
-
@gateway = Straight::Gateway.new
|
8
|
-
@gateway.pubkey = "pubkey"
|
9
|
-
@gateway.order_class = "Straight::Order"
|
10
|
-
@gateway.blockchain_adapters = [@mock_adapter]
|
11
|
-
@gateway.status_check_schedule = Straight::Gateway::DEFAULT_STATUS_CHECK_SCHEDULE
|
12
|
-
@gateway.order_callbacks = []
|
13
|
-
end
|
14
|
-
|
15
|
-
it "shares exchange rate adapter(s) instances between all/multiple gateway instances" do
|
16
|
-
gateway_2 = Straight::Gateway.new.tap do |g|
|
17
|
-
g.pubkey = "pubkey"
|
18
|
-
g.order_class = "Straight::Order"
|
19
|
-
g.blockchain_adapters = [@mock_adapter]
|
20
|
-
g.status_check_schedule = Straight::Gateway::DEFAULT_STATUS_CHECK_SCHEDULE
|
21
|
-
g.order_callbacks = []
|
22
|
-
end
|
23
|
-
# Checking if exchange rate adapters are the same objects for both gateways
|
24
|
-
@gateway.instance_variable_get(:@exchange_rate_adapters).each_with_index do |adapter, i|
|
25
|
-
expect(gateway_2.instance_variable_get(:@exchange_rate_adapters)[i]).to be adapter
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
it "passes methods on to the available adapter" do
|
30
|
-
@gateway.instance_variable_set('@blockchain_adapters', [@mock_adapter])
|
31
|
-
expect(@mock_adapter).to receive(:fetch_transaction).once
|
32
|
-
@gateway.fetch_transaction("xxx")
|
33
|
-
end
|
34
|
-
|
35
|
-
it "uses the next availabale adapter when something goes wrong with the current one" do
|
36
|
-
another_mock_adapter = double("another_mock blockchain adapter")
|
37
|
-
@gateway.instance_variable_set('@blockchain_adapters', [@mock_adapter, another_mock_adapter])
|
38
|
-
allow(@mock_adapter).to receive(:fetch_transaction).once.and_raise(Exception)
|
39
|
-
expect(another_mock_adapter).to receive(:fetch_transaction).once
|
40
|
-
@gateway.fetch_transaction("xxx")
|
41
|
-
end
|
42
|
-
|
43
|
-
it "creates new orders and addresses for them" do
|
44
|
-
@gateway.pubkey = 'xpub661MyMwAqRbcFhUeRviyfia1NdfX4BAv5zCsZ6HqsprRjdBDK8vwh3kfcnTvqNbmi5S1yZ5qL9ugZTyVqtyTZxccKZzMVMCQMhARycvBZvx'
|
45
|
-
expected_address = '1NEvrcxS3REbJgup8rMA4QvMFFSdWTLvM'
|
46
|
-
expect(@gateway.order_for_keychain_id(amount: 1, keychain_id: 1).address).to eq(expected_address)
|
47
|
-
end
|
48
|
-
|
49
|
-
it "checks the depth of the xpub key and uses derivation notation according to the depth" do
|
50
|
-
@gateway.pubkey = 'xpub6DkF8MtNnbJY6NBR5tBSZ2NYQL16sT4fiT1R8U4D24bfSNmzzbhzNdP25LLgmis6c7EsVdzdUqv1MZoU8TNHaNNRHfTE1hqXRNMfPyJ2fCw'
|
51
|
-
expect(@gateway.address_for_keychain_id(0)).to eq("1GS7KMkpz27xpFKddFPkjqCAAPPo9eyUev")
|
52
|
-
end
|
53
|
-
|
54
|
-
it "calls all the order callbacks" do
|
55
|
-
callback1 = double('callback1')
|
56
|
-
callback2 = double('callback1')
|
57
|
-
@gateway.pubkey = MoneyTree::Master.new.to_bip32
|
58
|
-
@gateway.order_callbacks = [callback1, callback2]
|
59
|
-
|
60
|
-
order = @gateway.order_for_keychain_id(amount: 1, keychain_id: 1)
|
61
|
-
expect(callback1).to receive(:call).with(order)
|
62
|
-
expect(callback2).to receive(:call).with(order)
|
63
|
-
@gateway.order_status_changed(order)
|
64
|
-
end
|
65
|
-
|
66
|
-
describe "exchange rate calculation" do
|
67
|
-
|
68
|
-
it "sets order amount in satoshis calculated from another currency" do
|
69
|
-
adapter = Straight::ExchangeRate::BitpayAdapter.instance
|
70
|
-
allow(adapter).to receive(:rate_for).and_return(450.5412)
|
71
|
-
@gateway.exchange_rate_adapters = [adapter]
|
72
|
-
expect(@gateway.amount_from_exchange_rate(2252.706, currency: 'USD')).to eq(500000000)
|
73
|
-
end
|
74
|
-
|
75
|
-
it "tries various exchange adapters until one of them actually returns an exchange rate" do
|
76
|
-
adapter1 = Straight::ExchangeRate::BitpayAdapter.instance
|
77
|
-
adapter2 = Straight::ExchangeRate::BitpayAdapter.instance
|
78
|
-
allow(adapter1).to receive(:rate_for).and_return( -> { raise "connection problem" })
|
79
|
-
allow(adapter2).to receive(:rate_for).and_return(450.5412)
|
80
|
-
@gateway.exchange_rate_adapters = [adapter1, adapter2]
|
81
|
-
expect(@gateway.amount_from_exchange_rate(2252.706, currency: 'USD')).to eq(500000000)
|
82
|
-
end
|
83
|
-
|
84
|
-
it "converts btc denomination into satoshi if provided with :btc_denomination" do
|
85
|
-
expect(@gateway.amount_from_exchange_rate(5, currency: 'BTC', btc_denomination: :btc)).to eq(500000000)
|
86
|
-
end
|
87
|
-
|
88
|
-
it "accepts string as amount and converts it properly" do
|
89
|
-
expect(@gateway.amount_from_exchange_rate('0.5', currency: 'BTC', btc_denomination: :btc)).to eq(50000000)
|
90
|
-
end
|
91
|
-
|
92
|
-
it "simply fetches current exchange rate for 1 BTC" do
|
93
|
-
expect(@gateway.current_exchange_rate('USD')).not_to be_nil
|
94
|
-
end
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
end
|
data/spec/lib/order_spec.rb
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe Straight::Order do
|
4
|
-
|
5
|
-
class Straight::Order
|
6
|
-
|
7
|
-
def status=(new_value)
|
8
|
-
# we later make sure this method also gets called
|
9
|
-
@original_status_setter_called = true
|
10
|
-
end
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
before(:each) do
|
15
|
-
@gateway = double("Straight Gateway mock")
|
16
|
-
@order = Straight::Order.new
|
17
|
-
@order.amount = 10
|
18
|
-
@order.gateway = @gateway
|
19
|
-
@order.address = 'address'
|
20
|
-
@order.keychain_id = 1
|
21
|
-
allow(@gateway).to receive(:order_status_changed).with(@order)
|
22
|
-
allow(@gateway).to receive(:fetch_transactions_for).with(@order.address).and_return([{ tid: 'xxx'}])
|
23
|
-
end
|
24
|
-
|
25
|
-
it "follows status check schedule" do
|
26
|
-
allow(@gateway).to receive(:fetch_transactions_for).with('address').and_return([])
|
27
|
-
allow(@gateway).to receive(:status_check_schedule).and_return(Straight::Gateway::DEFAULT_STATUS_CHECK_SCHEDULE)
|
28
|
-
[10, 20, 40, 80, 160, 320, 640].each do |i|
|
29
|
-
expect(@order).to receive(:sleep).with(i).exactly(20).times
|
30
|
-
end
|
31
|
-
@order.start_periodic_status_check(duration: 25400)
|
32
|
-
end
|
33
|
-
|
34
|
-
it "gets the last transaction for the current address, caches the request" do
|
35
|
-
expect(@gateway).to receive(:fetch_transactions_for).with(@order.address).once.and_return(true)
|
36
|
-
@order.transactions
|
37
|
-
@order.transactions
|
38
|
-
end
|
39
|
-
|
40
|
-
it "gets all transactions for the current address, caches the request" do
|
41
|
-
expect(@gateway).to receive(:fetch_transactions_for).with(@order.address).once.and_return(['t1', 't2'])
|
42
|
-
expect(@order.transaction).to eq('t1')
|
43
|
-
expect(@order.transaction).to eq('t1')
|
44
|
-
end
|
45
|
-
|
46
|
-
it "sets transaction id when the status is changed from 0" do
|
47
|
-
@order.status = 2
|
48
|
-
expect(@order.tid).to eq('xxx') # xxx because that's what we set in the allow() in before(:each) block
|
49
|
-
end
|
50
|
-
|
51
|
-
it "displays order attributes as json" do
|
52
|
-
allow(@order).to receive(:status).and_return(1)
|
53
|
-
expect(@order.to_json).to eq('{"status":1,"amount":10,"address":"address","tid":null}')
|
54
|
-
end
|
55
|
-
|
56
|
-
it "returns amount in btc as a string" do
|
57
|
-
@order.amount = 1
|
58
|
-
expect(@order.amount_in_btc).to eq(0.00000001)
|
59
|
-
expect(@order.amount_in_btc(as: :string)).to eq('0.00000001')
|
60
|
-
end
|
61
|
-
|
62
|
-
describe "assigning statuses" do
|
63
|
-
|
64
|
-
before(:each) do
|
65
|
-
allow(@gateway).to receive(:confirmations_required).and_return(1)
|
66
|
-
end
|
67
|
-
|
68
|
-
it "doesn't reload the transaction unless forced" do
|
69
|
-
@order.instance_variable_set(:@status, 2)
|
70
|
-
expect(@order).to_not receive(:transaction)
|
71
|
-
@order.status
|
72
|
-
end
|
73
|
-
|
74
|
-
it "sets status to :new upon order creation" do
|
75
|
-
expect(@order.instance_variable_get(:@status)).to eq(0)
|
76
|
-
expect(@order.instance_variable_get(:@old_status)).to eq(nil)
|
77
|
-
end
|
78
|
-
|
79
|
-
it "sets status to :new if no transaction issued" do
|
80
|
-
expect(@order).to receive(:transaction).at_most(3).times.and_return(nil)
|
81
|
-
expect(@order.status(reload: true)).to eq(0)
|
82
|
-
end
|
83
|
-
|
84
|
-
it "sets status to :unconfirmed if transaction doesn't have enough confirmations" do
|
85
|
-
transaction = { confirmations: 0 }
|
86
|
-
expect(@order).to receive(:transaction).at_most(3).times.and_return(transaction)
|
87
|
-
expect(@order.status(reload: true)).to eq(1)
|
88
|
-
end
|
89
|
-
|
90
|
-
it "sets status to :paid if transaction has enough confirmations and the amount is correct" do
|
91
|
-
transaction = { confirmations: 1, total_amount: @order.amount }
|
92
|
-
expect(@order).to receive(:transaction).at_most(3).times.and_return(transaction)
|
93
|
-
expect(@order.status(reload: true)).to eq(2)
|
94
|
-
end
|
95
|
-
|
96
|
-
it "sets status to :underpaid if the total amount in a transaction is less than the amount of order" do
|
97
|
-
transaction = { confirmations: 1, total_amount: @order.amount-1 }
|
98
|
-
expect(@order).to receive(:transaction).at_most(3).times.and_return(transaction)
|
99
|
-
expect(@order.status(reload: true)).to eq(3)
|
100
|
-
end
|
101
|
-
|
102
|
-
it "sets status to :overderpaid if the total amount in a transaction is more than the amount of order" do
|
103
|
-
transaction = { confirmations: 1, total_amount: @order.amount+1 }
|
104
|
-
expect(@order).to receive(:transaction).at_most(3).times.and_return(transaction)
|
105
|
-
expect(@order.status(reload: true)).to eq(4)
|
106
|
-
end
|
107
|
-
|
108
|
-
it "invokes a callback on the gateway when status changes" do
|
109
|
-
transaction = { confirmations: 1, total_amount: @order.amount }
|
110
|
-
allow(@order).to receive(:transaction).and_return(transaction)
|
111
|
-
expect(@gateway).to receive(:order_status_changed).with(@order)
|
112
|
-
@order.status(reload: true)
|
113
|
-
end
|
114
|
-
|
115
|
-
it "calls the original status setter of the class that the module is included into" do
|
116
|
-
expect(@order.instance_variable_get(:@original_status_setter_called)).to be_falsy
|
117
|
-
@order.status = 1
|
118
|
-
expect(@order.instance_variable_get(:@original_status_setter_called)).to be_truthy
|
119
|
-
end
|
120
|
-
|
121
|
-
it "saves the old status in the old_status property" do
|
122
|
-
@order.status = 2
|
123
|
-
expect(@order.old_status).to eq(0)
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require_relative "../lib/straight"
|