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.
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"