coindcx-client 0.1.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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +55 -0
  3. data/.github/workflows/release.yml +138 -0
  4. data/.rubocop.yml +56 -0
  5. data/AGENT.md +352 -0
  6. data/README.md +224 -0
  7. data/bin/console +59 -0
  8. data/docs/README.md +29 -0
  9. data/docs/coindcx_docs_gaps.md +3 -0
  10. data/docs/core.md +179 -0
  11. data/docs/rails_integration.md +151 -0
  12. data/docs/standalone_bot.md +159 -0
  13. data/lib/coindcx/auth/signer.rb +48 -0
  14. data/lib/coindcx/client.rb +44 -0
  15. data/lib/coindcx/configuration.rb +108 -0
  16. data/lib/coindcx/contracts/channel_name.rb +23 -0
  17. data/lib/coindcx/contracts/identifiers.rb +36 -0
  18. data/lib/coindcx/contracts/order_request.rb +120 -0
  19. data/lib/coindcx/contracts/socket_backend.rb +19 -0
  20. data/lib/coindcx/contracts/wallet_transfer_request.rb +46 -0
  21. data/lib/coindcx/errors/base_error.rb +54 -0
  22. data/lib/coindcx/logging/null_logger.rb +12 -0
  23. data/lib/coindcx/logging/structured_logger.rb +17 -0
  24. data/lib/coindcx/models/balance.rb +8 -0
  25. data/lib/coindcx/models/base_model.rb +31 -0
  26. data/lib/coindcx/models/instrument.rb +8 -0
  27. data/lib/coindcx/models/market.rb +8 -0
  28. data/lib/coindcx/models/order.rb +8 -0
  29. data/lib/coindcx/models/trade.rb +8 -0
  30. data/lib/coindcx/rest/base_resource.rb +35 -0
  31. data/lib/coindcx/rest/funding/facade.rb +18 -0
  32. data/lib/coindcx/rest/funding/orders.rb +46 -0
  33. data/lib/coindcx/rest/futures/facade.rb +29 -0
  34. data/lib/coindcx/rest/futures/market_data.rb +71 -0
  35. data/lib/coindcx/rest/futures/orders.rb +47 -0
  36. data/lib/coindcx/rest/futures/positions.rb +93 -0
  37. data/lib/coindcx/rest/futures/wallets.rb +44 -0
  38. data/lib/coindcx/rest/margin/facade.rb +17 -0
  39. data/lib/coindcx/rest/margin/orders.rb +57 -0
  40. data/lib/coindcx/rest/public/facade.rb +17 -0
  41. data/lib/coindcx/rest/public/market_data.rb +52 -0
  42. data/lib/coindcx/rest/spot/facade.rb +17 -0
  43. data/lib/coindcx/rest/spot/orders.rb +67 -0
  44. data/lib/coindcx/rest/transfers/facade.rb +17 -0
  45. data/lib/coindcx/rest/transfers/wallets.rb +40 -0
  46. data/lib/coindcx/rest/user/accounts.rb +17 -0
  47. data/lib/coindcx/rest/user/facade.rb +17 -0
  48. data/lib/coindcx/transport/circuit_breaker.rb +65 -0
  49. data/lib/coindcx/transport/http_client.rb +290 -0
  50. data/lib/coindcx/transport/rate_limit_registry.rb +65 -0
  51. data/lib/coindcx/transport/request_policy.rb +152 -0
  52. data/lib/coindcx/transport/response_normalizer.rb +40 -0
  53. data/lib/coindcx/transport/retry_policy.rb +79 -0
  54. data/lib/coindcx/utils/payload.rb +51 -0
  55. data/lib/coindcx/version.rb +5 -0
  56. data/lib/coindcx/ws/connection_manager.rb +423 -0
  57. data/lib/coindcx/ws/connection_state.rb +75 -0
  58. data/lib/coindcx/ws/parsers/order_book_snapshot.rb +42 -0
  59. data/lib/coindcx/ws/private_channels.rb +38 -0
  60. data/lib/coindcx/ws/public_channels.rb +92 -0
  61. data/lib/coindcx/ws/socket_io_client.rb +89 -0
  62. data/lib/coindcx/ws/socket_io_simple_backend.rb +63 -0
  63. data/lib/coindcx/ws/subscription_registry.rb +80 -0
  64. data/lib/coindcx/ws/uri_ruby3_compat.rb +13 -0
  65. data/lib/coindcx.rb +63 -0
  66. data/spec/auth_signer_spec.rb +22 -0
  67. data/spec/client_spec.rb +19 -0
  68. data/spec/contracts/order_request_spec.rb +136 -0
  69. data/spec/contracts/wallet_transfer_request_spec.rb +45 -0
  70. data/spec/models/base_model_spec.rb +18 -0
  71. data/spec/rest/funding/orders_spec.rb +43 -0
  72. data/spec/rest/futures/market_data_spec.rb +49 -0
  73. data/spec/rest/futures/orders_spec.rb +107 -0
  74. data/spec/rest/futures/positions_spec.rb +57 -0
  75. data/spec/rest/futures/wallets_spec.rb +44 -0
  76. data/spec/rest/margin/orders_spec.rb +87 -0
  77. data/spec/rest/public/market_data_spec.rb +31 -0
  78. data/spec/rest/spot/orders_spec.rb +152 -0
  79. data/spec/rest/transfers/wallets_spec.rb +33 -0
  80. data/spec/rest/user/accounts_spec.rb +21 -0
  81. data/spec/spec_helper.rb +11 -0
  82. data/spec/transport/http_client_spec.rb +232 -0
  83. data/spec/transport/rate_limit_registry_spec.rb +28 -0
  84. data/spec/transport/request_policy_spec.rb +67 -0
  85. data/spec/transport/response_normalizer_spec.rb +63 -0
  86. data/spec/ws/connection_manager_spec.rb +339 -0
  87. data/spec/ws/order_book_snapshot_spec.rb +25 -0
  88. data/spec/ws/private_channels_spec.rb +28 -0
  89. data/spec/ws/public_channels_spec.rb +89 -0
  90. data/spec/ws/socket_io_client_spec.rb +229 -0
  91. data/spec/ws/socket_io_simple_backend_spec.rb +41 -0
  92. data/spec/ws/uri_ruby3_compat_spec.rb +12 -0
  93. metadata +164 -0
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe CoinDCX::WS::SocketIOClient do
6
+ let(:configuration) do
7
+ CoinDCX::Configuration.new.tap do |config|
8
+ config.api_key = "api-key"
9
+ config.api_secret = "api-secret"
10
+ config.socket_reconnect_attempts = 2
11
+ config.socket_reconnect_interval = 0.01
12
+ config.socket_heartbeat_interval = 0.5
13
+ config.socket_liveness_timeout = 1.0
14
+ end
15
+ end
16
+
17
+ let(:backend) { instance_double("SocketBackend") }
18
+ let(:sleeper) { class_double(Kernel, sleep: nil) }
19
+ # Stubbed sleeper.sleep returns immediately; a real heartbeat Thread would tight-loop on the GVL and
20
+ # starve examples. Capture the loop body; drive liveness via ConnectionManager#check_liveness! in specs.
21
+ let(:captured_heartbeat_blocks) { [] }
22
+ let(:thread_factory_without_run) do
23
+ lambda do |&block|
24
+ captured_heartbeat_blocks << block
25
+ instance_double(Thread, join: nil, kill: nil)
26
+ end
27
+ end
28
+
29
+ before do
30
+ allow(backend).to receive(:connect)
31
+ allow(backend).to receive(:start_transport!)
32
+ allow(backend).to receive(:on)
33
+ allow(backend).to receive(:emit)
34
+ allow(backend).to receive(:disconnect)
35
+ end
36
+
37
+ describe "#leave_channel" do
38
+ it "emits leave with a validated channelName" do
39
+ client = described_class.new(
40
+ configuration: configuration,
41
+ backend: backend,
42
+ sleeper: sleeper,
43
+ thread_factory: thread_factory_without_run
44
+ )
45
+ client.connect
46
+ client.leave_channel(channel_name: "B-BTC_USDT@prices")
47
+
48
+ expect(backend).to have_received(:emit).with(
49
+ "leave",
50
+ { "channelName" => "B-BTC_USDT@prices" }
51
+ )
52
+ end
53
+ end
54
+
55
+ describe "#subscribe_public" do
56
+ it "emits join after Engine.IO open (:connect), and resubscribes on each open" do
57
+ handlers = {}
58
+ allow(backend).to receive(:on) do |event_name, &block|
59
+ handlers[event_name] = block
60
+ end
61
+
62
+ client = described_class.new(
63
+ configuration: configuration,
64
+ backend: backend,
65
+ sleeper: sleeper,
66
+ thread_factory: thread_factory_without_run
67
+ )
68
+ client.connect
69
+ client.subscribe_public(channel_name: "B-BTC_USDT@prices", event_name: "price-change")
70
+
71
+ expect(backend).not_to have_received(:emit).with("join", { "channelName" => "B-BTC_USDT@prices" })
72
+
73
+ handlers.fetch(:connect).call
74
+
75
+ expect(backend).to have_received(:emit).with("join", { "channelName" => "B-BTC_USDT@prices" }).once
76
+
77
+ handlers.fetch(:connect).call
78
+
79
+ expect(backend).to have_received(:emit).with("join", { "channelName" => "B-BTC_USDT@prices" }).twice
80
+ end
81
+ end
82
+
83
+ describe "#subscribe_private" do
84
+ it "renews private channel auth payloads after reconnect" do
85
+ handlers = {}
86
+ captured_join_payloads = []
87
+ allow(backend).to receive(:on) do |event_name, &block|
88
+ handlers[event_name] = block
89
+ end
90
+ allow(backend).to receive(:emit) do |op, payload|
91
+ captured_join_payloads << payload if op == "join"
92
+ end
93
+
94
+ client = described_class.new(
95
+ configuration: configuration,
96
+ backend: backend,
97
+ sleeper: sleeper,
98
+ thread_factory: thread_factory_without_run
99
+ )
100
+ client.connect
101
+ handlers.fetch(:connect).call
102
+ client.subscribe_private(event_name: CoinDCX::WS::PrivateChannels::ORDER_UPDATE_EVENT)
103
+
104
+ expect(captured_join_payloads.size).to eq(1)
105
+ first_payload = captured_join_payloads.first
106
+
107
+ allow(client).to receive(:join_payload).and_call_original
108
+ allow(client).to receive(:join_payload)
109
+ .with(type: :private, channel_name: CoinDCX::WS::PrivateChannels::DEFAULT_CHANNEL_NAME)
110
+ .and_wrap_original do |original, *args, **kwargs|
111
+ payload = original.call(*args, **kwargs)
112
+ payload.merge("authSignature" => "#{payload.fetch('authSignature')}-renewed")
113
+ end
114
+
115
+ handlers.fetch(:disconnect).call("network_lost")
116
+ handlers[:connect]&.call
117
+
118
+ second_payload = captured_join_payloads.last
119
+
120
+ expect(second_payload).not_to eq(first_payload)
121
+ expect(second_payload.fetch("authSignature")).to end_with("-renewed")
122
+ end
123
+
124
+ it "does not reconnect a private subscription still within the liveness window" do
125
+ now = 100.0
126
+ handlers = {}
127
+ clock = -> { now }
128
+
129
+ allow(backend).to receive(:on) do |event_name, &block|
130
+ handlers[event_name] = block
131
+ end
132
+
133
+ client = described_class.new(
134
+ configuration: configuration,
135
+ backend: backend,
136
+ sleeper: sleeper,
137
+ thread_factory: thread_factory_without_run,
138
+ monotonic_clock: clock
139
+ )
140
+ client.connect
141
+ handlers.fetch(:connect).call
142
+ client.subscribe_private(event_name: CoinDCX::WS::PrivateChannels::ORDER_UPDATE_EVENT)
143
+
144
+ # Advance clock but stay inside the liveness timeout — no reconnect expected.
145
+ now += 0.5
146
+ client.send(:connection_manager).send(:check_liveness!)
147
+
148
+ expect(backend).to have_received(:connect).once
149
+ expect(client.alive?).to be(true)
150
+ end
151
+ end
152
+
153
+ describe "#connect" do
154
+ it "retries socket connection failures with backoff" do
155
+ allow(backend).to receive(:connect) do
156
+ @connect_attempts ||= 0
157
+ @connect_attempts += 1
158
+ raise CoinDCX::Errors::SocketConnectionError, "temporary failure" if @connect_attempts == 1
159
+
160
+ self
161
+ end
162
+
163
+ client = described_class.new(
164
+ configuration: configuration,
165
+ backend: backend,
166
+ sleeper: sleeper,
167
+ thread_factory: thread_factory_without_run,
168
+ randomizer: -> { 0.0 } # zero jitter → deterministic sleep value
169
+ )
170
+ client.connect
171
+
172
+ expect(backend).to have_received(:connect).twice
173
+ expect(sleeper).to have_received(:sleep).with(0.01)
174
+ end
175
+
176
+ it "reconnects and resubscribes after a disconnect event" do
177
+ handlers = {}
178
+ allow(backend).to receive(:on) do |event_name, &block|
179
+ handlers[event_name] = block
180
+ end
181
+
182
+ client = described_class.new(
183
+ configuration: configuration,
184
+ backend: backend,
185
+ sleeper: sleeper,
186
+ thread_factory: thread_factory_without_run
187
+ )
188
+ client.connect
189
+ handlers.fetch(:connect).call
190
+ client.subscribe_public(channel_name: "B-BTC_USDT@prices", event_name: "price-change")
191
+
192
+ handlers.fetch(:disconnect).call("network_lost")
193
+ handlers[:connect]&.call
194
+
195
+ expect(backend).to have_received(:connect).twice
196
+ expect(backend).to have_received(:emit).with("join", { "channelName" => "B-BTC_USDT@prices" }).at_least(:twice)
197
+ end
198
+ end
199
+
200
+ describe "heartbeat liveness" do
201
+ it "reconnects after a stale subscription" do
202
+ now = 100.0
203
+ handlers = {}
204
+ clock = -> { now }
205
+
206
+ allow(backend).to receive(:on) do |event_name, &block|
207
+ handlers[event_name] = block
208
+ end
209
+
210
+ client = described_class.new(
211
+ configuration: configuration,
212
+ backend: backend,
213
+ sleeper: sleeper,
214
+ thread_factory: thread_factory_without_run,
215
+ monotonic_clock: clock
216
+ )
217
+ client.connect
218
+ handlers.fetch(:connect).call
219
+ client.subscribe_public(channel_name: "B-BTC_USDT@prices", event_name: "price-change")
220
+
221
+ now += 2.0
222
+ client.send(:connection_manager).send(:check_liveness!)
223
+ handlers[:connect]&.call
224
+
225
+ expect(backend).to have_received(:connect).twice
226
+ expect(backend).to have_received(:emit).with("join", { "channelName" => "B-BTC_USDT@prices" }).at_least(:twice)
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe CoinDCX::WS::SocketIOSimpleBackend do
6
+ let(:client_instance) { instance_double("SocketIOClientInstance", connect: true, emit: true, on: true, disconnect: true) }
7
+ let(:client_class) { class_double("SocketIOClientClass", new: client_instance) }
8
+ subject(:backend) { described_class.new(socket_client_class: client_class, connect_options: { EIO: 3 }) }
9
+
10
+ before do
11
+ allow(client_instance).to receive(:on)
12
+ end
13
+
14
+ it "creates the client, registers listeners, then opens the transport" do
15
+ backend.connect("wss://stream.coindcx.com")
16
+ backend.on("price-change") { |_payload| nil }
17
+ backend.start_transport!
18
+ backend.emit("join", { "channelName" => "coindcx" })
19
+
20
+ expect(client_class).to have_received(:new).with("wss://stream.coindcx.com", hash_including(EIO: 3))
21
+ expect(client_instance).to have_received(:on).with("price-change")
22
+ expect(client_instance).to have_received(:connect)
23
+ expect(client_instance).to have_received(:emit).with("join", { "channelName" => "coindcx" })
24
+ end
25
+
26
+ it "disconnects the underlying client when present" do
27
+ backend.connect("wss://stream.coindcx.com")
28
+ backend.start_transport!
29
+ backend.disconnect
30
+
31
+ expect(client_instance).to have_received(:disconnect)
32
+ end
33
+
34
+ it "raises a socket connection error when client construction fails" do
35
+ allow(client_class).to receive(:new).and_raise(StandardError, "boom")
36
+
37
+ expect do
38
+ backend.connect("wss://stream.coindcx.com")
39
+ end.to raise_error(CoinDCX::Errors::SocketConnectionError, "boom")
40
+ end
41
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require_relative "../../lib/coindcx/ws/uri_ruby3_compat"
5
+
6
+ RSpec.describe "CoinDCX::WS URI.encode shim (Ruby 3 + socket.io-client-simple)" do
7
+ it "restores URI.encode using DEFAULT_PARSER so Engine.IO query params build" do
8
+ # rubocop:disable Lint/UriEscapeUnescape -- intentional: shim matches legacy URI.encode for socket.io-client-simple
9
+ expect(URI.encode("EIO=4")).to eq(URI::DEFAULT_PARSER.escape("EIO=4"))
10
+ # rubocop:enable Lint/UriEscapeUnescape
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: coindcx-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Cursor
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-04-12 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: '2.14'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: socket.io-client-simple
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.2'
41
+ description: CoinDCX-specific gem with separate REST and Socket.io layers, modeled
42
+ after layered exchange client architecture.
43
+ email:
44
+ - noreply@example.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".github/workflows/ci.yml"
50
+ - ".github/workflows/release.yml"
51
+ - ".rubocop.yml"
52
+ - AGENT.md
53
+ - README.md
54
+ - bin/console
55
+ - docs/README.md
56
+ - docs/coindcx_docs_gaps.md
57
+ - docs/core.md
58
+ - docs/rails_integration.md
59
+ - docs/standalone_bot.md
60
+ - lib/coindcx.rb
61
+ - lib/coindcx/auth/signer.rb
62
+ - lib/coindcx/client.rb
63
+ - lib/coindcx/configuration.rb
64
+ - lib/coindcx/contracts/channel_name.rb
65
+ - lib/coindcx/contracts/identifiers.rb
66
+ - lib/coindcx/contracts/order_request.rb
67
+ - lib/coindcx/contracts/socket_backend.rb
68
+ - lib/coindcx/contracts/wallet_transfer_request.rb
69
+ - lib/coindcx/errors/base_error.rb
70
+ - lib/coindcx/logging/null_logger.rb
71
+ - lib/coindcx/logging/structured_logger.rb
72
+ - lib/coindcx/models/balance.rb
73
+ - lib/coindcx/models/base_model.rb
74
+ - lib/coindcx/models/instrument.rb
75
+ - lib/coindcx/models/market.rb
76
+ - lib/coindcx/models/order.rb
77
+ - lib/coindcx/models/trade.rb
78
+ - lib/coindcx/rest/base_resource.rb
79
+ - lib/coindcx/rest/funding/facade.rb
80
+ - lib/coindcx/rest/funding/orders.rb
81
+ - lib/coindcx/rest/futures/facade.rb
82
+ - lib/coindcx/rest/futures/market_data.rb
83
+ - lib/coindcx/rest/futures/orders.rb
84
+ - lib/coindcx/rest/futures/positions.rb
85
+ - lib/coindcx/rest/futures/wallets.rb
86
+ - lib/coindcx/rest/margin/facade.rb
87
+ - lib/coindcx/rest/margin/orders.rb
88
+ - lib/coindcx/rest/public/facade.rb
89
+ - lib/coindcx/rest/public/market_data.rb
90
+ - lib/coindcx/rest/spot/facade.rb
91
+ - lib/coindcx/rest/spot/orders.rb
92
+ - lib/coindcx/rest/transfers/facade.rb
93
+ - lib/coindcx/rest/transfers/wallets.rb
94
+ - lib/coindcx/rest/user/accounts.rb
95
+ - lib/coindcx/rest/user/facade.rb
96
+ - lib/coindcx/transport/circuit_breaker.rb
97
+ - lib/coindcx/transport/http_client.rb
98
+ - lib/coindcx/transport/rate_limit_registry.rb
99
+ - lib/coindcx/transport/request_policy.rb
100
+ - lib/coindcx/transport/response_normalizer.rb
101
+ - lib/coindcx/transport/retry_policy.rb
102
+ - lib/coindcx/utils/payload.rb
103
+ - lib/coindcx/version.rb
104
+ - lib/coindcx/ws/connection_manager.rb
105
+ - lib/coindcx/ws/connection_state.rb
106
+ - lib/coindcx/ws/parsers/order_book_snapshot.rb
107
+ - lib/coindcx/ws/private_channels.rb
108
+ - lib/coindcx/ws/public_channels.rb
109
+ - lib/coindcx/ws/socket_io_client.rb
110
+ - lib/coindcx/ws/socket_io_simple_backend.rb
111
+ - lib/coindcx/ws/subscription_registry.rb
112
+ - lib/coindcx/ws/uri_ruby3_compat.rb
113
+ - spec/auth_signer_spec.rb
114
+ - spec/client_spec.rb
115
+ - spec/contracts/order_request_spec.rb
116
+ - spec/contracts/wallet_transfer_request_spec.rb
117
+ - spec/models/base_model_spec.rb
118
+ - spec/rest/funding/orders_spec.rb
119
+ - spec/rest/futures/market_data_spec.rb
120
+ - spec/rest/futures/orders_spec.rb
121
+ - spec/rest/futures/positions_spec.rb
122
+ - spec/rest/futures/wallets_spec.rb
123
+ - spec/rest/margin/orders_spec.rb
124
+ - spec/rest/public/market_data_spec.rb
125
+ - spec/rest/spot/orders_spec.rb
126
+ - spec/rest/transfers/wallets_spec.rb
127
+ - spec/rest/user/accounts_spec.rb
128
+ - spec/spec_helper.rb
129
+ - spec/transport/http_client_spec.rb
130
+ - spec/transport/rate_limit_registry_spec.rb
131
+ - spec/transport/request_policy_spec.rb
132
+ - spec/transport/response_normalizer_spec.rb
133
+ - spec/ws/connection_manager_spec.rb
134
+ - spec/ws/order_book_snapshot_spec.rb
135
+ - spec/ws/private_channels_spec.rb
136
+ - spec/ws/public_channels_spec.rb
137
+ - spec/ws/socket_io_client_spec.rb
138
+ - spec/ws/socket_io_simple_backend_spec.rb
139
+ - spec/ws/uri_ruby3_compat_spec.rb
140
+ homepage: https://github.com/shubhamtaywade82/coindcx-client
141
+ licenses:
142
+ - MIT
143
+ metadata:
144
+ rubygems_mfa_required: 'true'
145
+ post_install_message:
146
+ rdoc_options: []
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '3.2'
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ requirements: []
160
+ rubygems_version: 3.5.22
161
+ signing_key:
162
+ specification_version: 4
163
+ summary: Ruby client for CoinDCX REST and Socket.io APIs
164
+ test_files: []