straight 0.2.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -0
  3. data/Gemfile +7 -5
  4. data/Gemfile.lock +17 -7
  5. data/README.md +3 -0
  6. data/Rakefile +9 -0
  7. data/VERSION +1 -1
  8. data/lib/straight.rb +9 -1
  9. data/lib/straight/address_providers/base.rb +28 -0
  10. data/lib/straight/address_providers/bip32.rb +22 -0
  11. data/lib/straight/blockchain_adapter.rb +17 -1
  12. data/lib/straight/blockchain_adapters/biteasy_adapter.rb +14 -13
  13. data/lib/straight/blockchain_adapters/blockchain_info_adapter.rb +15 -14
  14. data/lib/straight/blockchain_adapters/insight_adapter.rb +76 -0
  15. data/lib/straight/blockchain_adapters/mycelium_adapter.rb +74 -49
  16. data/lib/straight/exchange_rate_adapter.rb +2 -2
  17. data/lib/straight/exchange_rate_adapters/average_rate_adapter.rb +1 -1
  18. data/lib/straight/exchange_rate_adapters/okcoin_adapter.rb +1 -1
  19. data/lib/straight/faraday_monkeypatch.rb +22 -0
  20. data/lib/straight/gateway.rb +71 -34
  21. data/lib/straight/order.rb +43 -32
  22. data/straight.gemspec +20 -27
  23. metadata +30 -26
  24. data/spec/lib/blockchain_adapters/biteasy_adapter_spec.rb +0 -48
  25. data/spec/lib/blockchain_adapters/blockchain_info_adapter_spec.rb +0 -57
  26. data/spec/lib/blockchain_adapters/mycelium_adapter_spec.rb +0 -58
  27. data/spec/lib/exchange_rate_adapter_spec.rb +0 -55
  28. data/spec/lib/exchange_rate_adapters/average_rate_adapter_spec.rb +0 -43
  29. data/spec/lib/exchange_rate_adapters/bitpay_adapter_spec.rb +0 -27
  30. data/spec/lib/exchange_rate_adapters/bitstamp_adapter_spec.rb +0 -27
  31. data/spec/lib/exchange_rate_adapters/btce_adapter_spec.rb +0 -27
  32. data/spec/lib/exchange_rate_adapters/coinbase_adapter_spec.rb +0 -27
  33. data/spec/lib/exchange_rate_adapters/kraken_adapter_spec.rb +0 -27
  34. data/spec/lib/exchange_rate_adapters/localbitcoins_adapter_spec.rb +0 -27
  35. data/spec/lib/exchange_rate_adapters/okcoin_adapter_spec.rb +0 -27
  36. data/spec/lib/gateway_spec.rb +0 -98
  37. data/spec/lib/order_spec.rb +0 -128
  38. 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
@@ -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
@@ -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"