cryptsy 0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 86fbb550ff9903ff92f1ed6bd6f72addb0f7c262
4
+ data.tar.gz: 21735c159cd27ce56adcbf629264b5da170692bd
5
+ SHA512:
6
+ metadata.gz: f541a58e0610ba764d25de2cc0d2c4173b33f77aa58d96074e16a70113393c32c6d0a9b43ca58b5c0ad251f1b6c159d27031c1e2187166c8c362ff3d51ebe534
7
+ data.tar.gz: 7dbafded98719d639791e1997bd4199e9af63382967ff02ce939a3c513423085cd5a954d0a096b53b0668b20234750572f4b5d699647edd4bb42526423fa740c
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014 Ian Unruh
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,166 @@
1
+ # Cryptsy
2
+
3
+ Provides an idiomatic Ruby client for the authenticated Cryptsy API
4
+
5
+ ## JSON client
6
+
7
+ Grab your API keys from the Crypsty website
8
+
9
+ ```ruby
10
+ client = Cryptsy::Client.new('YOUR PUBLIC KEY', 'YOUR PRIVATE KEY')
11
+ ```
12
+
13
+ ### Check your account details
14
+
15
+ ```ruby
16
+ # Get account blances
17
+ client.info.balances_available
18
+
19
+ # Get transfers, transactions, open orders
20
+ client.transfers
21
+ client.transactions
22
+ client.orders
23
+ ```
24
+
25
+ ### Query markets
26
+
27
+ ```ruby
28
+ client.markets
29
+ client.market_by_pair('DOGE', 'BTC')
30
+ ```
31
+
32
+ ### Generate new receive addresses
33
+
34
+ ```ruby
35
+ client.generate_deposit_address('DOGE') # => 'DTP2Na7P4JpwhUuPTdJWjGzAK9P5VXF5Zd'
36
+ client.generate_deposit_address('BTC') # => '156q4WMvWmCSmTdZSVVn8zdnDFJWZsb6XW'
37
+ ```
38
+
39
+ ### Make withdrawals
40
+
41
+ ```ruby
42
+ client.make_withdrawal('DKkNNCF2DRcHtSgbKeDTVd2T8qjng1Z8hV', 1250.0)
43
+ ```
44
+
45
+ Note that this method only works for addresses that have been pre-approved. Use the web client
46
+ if you wish to automate the pre-approval process.
47
+
48
+ ## Web client
49
+
50
+ This package provides a web client for doing unsafe operations on Cryptsy. The official JSON
51
+ API does not allow you to withdraw funds to addresses that have not been pre-approved. Nor does
52
+ it let you pre-approval addresses. Therefore, you would be forced to login to the HTML web interface,
53
+ and input your password and TFA token to do withdrawals and add trusted addresses.
54
+
55
+ This client is rough around the edges, but the basic functionality is there. It has many extra dependencies,
56
+ so you have to explicitly require it and install the dependencies.
57
+
58
+ ```sh
59
+ gem install faraday-cookie_jar nokogiri rotp
60
+ ```
61
+
62
+ ```ruby
63
+ require 'cryptsy/web_client'
64
+
65
+ web_client = Cryptsy::WebClient.new('YOUR CRYPTSY USERNAME', 'YOUR CRYPTSY PASSWORD', 'YOUR TFA SECRET')
66
+ web_client.login
67
+ web_client.pincode
68
+ ```
69
+
70
+ Now that you have a session on Cryptsy, you can perform privileged operations.
71
+
72
+ ### Withdrawals
73
+
74
+ If you wish to make a withdrawal to an address that has not been pre-approved, use the following:
75
+
76
+ ```ruby
77
+ web_client.make_withdrawal(94, 'DKkNNCF2DRcHtSgbKeDTVd2T8qjng1Z8hV', 1250.0)
78
+ ```
79
+
80
+ You will receive a confirmation email after a short period. The link in this email must be visited
81
+ for the withdrawal to continue.
82
+
83
+ Note that you **will not** be able to use this for trusted addresses. Instead, use the respective method
84
+ on the regular JSON client.
85
+
86
+ ### Trusted addresses
87
+
88
+ If you wish to make an address trusted, use the following:
89
+
90
+ ```ruby
91
+ web_client.add_trusted_address('DQRhettwhyR6xeK6xFQ2nbhjhSTgZzdgMR')
92
+ ```
93
+
94
+ You will receive a confirmatin email after a short period. The link in this email must be visited
95
+ for the address to become trusted.
96
+
97
+ Note that you **will not** be able to use the web client to make withdrawals to this address now. Instead,
98
+ use the respective method on the regular JSON client.
99
+
100
+ ### Caching sessions
101
+
102
+ The web client uses Faraday with middleware for [HTTP::CookieJar](https://github.com/sparklemotion/http-cookie).
103
+
104
+ The cookie jar is accessible, so you can save and load cookies to a file between uses.
105
+
106
+ ```ruby
107
+ jar = web_client.cookie_jar
108
+
109
+ jar.load('path/to/cookies.txt')
110
+ jar.cleanup
111
+
112
+ jar.save('path/to/cookies.txt', session: true)
113
+ ```
114
+
115
+ ## Confirmation email polling
116
+
117
+ Take automation to the next level! Using `ConfirmationPoller`, you can scan the email account associated with your
118
+ Cryptsy account. Combining this with the web client allows you to automatically confirm:
119
+
120
+ - Trusted addresses
121
+ - Withdrawals to untrusted addresses
122
+
123
+ Refer to `examples/gmail_poller.rb` to see basic integration with Gmail. Combine it with the web client like so:
124
+
125
+ ```ruby
126
+ adapter = GmailAdapter.new('GMAIL USERNAME', 'GMAIL PASSWORD')
127
+ web_client = Cryptsy::WebClient.new('CRYPTSY USERNAME', 'CRYPTSY PASSWORD', 'TFA SECRET')
128
+ poller = Cryptsy::ConfirmationPoller.new(adapter, CONFIRM_TRUSTED_ADDRESS_PATTERN)
129
+
130
+ poller.run_until_found.each do |link|
131
+ web_client.get(link)
132
+ end
133
+
134
+ adapter.logout
135
+ ```
136
+
137
+ It's recommended to setup an application-specific password instead of using your primary Gmail password.
138
+
139
+ ## Security concerns
140
+
141
+ ### SSL verification
142
+
143
+ The certificate for `https://api.cryptsy.com` is invalid. Therefore, any clients that connect to it
144
+ must disable SSL verification. **This opens up the possibility for a MITM attack.**
145
+
146
+ Until this is fixed, avoid experimenting with the JSON client on untrusted networks until this
147
+ is corrected. The web client does not have this vulnerability, the `https://www.cryptsy.com` certificate
148
+ is correct.
149
+
150
+ ### Plaintext credentials
151
+
152
+ Using both clients will result in a large number of credentials needing to be stored in plaintext.
153
+
154
+ This includes the following:
155
+
156
+ - Cryptsy username & password
157
+ - Cryptsy API key pair
158
+ - Cryptsy two-factor authentication (TFA) secret
159
+
160
+ Therefore, you should isolate the use of this client away from a public-facing service. On a separate VM,
161
+ you can use a background worker process, like Sidekiq or Resque.
162
+
163
+ ## To-do
164
+
165
+ - [FIX] Add error handling to web client
166
+ - [FEAT] Add auto-confirm for emailed confirmation links
@@ -0,0 +1,7 @@
1
+ require 'faraday'
2
+
3
+ require 'cryptsy/version'
4
+
5
+ require 'cryptsy/client'
6
+ require 'cryptsy/confirmation_poller'
7
+ require 'cryptsy/errors'
@@ -0,0 +1,171 @@
1
+ require 'hashie'
2
+ require 'json'
3
+ require 'openssl'
4
+ require 'uri'
5
+
6
+ module Cryptsy
7
+ class Client
8
+ DEFAULT_OPTIONS = {
9
+ url: 'https://api.cryptsy.com',
10
+ ssl: {
11
+ verify: false
12
+ }
13
+ }
14
+
15
+ ORDER_TYPE_BUY = 'Buy'
16
+ ORDER_TYPE_SELL = 'Sell'
17
+
18
+ # @param [String] public_key
19
+ # @param [String] private_key
20
+ # @param [Hash] options
21
+ def initialize(public_key, private_key, options = {})
22
+ @public_key = public_key
23
+ @private_key = private_key
24
+ @digest = OpenSSL::Digest::SHA512.new
25
+ @connection = Faraday.new(DEFAULT_OPTIONS.merge(options))
26
+ end
27
+
28
+ def info
29
+ call(:getinfo)
30
+ end
31
+
32
+ def markets
33
+ call(:getmarkets)
34
+ end
35
+
36
+ def market_by_pair(primary_code, secondary_code)
37
+ markets.find do |market|
38
+ market.primary_currency_code == normalize_currency_code(primary_code) &&
39
+ market.secondary_currency_code == normalize_currency_code(secondary_code)
40
+ end
41
+ end
42
+
43
+ def orders(market_id)
44
+ call(:myorders, marketid: market_id)
45
+ end
46
+
47
+ def all_orders
48
+ call(:allmyorders)
49
+ end
50
+
51
+ def trades(market_id, limit = 200)
52
+ call(:mytrades, marketid: market_id, limit: limit)
53
+ end
54
+
55
+ def all_trades
56
+ call(:allmytrades)
57
+ end
58
+
59
+ def transactions
60
+ call(:mytransactions)
61
+ end
62
+
63
+ def transfers
64
+ call(:mytransfers)
65
+ end
66
+
67
+ def market_depth(market_id)
68
+ call(:depth, marketid: market_id)
69
+ end
70
+
71
+ def market_orders(market_id)
72
+ call(:marketorders, marketid: market_id)
73
+ end
74
+
75
+ def market_trades(market_id)
76
+ call(:markettrades, marketid: market_id)
77
+ end
78
+
79
+ def create_buy_order(market_id, quantity, price)
80
+ create_order(market_id, ORDER_TYPE_BUY, quantity, price)
81
+ end
82
+
83
+ def create_sell_order(market_id, quantity, price)
84
+ create_order(market_id, ORDER_TYPE_SELL, quantity, price)
85
+ end
86
+
87
+ def create_order(market_id, order_type, quantity, price)
88
+ call(:createorder, marketid: market_id, ordertype: order_type, quantity: quantity, price: price)
89
+ end
90
+
91
+ def cancel_order(order_id)
92
+ call(:cancelorder, orderid: order_id)
93
+ end
94
+
95
+ def cancel_orders(market_id)
96
+ call(:cancelmarketorders, marketid: market_id)
97
+ end
98
+
99
+ def cancel_all_orders
100
+ call(:cancelallorders)
101
+ end
102
+
103
+ def calculate_buy_fees(quantity, price)
104
+ calculate_fees(ORDER_TYPE_BUY, quantity, price)
105
+ end
106
+
107
+ def calculate_sell_fees(quantity, price)
108
+ calculate_fees(ORDER_TYPE_SELL, quantity, price)
109
+ end
110
+
111
+ def calculate_fees(order_type, quantity, price)
112
+ call(:calculatefees, ordertype: order_type, quantity: quantity, price: price)
113
+ end
114
+
115
+ def generate_new_address(currency)
116
+ if currency.is_a?(Integer)
117
+ call(:generatenewaddress, currencyid: currency).address
118
+ else
119
+ call(:generatenewaddress, currencycode: normalize_currency_code(currency)).address
120
+ end
121
+ end
122
+
123
+ def make_withdrawal(address, amount)
124
+ call(:makewithdrawal, address: address, amount: amount)
125
+ end
126
+
127
+ # @raise [CryptsyError]
128
+ # @param [Symbol] method_name
129
+ # @param [Hash] params
130
+ # @return [Object]
131
+ def call(method_name, params = {})
132
+ request = {
133
+ method: method_name,
134
+ nonce: Time.now.to_i
135
+ }.merge(params)
136
+
137
+ body = URI.encode_www_form(request)
138
+ signature = OpenSSL::HMAC.hexdigest(@digest, @private_key, body)
139
+
140
+ response = @connection.post do |req|
141
+ req.url 'api'
142
+ req.body = body
143
+
144
+ req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
145
+ req.headers['Key'] = @public_key
146
+ req.headers['Sign'] = signature
147
+ end
148
+
149
+ process_response(response)
150
+ end
151
+
152
+ # @raise [CryptsyError]
153
+ # @param [Faraday::Response] response
154
+ # @return [Object]
155
+ def process_response(response)
156
+ body = Hashie::Mash.new(JSON.parse(response.body))
157
+
158
+ unless body.success.to_i == 1
159
+ raise ClientError, body.error
160
+ end
161
+
162
+ body.return
163
+ end
164
+
165
+ # @param [Object] code
166
+ # @return [String]
167
+ def normalize_currency_code(code)
168
+ code.to_s.upcase
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,47 @@
1
+ require 'nokogiri'
2
+
3
+ module Cryptsy
4
+ class ConfirmationPoller
5
+ # @param [Object] adapter
6
+ # @param [Regexp] pattern
7
+ def initialize(adapter, pattern)
8
+ @adapter = adapter
9
+ @pattern = pattern
10
+ end
11
+
12
+ # @return [Enumerable]
13
+ def run_once
14
+ links = []
15
+
16
+ @adapter.call do |email|
17
+ scan_links(links, email)
18
+ end
19
+
20
+ links
21
+ end
22
+
23
+ # @param [Integer] sleep_interval
24
+ # @return [void]
25
+ def run_until_found(sleep_interval = 3)
26
+ loop do
27
+ links = run_once
28
+ return links unless links.empty?
29
+ sleep sleep_interval
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ # @param [Array] links
36
+ # @param [String] email
37
+ # @return [void]
38
+ def scan_links(links, email)
39
+ doc = Nokogiri::HTML(email.to_s)
40
+ doc.xpath('//a').each do |link|
41
+ if link[:href] =~ @pattern
42
+ links.push($1)
43
+ end
44
+ end
45
+ end
46
+ end # ConfirmationPoller
47
+ end
@@ -0,0 +1,3 @@
1
+ module Cryptsy
2
+ ClientError = Class.new(RuntimeError)
3
+ end
@@ -0,0 +1,3 @@
1
+ module Cryptsy
2
+ VERSION = '0.1'
3
+ end
@@ -0,0 +1,134 @@
1
+ require 'faraday-cookie_jar'
2
+ require 'nokogiri'
3
+ require 'rotp'
4
+
5
+ module Cryptsy
6
+ # Unsafe client that allows you to do things that the Cryptsy API does not permit,
7
+ # including withdrawals to untrusted addresses, as well as pre-approving addresses
8
+ # for withdrawals
9
+ class WebClient
10
+ DEFAULT_OPTIONS = {
11
+ url: 'https://www.cryptsy.com'
12
+ }
13
+
14
+ # @param [HTTP::CookieJar]
15
+ attr_reader :cookie_jar
16
+
17
+ # @param [String] username
18
+ # @param [String] password
19
+ # @param [String] tfa_secret
20
+ # @param [Hash] options
21
+ def initialize(username, password, tfa_secret, options = {})
22
+ @username = username
23
+ @password = password
24
+ @tfa = ROTP::TOTP.new(tfa_secret)
25
+
26
+ @cookie_jar = HTTP::CookieJar.new
27
+
28
+ @connection = Faraday.new(DEFAULT_OPTIONS.merge(options)) do |builder|
29
+ builder.use :cookie_jar, jar: @cookie_jar
30
+ builder.request :url_encoded
31
+ builder.adapter Faraday.default_adapter
32
+ end
33
+ end
34
+
35
+ # Performs login operation using the configured username and password
36
+ # @return [Faraday::Response]
37
+ def login
38
+ request = {
39
+ 'data[User][username]' => @username,
40
+ 'data[User][password]' => @password,
41
+ }
42
+
43
+ post_with_csrf('/users/login', request)
44
+ end
45
+
46
+ # Finishes login operation using TOTP and TFA secret
47
+ # @return [Faraday::Response]
48
+ def pincode
49
+ request = {
50
+ 'data[User][pincode]' => @tfa.now
51
+ }
52
+
53
+ post_with_csrf('/users/pincode', request)
54
+ end
55
+
56
+ # Submits a trusted address for pre-approved withdrawals
57
+ #
58
+ # @param [Integer] currency_id
59
+ # @param [String] address
60
+ # @return [Faraday::Response]
61
+ def add_trusted_address(currency_id, address)
62
+ request = {
63
+ 'data[Withdrawal][currency_id]' => currency_id,
64
+ 'data[Withdrawal][address]' => address,
65
+ 'data[Withdrawal][existing_password]' => @password,
66
+ 'data[Withdrawal][pincode]' => @tfa.now,
67
+ }
68
+
69
+ post_with_csrf('/users/addtrustedaddress', request)
70
+ end
71
+
72
+ # Submits a request for withdrawal to an untrusted address
73
+ #
74
+ # @param [Integer] currency_id
75
+ # @param [String] address
76
+ # @param [Float] amount
77
+ # @return [Faraday::Response]
78
+ def make_withdrawal(currency_id, address, amount)
79
+ request = {
80
+ 'data[Withdrawal][fee]' => '1.00000000',
81
+ 'data[Withdrawal][wdamount]' => amount,
82
+ 'data[Withdrawal][address]' => address,
83
+ 'data[Withdrawal][approvedaddress]' => '',
84
+ 'data[Withdrawal][existing_password]' => @password,
85
+ 'data[Withdrawal][pincode]' => @tfa.now,
86
+ }
87
+
88
+ post_with_csrf("/users/makewithdrawal/#{currency_id}", request)
89
+ end
90
+
91
+ # @param [String] url
92
+ # @return [Faraday::Response]
93
+ def get(url)
94
+ @connection.get(url)
95
+ end
96
+
97
+ # @param [String] url
98
+ # @param [Hash] body
99
+ # @return [Faraday::Response]
100
+ def post(url, body)
101
+ @connection.post(url, body)
102
+ end
103
+
104
+ # Performs an initial GET request to the given URL to obtain any CSRF tokens,
105
+ # injects them into the given request, then performs a POST request to the given URL
106
+ #
107
+ # @param [String] url
108
+ # @param [Hash] request
109
+ # @return [Faraday::Request]
110
+ def post_with_csrf(url, request)
111
+ prepare_request(get(url), request)
112
+ post(url, request)
113
+ end
114
+
115
+ private
116
+
117
+ # @param [Faraday::Response] initial_response
118
+ # @param [Hash] new_request
119
+ # @return [void]
120
+ def prepare_request(initial_response, new_request)
121
+ doc = Nokogiri::HTML(initial_response.body)
122
+
123
+ # Inject CSRF token into new request
124
+ doc.xpath('//input').each do |input|
125
+ if input[:name] =~ /_Token/
126
+ new_request[input[:name]] = input[:value]
127
+ end
128
+ end
129
+
130
+ # Set the request method
131
+ new_request['_method'] = 'POST'
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cryptsy::Client do
4
+ before(:all) do
5
+ @public_key = ENV['CRYPTSY_PUBKEY']
6
+ @private_key = ENV['CRYPTSY_PRIVKEY']
7
+
8
+ unless @public_key && @private_key
9
+ fail 'API keys are required to run this spec'
10
+ end
11
+ end
12
+
13
+ subject { Cryptsy::Client.new(@public_key, @private_key) }
14
+
15
+ describe '#info' do
16
+ it 'displays current user info' do
17
+ result = subject.info
18
+ expect(result.balances_available).to be_a(Hash)
19
+ expect(result.servertimestamp).to be_an(Integer)
20
+ end
21
+ end
22
+
23
+ describe '#calculate_fees' do
24
+ it 'calculates the fee for a buy order' do
25
+ quantity = 1000
26
+ price = 10
27
+
28
+ result = subject.calculate_buy_fees(quantity, price)
29
+
30
+ expect(result.net.to_f).to eql((quantity * price) + result.fee.to_f)
31
+ end
32
+
33
+ it 'calculates the fee for a sell order' do
34
+ quantity = 1000
35
+ price = 10
36
+
37
+ result = subject.calculate_sell_fees(quantity, price)
38
+
39
+ expect(result.net.to_f).to eql((quantity * price) - result.fee.to_f)
40
+ end
41
+ end
42
+
43
+ describe '#generate_new_address' do
44
+ it 'raises an error when using an invalid currency' do
45
+ expect {
46
+ subject.generate_new_address('HERP')
47
+ }.to raise_error(Cryptsy::ClientError)
48
+ end
49
+
50
+ it 'returns a newly generated receive address' do
51
+ result = subject.generate_new_address('DOGE')
52
+ expect(result.size).to eql(34)
53
+ end
54
+ end
55
+
56
+ describe '#transactions' do
57
+ it 'returns all deposits and withdrawals' do
58
+ expect(subject.transactions).to be_an(Enumerable)
59
+ end
60
+ end
61
+
62
+ describe '#market_by_pair' do
63
+ it 'returns market by its currency code pair' do
64
+ expect(subject.market_by_pair('DOGE', 'BTC')).to be_a(Hash)
65
+ end
66
+
67
+ it 'returns nil for non-existant markets' do
68
+ expect(subject.market_by_pair('DOGE', 'HERP')).to be_nil
69
+ end
70
+ end
71
+ end
@@ -0,0 +1 @@
1
+ require 'cryptsy'
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+ require 'cryptsy/web_client'
3
+
4
+ describe Cryptsy::WebClient do
5
+ before(:all) do
6
+ @username = ENV['CRYPTSY_USERNAME']
7
+ @password = ENV['CRYPTSY_PASSWORD']
8
+ @tfa_secret = ENV['CRYPTSY_TFA_SECRET']
9
+
10
+ unless @username && @password && @tfa_secret
11
+ fail 'Cryptsy login details are required to run this spec'
12
+ end
13
+ end
14
+
15
+ subject { Cryptsy::WebClient.new(@username, @password, @tfa_secret) }
16
+
17
+ describe '#login' do
18
+ it 'logs into Cryptsy' do
19
+ response = subject.login
20
+ # Should respond with 302
21
+ # Location header should be /users/dashboard or /users/pincode
22
+ end
23
+
24
+ context 'with bad credentials' do
25
+ subject { Cryptsy::WebClient.new('herp', 'derp', 'lolz') }
26
+ it 'fails to login' do
27
+ response = subject.login
28
+ # Should respond with 200
29
+ # Look for div#flashMessages
30
+ end
31
+ end
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cryptsy
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Ian Unruh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hashie
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: API client for interacting with Cryptsy
42
+ email: ianunruh@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - LICENSE
48
+ - README.md
49
+ - lib/cryptsy.rb
50
+ - lib/cryptsy/client.rb
51
+ - lib/cryptsy/confirmation_poller.rb
52
+ - lib/cryptsy/errors.rb
53
+ - lib/cryptsy/version.rb
54
+ - lib/cryptsy/web_client.rb
55
+ - spec/client_spec.rb
56
+ - spec/spec_helper.rb
57
+ - spec/web_client_spec.rb
58
+ homepage: https://github.com/ianunruh/cryptsy
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.2.2
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: API client for interacting with Cryptsy
82
+ test_files:
83
+ - spec/client_spec.rb
84
+ - spec/spec_helper.rb
85
+ - spec/web_client_spec.rb
86
+ has_rdoc: