straight 0.2.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|