gate.rb 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97a150ad280b246d59dce0e7e011ae8faeaf9f3c743b726ae931a7353457e5b3
4
- data.tar.gz: a530909743923390245e633b04703c81fe9d78d10387118c959cea4d59a94e41
3
+ metadata.gz: af5b0afadf1a2aba9eba70064c5fc2503477d45569d27a5cfacfe0798d1ea3f0
4
+ data.tar.gz: 4c9cd963ac62156e5e0ae6c1b73c50c82c13cbeaacd15ed8c6ab657bcd5dfe50
5
5
  SHA512:
6
- metadata.gz: e05641f6589dd90c24b86779b8845309da739e3fc662712c43b9ac49c00abd0097a45b17f77378ef94d943566da09b03519792e5b9d965d9e7ac953bdc6bd252
7
- data.tar.gz: a40ba066ccc2aeeea27067601a0d91bd5f41ac5c5843c599efa58c69bd5b71bde598fec6979efdab6582d8ff634d1c02aa815d0de91050425ff90ef685e35cf4
6
+ metadata.gz: ced56131ac508a3cbb25b6cdb14f1e0382b7d91bf57bcecd452169158daee8f595dddabeb3ef820aaedb47362348a4613c69c76f2d5f9c4d69b47ac8a30488ca
7
+ data.tar.gz: 3aab85b7ba081c0de10770cf8123e22358bd84d66524f1dc3a7f156dd1589e1219ae2a296484710ac0df59ad60da0c94e32f64b0e232d4a3a243b5c180201289
data/gate.rb.gemspec CHANGED
@@ -4,7 +4,7 @@ Gem::Specification.new do |spec|
4
4
  spec.name = 'gate.rb'
5
5
 
6
6
  spec.version = Gate::VERSION
7
- spec.date = '2025-07-14'
7
+ spec.date = '2025-07-17'
8
8
 
9
9
  spec.summary = "Access the Gate API with Ruby."
10
10
  spec.description = "Access the Gate API with Ruby."
@@ -0,0 +1,45 @@
1
+ # Gate/Configuration.rb
2
+ # Gate::Configuration
3
+
4
+ module Gate
5
+ class << self
6
+ attr_writer :configuration
7
+
8
+ def configuration
9
+ @configuration ||= Configuration.new
10
+ end
11
+
12
+ def configure
13
+ yield(configuration)
14
+ end
15
+
16
+ def reset_configuration!
17
+ @configuration = Configuration.new
18
+ end
19
+ end # class << self
20
+
21
+ class Configuration
22
+ attr_accessor\
23
+ :api_key,
24
+ :api_secret,
25
+ :debug,
26
+
27
+ def configure
28
+ yield(self)
29
+ end
30
+
31
+ def reset!
32
+ @api_key = nil
33
+ @api_secret = nil
34
+ @debug = false
35
+ end
36
+
37
+ private
38
+
39
+ def initialize(api_key: nil, api_secret: nil, debug: nil)
40
+ @api_key = api_key
41
+ @api_secret = api_secret
42
+ @debug = debug
43
+ end
44
+ end
45
+ end
data/lib/Gate/Error.rb CHANGED
@@ -27,4 +27,5 @@ module Gate
27
27
  class InvalidRequestError < Error; end
28
28
  class NetworkError < Error; end
29
29
  class RateLimitError < Error; end
30
+ class InsufficientPermissionError < Error; end
30
31
  end
@@ -120,6 +120,13 @@ module Gate
120
120
  handle_response(response)
121
121
  end
122
122
 
123
+ # Private endpoints
124
+
125
+ def wallet_total_balance(currency: nil)
126
+ response = get(path: '/wallet/total_balance', args: {currency: currency})
127
+ handle_response(response)
128
+ end
129
+
123
130
  def spot_orders(
124
131
  text: nil,
125
132
  currency_pair:,
@@ -149,20 +156,63 @@ module Gate
149
156
  handle_response(response)
150
157
  end
151
158
 
159
+ def spot_my_trades(
160
+ currency_pair: nil,
161
+ limit: nil,
162
+ page: nil,
163
+ order_id: nil,
164
+ account: nil,
165
+ from: nil,
166
+ to: nil
167
+ )
168
+ args = {
169
+ currency_pair: currency_pair,
170
+ limit: limit,
171
+ page: page,
172
+ order_id: order_id,
173
+ account: account,
174
+ from: from,
175
+ to: to
176
+ }.reject{|k,v| v.nil?}.stringify_values
177
+ response = get(
178
+ path: '/spot/my_trades',
179
+ args: args
180
+ )
181
+ handle_response(response)
182
+ end
183
+
152
184
  private
153
185
 
154
- def initialize(api_key:, api_secret:)
155
- @api_key = api_key.encode('UTF-8')
156
- @api_secret = api_secret.encode('UTF-8')
186
+ def initialize(api_key: nil, api_secret: nil, configuration: nil)
187
+ @api_key = (api_key || configuration&.api_key || Gate.configuration.api_key).encode('UTF-8')
188
+ @api_secret = (api_secret || configuration&.api_secret || Gate.configuration.api_secret).encode('UTF-8')
157
189
  end
158
190
 
159
191
  def full_path(path)
160
192
  self.class.path_prefix + path
161
193
  end
162
194
 
163
- def encoded_payload(args)
164
- args.reject!{|k,v| v.nil?}
165
- OpenSSL::Digest::SHA512.hexdigest(JSON.dump(args))
195
+ def query_string(verb:, args:)
196
+ case verb
197
+ when 'GET'
198
+ args.x_www_form_urlencode
199
+ when 'POST'
200
+ nil
201
+ else
202
+ raise "The verb, #{verb}, is not acceptable."
203
+ end
204
+ end
205
+
206
+ def encoded_payload(verb:, args:)
207
+ case verb
208
+ when 'GET'
209
+ OpenSSL::Digest::SHA512.hexdigest('')
210
+ when 'POST'
211
+ args.reject!{|k,v| v.nil?}
212
+ OpenSSL::Digest::SHA512.hexdigest(JSON.dump(args))
213
+ else
214
+ raise "The verb, #{verb}, is not acceptable."
215
+ end
166
216
  end
167
217
 
168
218
  def timestamp
@@ -170,17 +220,13 @@ module Gate
170
220
  end
171
221
 
172
222
  def message(verb:, path:, args:)
173
- query_string = (
174
- case verb
175
- when 'GET'
176
- args.x_www_form_urlencode
177
- when 'POST'
178
- nil
179
- else
180
- raise "The verb, #{verb}, is not acceptable."
181
- end
182
- )
183
- [verb, full_path(path), query_string, encoded_payload(args), timestamp].join("\n")
223
+ [
224
+ verb,
225
+ full_path(path),
226
+ query_string(verb: verb, args: args),
227
+ encoded_payload(verb: verb, args: args),
228
+ timestamp
229
+ ].join("\n")
184
230
  end
185
231
 
186
232
  def signature(message)
@@ -234,6 +280,12 @@ module Gate
234
280
  message: response.message,
235
281
  body: response.body
236
282
  )
283
+ when 403
284
+ raise Gate::InsufficientPermissionError.new(
285
+ code: response.code,
286
+ message: response.message,
287
+ body: response.body
288
+ )
237
289
  when 429
238
290
  raise Gate::RateLimitError.new(
239
291
  code: response.code,
data/lib/Gate/VERSION.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # Gate::VERSION
3
3
 
4
4
  module Gate
5
- VERSION = '0.1.0'
5
+ VERSION = '0.2.0'
6
6
  end
data/lib/gate.rb CHANGED
@@ -6,4 +6,5 @@ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
6
6
  module Gate; end
7
7
 
8
8
  require 'Gate/Client'
9
+ require 'Gate/Configuration'
9
10
  require 'Gate/VERSION'
@@ -0,0 +1,13 @@
1
+ require_relative '../helper'
2
+
3
+ describe Gate::Configuration do
4
+ describe "#initialize" do
5
+ it "sets default values" do
6
+ config = Gate::Configuration.new
7
+ _(config.api_base_url).to eq('https://api.gateio.ws')
8
+ _(config.debug).to eq(false)
9
+ _(config.api_key).must_equal(nil)
10
+ _(config.api_secret).must_equal(nil)
11
+ end
12
+ end
13
+ end
@@ -11,6 +11,57 @@ describe Gate::V4::Client do
11
11
  )
12
12
  end
13
13
 
14
+ describe "#wallet_total_balance" do
15
+ let(:amount){0.0001}
16
+ let(:price){100_000}
17
+ let(:side){'buy'}
18
+
19
+ context "success" do
20
+ it "fetches my wallet balance for a specific market" do
21
+ VCR.use_cassette('v4/wallet/total_balance') do
22
+ response = subject.wallet_total_balance(currency: 'BTC')
23
+ _(response).must_be_kind_of(Hash)
24
+ assert(response['details'])
25
+ assert(response['details']['delivery'])
26
+ _(response['details']['delivery']['currency']).must_equal('BTC')
27
+ _(response['details']['delivery']['amount']).must_equal('0')
28
+ assert(response['details']['finance'])
29
+ _(response['details']['finance']['currency']).must_equal('BTC')
30
+ _(response['details']['finance']['amount']).must_equal('0')
31
+ assert(response['details']['futures'])
32
+ _(response['details']['futures']['currency']).must_equal('BTC')
33
+ _(response['details']['futures']['amount']).must_equal('0')
34
+ end
35
+ end
36
+ end
37
+
38
+ context "invalid key" do
39
+ let(:api_key){'invalid_key'}
40
+ let(:api_secret){'invalid_secret'}
41
+
42
+ it "raises an error" do
43
+ VCR.use_cassette('v4/wallet/total_balance_with_an_invalid_key') do
44
+ assert_raises do
45
+ subject.wallet_total_balance(currency: 'BTC')
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ context "insufficient permission" do
52
+ let(:api_key){'key_with_insufficient_permission'}
53
+ let(:api_secret){'secret_with_insufficient_permission'}
54
+
55
+ it "raises an error" do
56
+ VCR.use_cassette('v4/wallet/total_balance_with_insufficient_permission') do
57
+ assert_raises do
58
+ subject.spot_orders(currency: 'BTC')
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
14
65
  describe "#spot_orders" do
15
66
  let(:amount){0.0001}
16
67
  let(:price){100_000}
@@ -31,7 +82,10 @@ describe Gate::V4::Client do
31
82
  end
32
83
 
33
84
  context "invalid key" do
34
- it "fetches my orders for a specific market" do
85
+ let(:api_key){'invalid_key'}
86
+ let(:api_secret){'invalid_secret'}
87
+
88
+ it "raises an error" do
35
89
  VCR.use_cassette('v4/spot/orders_with_an_invalid_key') do
36
90
  assert_raises do
37
91
  subject.spot_orders(currency_pair: 'BTC_USDT', side: side, amount: amount, time_in_force: 'gtc', price: price)
@@ -39,5 +93,49 @@ describe Gate::V4::Client do
39
93
  end
40
94
  end
41
95
  end
96
+
97
+ context "insufficient permission" do
98
+ let(:api_key){'key_with_insufficient_permission'}
99
+ let(:api_secret){'secret_with_insufficient_permission'}
100
+
101
+ it "raises an error" do
102
+ VCR.use_cassette('v4/spot/orders_with_insufficient_permission') do
103
+ assert_raises do
104
+ subject.spot_orders(currency_pair: 'BTC_USDT', side: side, amount: amount, time_in_force: 'gtc', price: price)
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "#spot_my_trades" do
112
+ context "success" do
113
+ it "fetches my trading history" do
114
+ VCR.use_cassette('v4/spot/my_trades') do
115
+ response = subject.spot_my_trades(currency_pair: 'BTC5S_USDT')
116
+ _(response).must_be_kind_of(Array)
117
+ _(response.first).must_be_kind_of(Hash)
118
+ assert(response.first['id'])
119
+ assert(response.first['create_time'])
120
+ assert(response.first['currency_pair'])
121
+ assert(response.first['amount'])
122
+ assert(response.first['price'])
123
+ assert(['buy', 'sell'].include?(response.first['side']))
124
+ end
125
+ end
126
+ end
127
+
128
+ context "invalid key" do
129
+ let(:api_key){'invalid_key'}
130
+ let(:api_secret){'invalid_secret'}
131
+
132
+ it "raises an error" do
133
+ VCR.use_cassette('v4/spot/my_trades_with_an_invalid_key') do
134
+ assert_raises do
135
+ subject.spot_my_trades(currency_pair: 'BTC_USDT')
136
+ end
137
+ end
138
+ end
139
+ end
42
140
  end
43
141
  end
data/test/helper.rb CHANGED
@@ -9,18 +9,17 @@ require 'gate'
9
9
 
10
10
  VCR.configure do |config|
11
11
  config.cassette_library_dir = 'test/fixtures/vcr_cassettes'
12
-
13
12
  config.hook_into :webmock
14
13
 
15
14
  config.filter_sensitive_data('<API_KEY>'){ENV['GATE_API_KEY']}
16
- config.filter_sensitive_data('<API_SECRET>'){ENV['GATE_API_SECRET'] }
15
+ config.filter_sensitive_data('<API_SECRET>'){ENV['GATE_API_SECRET']}
17
16
 
18
17
  # Allow localhost connections for debugging
19
18
  config.ignore_localhost = true
20
19
 
21
20
  config.default_cassette_options = {
22
21
  record: :new_episodes,
23
- match_requests_on: [:method, :uri]
22
+ match_requests_on: [:method, :uri, :body]
24
23
  }
25
24
  end
26
25
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gate.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - thoran
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-07-14 00:00:00.000000000 Z
10
+ date: 2025-07-17 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: http.rb
@@ -33,6 +33,7 @@ files:
33
33
  - README.md
34
34
  - gate.rb.gemspec
35
35
  - lib/Gate/Client.rb
36
+ - lib/Gate/Configuration.rb
36
37
  - lib/Gate/Error.rb
37
38
  - lib/Gate/V4/Client.rb
38
39
  - lib/Gate/VERSION.rb
@@ -41,6 +42,7 @@ files:
41
42
  - lib/Thoran/Hash/XWwwFormUrlencode/x_www_form_urlencode.rb
42
43
  - lib/Thoran/String/UrlEncode/url_encode.rb
43
44
  - lib/gate.rb
45
+ - test/Gate/Configuration_test.rb
44
46
  - test/Gate/V4/Client/test_private_endpoints.rb
45
47
  - test/Gate/V4/Client/test_public_endpoints.rb
46
48
  - test/helper.rb