hyperliquid 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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +16 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +200 -0
- data/Rakefile +12 -0
- data/example.rb +58 -0
- data/lib/hyperliquid/client.rb +121 -0
- data/lib/hyperliquid/constants.rb +17 -0
- data/lib/hyperliquid/errors.rb +38 -0
- data/lib/hyperliquid/info.rb +82 -0
- data/lib/hyperliquid/version.rb +5 -0
- data/lib/hyperliquid.rb +47 -0
- data/sig/hyperliquid.rbs +4 -0
- metadata +89 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a800dda349fdca23a3e13c4b49020a3c17d506934f7f80b9859d760a95fb1a23
|
|
4
|
+
data.tar.gz: 518ae3c035ba3018ccb27d80552dd2f08b7b64d126b58f9828ce2db7d84052f6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 64ecd6d0060040e6f98143535c05ed0d4a3035e72fa9e0eba0faf4865b14496172463eecd1d57f664c9e5c9df35f0517cfd53b40d3bd34d1325625e1526c64e4
|
|
7
|
+
data.tar.gz: 1700985bc702806ea068c180327ae6ee5fc2ac0d8d5c801e85a61dc044a30c298a3130c840d44b9a132dddbc55c00262ff2137635a03b2139279b0ed0522890a
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: enable
|
|
3
|
+
TargetRubyVersion: 3.4.3
|
|
4
|
+
|
|
5
|
+
# Allow longer methods for complex logic
|
|
6
|
+
Metrics/MethodLength:
|
|
7
|
+
Max: 50
|
|
8
|
+
|
|
9
|
+
# Allow higher complexity for response handling
|
|
10
|
+
Metrics/AbcSize:
|
|
11
|
+
Max: 25
|
|
12
|
+
|
|
13
|
+
# Allow longer blocks in specs - this is normal for tests
|
|
14
|
+
Metrics/BlockLength:
|
|
15
|
+
Exclude:
|
|
16
|
+
- 'spec/**/*'
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.4.3
|
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
+
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
10
|
+
identity and orientation.
|
|
11
|
+
|
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
+
diverse, inclusive, and healthy community.
|
|
14
|
+
|
|
15
|
+
## Our Standards
|
|
16
|
+
|
|
17
|
+
Examples of behavior that contributes to a positive environment for our
|
|
18
|
+
community include:
|
|
19
|
+
|
|
20
|
+
* Demonstrating empathy and kindness toward other people
|
|
21
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
+
* Giving and gracefully accepting constructive feedback
|
|
23
|
+
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
+
and learning from the experience
|
|
25
|
+
* Focusing on what is best not just for us as individuals, but for the overall
|
|
26
|
+
community
|
|
27
|
+
|
|
28
|
+
Examples of unacceptable behavior include:
|
|
29
|
+
|
|
30
|
+
* The use of sexualized language or imagery, and sexual attention or advances of
|
|
31
|
+
any kind
|
|
32
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
33
|
+
* Public or private harassment
|
|
34
|
+
* Publishing others' private information, such as a physical or email address,
|
|
35
|
+
without their explicit permission
|
|
36
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
|
37
|
+
professional setting
|
|
38
|
+
|
|
39
|
+
## Enforcement Responsibilities
|
|
40
|
+
|
|
41
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
|
42
|
+
acceptable behavior and will take appropriate and fair corrective action in
|
|
43
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
44
|
+
or harmful.
|
|
45
|
+
|
|
46
|
+
Community leaders have the right and responsibility to remove, edit, or reject
|
|
47
|
+
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
48
|
+
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
49
|
+
decisions when appropriate.
|
|
50
|
+
|
|
51
|
+
## Scope
|
|
52
|
+
|
|
53
|
+
This Code of Conduct applies within all community spaces, and also applies when
|
|
54
|
+
an individual is officially representing the community in public spaces.
|
|
55
|
+
Examples of representing our community include using an official email address,
|
|
56
|
+
posting via an official social media account, or acting as an appointed
|
|
57
|
+
representative at an online or offline event.
|
|
58
|
+
|
|
59
|
+
## Enforcement
|
|
60
|
+
|
|
61
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
62
|
+
reported to the community leaders responsible for enforcement at
|
|
63
|
+
[INSERT CONTACT METHOD].
|
|
64
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
|
65
|
+
|
|
66
|
+
All community leaders are obligated to respect the privacy and security of the
|
|
67
|
+
reporter of any incident.
|
|
68
|
+
|
|
69
|
+
## Enforcement Guidelines
|
|
70
|
+
|
|
71
|
+
Community leaders will follow these Community Impact Guidelines in determining
|
|
72
|
+
the consequences for any action they deem in violation of this Code of Conduct:
|
|
73
|
+
|
|
74
|
+
### 1. Correction
|
|
75
|
+
|
|
76
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
77
|
+
unprofessional or unwelcome in the community.
|
|
78
|
+
|
|
79
|
+
**Consequence**: A private, written warning from community leaders, providing
|
|
80
|
+
clarity around the nature of the violation and an explanation of why the
|
|
81
|
+
behavior was inappropriate. A public apology may be requested.
|
|
82
|
+
|
|
83
|
+
### 2. Warning
|
|
84
|
+
|
|
85
|
+
**Community Impact**: A violation through a single incident or series of
|
|
86
|
+
actions.
|
|
87
|
+
|
|
88
|
+
**Consequence**: A warning with consequences for continued behavior. No
|
|
89
|
+
interaction with the people involved, including unsolicited interaction with
|
|
90
|
+
those enforcing the Code of Conduct, for a specified period of time. This
|
|
91
|
+
includes avoiding interactions in community spaces as well as external channels
|
|
92
|
+
like social media. Violating these terms may lead to a temporary or permanent
|
|
93
|
+
ban.
|
|
94
|
+
|
|
95
|
+
### 3. Temporary Ban
|
|
96
|
+
|
|
97
|
+
**Community Impact**: A serious violation of community standards, including
|
|
98
|
+
sustained inappropriate behavior.
|
|
99
|
+
|
|
100
|
+
**Consequence**: A temporary ban from any sort of interaction or public
|
|
101
|
+
communication with the community for a specified period of time. No public or
|
|
102
|
+
private interaction with the people involved, including unsolicited interaction
|
|
103
|
+
with those enforcing the Code of Conduct, is allowed during this period.
|
|
104
|
+
Violating these terms may lead to a permanent ban.
|
|
105
|
+
|
|
106
|
+
### 4. Permanent Ban
|
|
107
|
+
|
|
108
|
+
**Community Impact**: Demonstrating a pattern of violation of community
|
|
109
|
+
standards, including sustained inappropriate behavior, harassment of an
|
|
110
|
+
individual, or aggression toward or disparagement of classes of individuals.
|
|
111
|
+
|
|
112
|
+
**Consequence**: A permanent ban from any sort of public interaction within the
|
|
113
|
+
community.
|
|
114
|
+
|
|
115
|
+
## Attribution
|
|
116
|
+
|
|
117
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
118
|
+
version 2.1, available at
|
|
119
|
+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
120
|
+
|
|
121
|
+
Community Impact Guidelines were inspired by
|
|
122
|
+
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
123
|
+
|
|
124
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
|
125
|
+
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
|
126
|
+
[https://www.contributor-covenant.org/translations][translations].
|
|
127
|
+
|
|
128
|
+
[homepage]: https://www.contributor-covenant.org
|
|
129
|
+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
130
|
+
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
131
|
+
[FAQ]: https://www.contributor-covenant.org/faq
|
|
132
|
+
[translations]: https://www.contributor-covenant.org/translations
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 carter2099
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Hyperliquid Ruby SDK
|
|
2
|
+
|
|
3
|
+
A Ruby SDK for interacting with the Hyperliquid decentralized exchange API.
|
|
4
|
+
|
|
5
|
+
This is v0.1.0 - a read-only implementation focusing on the Info API endpoints for market data, user information, and order book data.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add this line to your application's Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'hyperliquid'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
And then execute:
|
|
16
|
+
|
|
17
|
+
$ bundle install
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
|
|
21
|
+
$ gem install hyperliquid
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Basic Setup
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
require 'hyperliquid'
|
|
29
|
+
|
|
30
|
+
# Create SDK instance (mainnet by default)
|
|
31
|
+
sdk = Hyperliquid.new
|
|
32
|
+
|
|
33
|
+
# Or use testnet
|
|
34
|
+
testnet_sdk = Hyperliquid.new(testnet: true)
|
|
35
|
+
|
|
36
|
+
# Access the Info API
|
|
37
|
+
info = sdk.info
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Info API Methods
|
|
41
|
+
|
|
42
|
+
The SDK provides access to all Hyperliquid Info API endpoints:
|
|
43
|
+
|
|
44
|
+
#### Market Data
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
# Get all market mid prices
|
|
48
|
+
mids = sdk.info.all_mids
|
|
49
|
+
# => { "BTC" => "50000", "ETH" => "3000", ... }
|
|
50
|
+
|
|
51
|
+
# Get asset metadata
|
|
52
|
+
meta = sdk.info.meta
|
|
53
|
+
# => { "universe" => [...] }
|
|
54
|
+
|
|
55
|
+
# Get extended asset metadata with contexts
|
|
56
|
+
meta_ctxs = sdk.info.meta_and_asset_ctxs
|
|
57
|
+
# => { "universe" => [...], "assetCtxs" => [...] }
|
|
58
|
+
|
|
59
|
+
# Get L2 order book for a coin
|
|
60
|
+
book = sdk.info.l2_book("BTC")
|
|
61
|
+
# => { "coin" => "BTC", "levels" => [[asks], [bids]], "time" => ... }
|
|
62
|
+
|
|
63
|
+
# Get candlestick data
|
|
64
|
+
candles = sdk.info.candles_snapshot("BTC", "1h", start_time, end_time)
|
|
65
|
+
# => [{ "t" => ..., "o" => "50000", "h" => "51000", "l" => "49000", "c" => "50500", "v" => "100" }]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### User Data
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
user_address = "0x..." # Wallet address
|
|
72
|
+
|
|
73
|
+
# Get user's open orders
|
|
74
|
+
orders = sdk.info.open_orders(user_address)
|
|
75
|
+
# => [{ "coin" => "BTC", "sz" => "0.1", "px" => "50000", "side" => "A" }]
|
|
76
|
+
|
|
77
|
+
# Get user's fill history
|
|
78
|
+
fills = sdk.info.user_fills(user_address)
|
|
79
|
+
# => [{ "coin" => "BTC", "sz" => "0.1", "px" => "50000", "side" => "A", "time" => 1234567890 }]
|
|
80
|
+
|
|
81
|
+
# Get user's trading state (positions, balances)
|
|
82
|
+
state = sdk.info.user_state(user_address)
|
|
83
|
+
# => { "assetPositions" => [...], "marginSummary" => {...} }
|
|
84
|
+
|
|
85
|
+
# Get order status
|
|
86
|
+
status = sdk.info.order_status(user_address, order_id)
|
|
87
|
+
# => { "status" => "filled", "sz" => "0.1", "px" => "50000" }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Configuration
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
# Custom timeout (default: 30 seconds)
|
|
94
|
+
sdk = Hyperliquid.new(timeout: 60)
|
|
95
|
+
|
|
96
|
+
# Check which environment you're using
|
|
97
|
+
sdk.testnet? # => false
|
|
98
|
+
sdk.base_url # => "https://api.hyperliquid.xyz"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Error Handling
|
|
102
|
+
|
|
103
|
+
The SDK provides comprehensive error handling:
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
begin
|
|
107
|
+
orders = sdk.info.open_orders(user_address)
|
|
108
|
+
rescue Hyperliquid::AuthenticationError
|
|
109
|
+
# Handle authentication issues
|
|
110
|
+
rescue Hyperliquid::RateLimitError
|
|
111
|
+
# Handle rate limiting
|
|
112
|
+
rescue Hyperliquid::ServerError
|
|
113
|
+
# Handle server errors
|
|
114
|
+
rescue Hyperliquid::NetworkError
|
|
115
|
+
# Handle network connectivity issues
|
|
116
|
+
rescue Hyperliquid::Error => e
|
|
117
|
+
# Handle any other Hyperliquid API errors
|
|
118
|
+
puts "Error: #{e.message}"
|
|
119
|
+
puts "Status: #{e.status_code}" if e.status_code
|
|
120
|
+
puts "Response: #{e.response_body}" if e.response_body
|
|
121
|
+
end
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Available error classes:
|
|
125
|
+
- `Hyperliquid::Error` - Base error class
|
|
126
|
+
- `Hyperliquid::ClientError` - 4xx errors
|
|
127
|
+
- `Hyperliquid::ServerError` - 5xx errors
|
|
128
|
+
- `Hyperliquid::AuthenticationError` - 401 errors
|
|
129
|
+
- `Hyperliquid::BadRequestError` - 400 errors
|
|
130
|
+
- `Hyperliquid::NotFoundError` - 404 errors
|
|
131
|
+
- `Hyperliquid::RateLimitError` - 429 errors
|
|
132
|
+
- `Hyperliquid::NetworkError` - Connection issues
|
|
133
|
+
- `Hyperliquid::TimeoutError` - Request timeouts
|
|
134
|
+
|
|
135
|
+
## API Reference
|
|
136
|
+
|
|
137
|
+
### Hyperliquid.new(options = {})
|
|
138
|
+
|
|
139
|
+
Creates a new SDK instance.
|
|
140
|
+
|
|
141
|
+
**Parameters:**
|
|
142
|
+
- `testnet` (Boolean) - Use testnet instead of mainnet (default: false)
|
|
143
|
+
- `timeout` (Integer) - Request timeout in seconds (default: 30)
|
|
144
|
+
|
|
145
|
+
### Info API Methods
|
|
146
|
+
|
|
147
|
+
All Info methods return parsed JSON responses from the Hyperliquid API.
|
|
148
|
+
|
|
149
|
+
#### Market Data Methods
|
|
150
|
+
- `all_mids()` - Get all market mid prices
|
|
151
|
+
- `meta()` - Get asset metadata
|
|
152
|
+
- `meta_and_asset_ctxs()` - Get extended asset metadata
|
|
153
|
+
- `l2_book(coin)` - Get L2 order book for a coin
|
|
154
|
+
- `candles_snapshot(coin, interval, start_time, end_time)` - Get candlestick data
|
|
155
|
+
|
|
156
|
+
#### User Data Methods
|
|
157
|
+
- `open_orders(user)` - Get user's open orders
|
|
158
|
+
- `user_fills(user)` - Get user's fill history
|
|
159
|
+
- `user_state(user)` - Get user's trading state
|
|
160
|
+
- `order_status(user, oid)` - Get order status
|
|
161
|
+
|
|
162
|
+
## Development
|
|
163
|
+
|
|
164
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
165
|
+
|
|
166
|
+
Run the example:
|
|
167
|
+
```bash
|
|
168
|
+
ruby example.rb
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Run tests:
|
|
172
|
+
```bash
|
|
173
|
+
rake spec
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Run tests and linting together:
|
|
177
|
+
```bash
|
|
178
|
+
rake
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Run linting:
|
|
182
|
+
```bash
|
|
183
|
+
rake rubocop
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Roadmap
|
|
187
|
+
|
|
188
|
+
This is v0.1.0 with read-only Info API support. Future versions will include:
|
|
189
|
+
|
|
190
|
+
- v0.2.0: Trading API (place orders, cancel orders, etc.)
|
|
191
|
+
- v0.3.0: WebSocket support for real-time data
|
|
192
|
+
- v0.4.0: Advanced trading features
|
|
193
|
+
|
|
194
|
+
## Contributing
|
|
195
|
+
|
|
196
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/carter2099/hyperliquid.
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/example.rb
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative 'lib/hyperliquid'
|
|
5
|
+
|
|
6
|
+
# Example usage of the Hyperliquid Ruby SDK
|
|
7
|
+
|
|
8
|
+
# Create a new SDK instance (defaults to mainnet)
|
|
9
|
+
sdk = Hyperliquid.new
|
|
10
|
+
|
|
11
|
+
puts 'Hyperliquid Ruby SDK v0.1 - Info API Examples'
|
|
12
|
+
puts '=' * 50
|
|
13
|
+
|
|
14
|
+
# Example 1: Get all market mid prices
|
|
15
|
+
begin
|
|
16
|
+
puts "\n1. Getting all market mid prices..."
|
|
17
|
+
mids = sdk.info.all_mids
|
|
18
|
+
puts "Found #{mids.length} markets" if mids.is_a?(Hash)
|
|
19
|
+
rescue Hyperliquid::Error => e
|
|
20
|
+
puts "Error getting market mids: #{e.message}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Example 2: Get metadata for all assets
|
|
24
|
+
begin
|
|
25
|
+
puts "\n2. Getting asset metadata..."
|
|
26
|
+
meta = sdk.info.meta
|
|
27
|
+
puts 'Got metadata for universe' if meta.is_a?(Hash)
|
|
28
|
+
rescue Hyperliquid::Error => e
|
|
29
|
+
puts "Error getting metadata: #{e.message}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Example 3: Use testnet
|
|
33
|
+
puts "\n3. Using testnet..."
|
|
34
|
+
testnet_sdk = Hyperliquid.new(testnet: true)
|
|
35
|
+
puts "Testnet SDK created, base URL: #{testnet_sdk.base_url}"
|
|
36
|
+
|
|
37
|
+
# Example 4: User-specific endpoints (requires valid wallet address)
|
|
38
|
+
wallet_address = "0x#{'0' * 40}" # Example placeholder address
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
puts "\n4. Getting open orders for wallet #{wallet_address[0..10]}..."
|
|
42
|
+
orders = sdk.info.open_orders(wallet_address)
|
|
43
|
+
puts "Open orders: #{orders}"
|
|
44
|
+
rescue Hyperliquid::Error => e
|
|
45
|
+
puts "Error getting open orders: #{e.message}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
puts "\nSDK examples completed!"
|
|
49
|
+
puts "\nAvailable Info methods:"
|
|
50
|
+
puts '- all_mids() - Get all market mid prices'
|
|
51
|
+
puts "- open_orders(user) - Get user's open orders"
|
|
52
|
+
puts "- user_fills(user) - Get user's fill history"
|
|
53
|
+
puts '- order_status(user, oid) - Get order status'
|
|
54
|
+
puts "- user_state(user) - Get user's trading state"
|
|
55
|
+
puts '- meta() - Get asset metadata'
|
|
56
|
+
puts '- meta_and_asset_ctxs() - Get extended asset metadata'
|
|
57
|
+
puts '- l2_book(coin) - Get L2 order book'
|
|
58
|
+
puts '- candles_snapshot(coin, interval, start_time, end_time) - Get candlestick data'
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'faraday/retry'
|
|
5
|
+
require 'json'
|
|
6
|
+
|
|
7
|
+
module Hyperliquid
|
|
8
|
+
# HTTP client for making requests to Hyperliquid API
|
|
9
|
+
class Client
|
|
10
|
+
# TODO:
|
|
11
|
+
# Unused for now. To be added to build_connection
|
|
12
|
+
DEFAULT_RETRY_OPTIONS = {
|
|
13
|
+
max: 2,
|
|
14
|
+
interval: 0.5,
|
|
15
|
+
interval_randomness: 0.5,
|
|
16
|
+
backoff_factor: 2,
|
|
17
|
+
retry_statuses: [502, 503, 504],
|
|
18
|
+
exceptions: [
|
|
19
|
+
Faraday::ConnectionFailed,
|
|
20
|
+
Faraday::TimeoutError
|
|
21
|
+
]
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
# Initialize a new HTTP client
|
|
25
|
+
# @param base_url [String] The base URL for the API
|
|
26
|
+
# @param timeout [Integer] Request timeout in seconds (default: Constants::DEFAULT_TIMEOUT)
|
|
27
|
+
def initialize(base_url:, timeout: Constants::DEFAULT_TIMEOUT)
|
|
28
|
+
@connection = build_connection(base_url, timeout)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Make a POST request to the API
|
|
32
|
+
# @param endpoint [String] The API endpoint to make the request to
|
|
33
|
+
# @param body [Hash] The request body as a hash (default: {})
|
|
34
|
+
# @return [Hash, String] The parsed JSON response or raw response body
|
|
35
|
+
# @raise [NetworkError] When connection fails
|
|
36
|
+
# @raise [TimeoutError] When request times out
|
|
37
|
+
# @raise [BadRequestError] When API returns 400 status
|
|
38
|
+
# @raise [AuthenticationError] When API returns 401 status
|
|
39
|
+
# @raise [NotFoundError] When API returns 404 status
|
|
40
|
+
# @raise [RateLimitError] When API returns 429 status
|
|
41
|
+
# @raise [ServerError] When API returns 5xx status
|
|
42
|
+
# @raise [ClientError] When API returns unexpected status
|
|
43
|
+
def post(endpoint, body = {})
|
|
44
|
+
response = @connection.post(endpoint) do |req|
|
|
45
|
+
req.headers['Content-Type'] = 'application/json'
|
|
46
|
+
req.body = body.to_json unless body.empty?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
handle_response(response)
|
|
50
|
+
rescue Faraday::ConnectionFailed => e
|
|
51
|
+
raise NetworkError, "Connection failed: #{e.message}"
|
|
52
|
+
rescue Faraday::TimeoutError => e
|
|
53
|
+
raise TimeoutError, "Request timed out: #{e.message}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def build_connection(base_url, timeout)
|
|
59
|
+
Faraday.new(url: base_url) do |conn|
|
|
60
|
+
conn.options.timeout = timeout
|
|
61
|
+
conn.options.read_timeout = Constants::DEFAULT_READ_TIMEOUT
|
|
62
|
+
|
|
63
|
+
# TODO:
|
|
64
|
+
# conn.request :retry, DEFAULT_RETRY_OPTIONS
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def handle_response(response)
|
|
69
|
+
parsed_body = parse_json(response.body)
|
|
70
|
+
|
|
71
|
+
case response.status
|
|
72
|
+
when 200..299
|
|
73
|
+
parsed_body
|
|
74
|
+
when 400
|
|
75
|
+
raise BadRequestError.new(
|
|
76
|
+
'Bad request',
|
|
77
|
+
status_code: response.status,
|
|
78
|
+
response_body: parsed_body
|
|
79
|
+
)
|
|
80
|
+
when 401
|
|
81
|
+
raise AuthenticationError.new(
|
|
82
|
+
'Authentication failed',
|
|
83
|
+
status_code: response.status,
|
|
84
|
+
response_body: parsed_body
|
|
85
|
+
)
|
|
86
|
+
when 404
|
|
87
|
+
raise NotFoundError.new(
|
|
88
|
+
'Resource not found',
|
|
89
|
+
status_code: response.status,
|
|
90
|
+
response_body: parsed_body
|
|
91
|
+
)
|
|
92
|
+
when 429
|
|
93
|
+
raise RateLimitError.new(
|
|
94
|
+
'Rate limit exceeded',
|
|
95
|
+
status_code: response.status,
|
|
96
|
+
response_body: parsed_body
|
|
97
|
+
)
|
|
98
|
+
when 500..599
|
|
99
|
+
raise ServerError.new(
|
|
100
|
+
'Server error',
|
|
101
|
+
status_code: response.status,
|
|
102
|
+
response_body: parsed_body
|
|
103
|
+
)
|
|
104
|
+
else
|
|
105
|
+
raise ClientError.new(
|
|
106
|
+
"Unexpected response status: #{response.status}",
|
|
107
|
+
status_code: response.status,
|
|
108
|
+
response_body: parsed_body
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def parse_json(body)
|
|
114
|
+
return {} if body.nil? || body.empty?
|
|
115
|
+
|
|
116
|
+
JSON.parse(body)
|
|
117
|
+
rescue JSON::ParserError
|
|
118
|
+
body
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hyperliquid
|
|
4
|
+
# Constants for Hyperliquid API
|
|
5
|
+
module Constants
|
|
6
|
+
# API URLs
|
|
7
|
+
MAINNET_API_URL = 'https://api.hyperliquid.xyz'
|
|
8
|
+
TESTNET_API_URL = 'https://api.hyperliquid-testnet.xyz'
|
|
9
|
+
|
|
10
|
+
# API endpoints
|
|
11
|
+
INFO_ENDPOINT = '/info'
|
|
12
|
+
|
|
13
|
+
# Request timeouts (seconds)
|
|
14
|
+
DEFAULT_TIMEOUT = 30
|
|
15
|
+
DEFAULT_READ_TIMEOUT = 30
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hyperliquid
|
|
4
|
+
# Base error class for all Hyperliquid SDK errors
|
|
5
|
+
class Error < StandardError
|
|
6
|
+
attr_reader :status_code, :response_body
|
|
7
|
+
|
|
8
|
+
def initialize(message, status_code: nil, response_body: nil)
|
|
9
|
+
super(message)
|
|
10
|
+
@status_code = status_code
|
|
11
|
+
@response_body = response_body
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Error for HTTP client issues
|
|
16
|
+
class ClientError < Error; end
|
|
17
|
+
|
|
18
|
+
# Error for server-side issues
|
|
19
|
+
class ServerError < Error; end
|
|
20
|
+
|
|
21
|
+
# Error for authentication issues
|
|
22
|
+
class AuthenticationError < ClientError; end
|
|
23
|
+
|
|
24
|
+
# Error for rate limiting
|
|
25
|
+
class RateLimitError < ClientError; end
|
|
26
|
+
|
|
27
|
+
# Error for bad requests
|
|
28
|
+
class BadRequestError < ClientError; end
|
|
29
|
+
|
|
30
|
+
# Error for not found resources
|
|
31
|
+
class NotFoundError < ClientError; end
|
|
32
|
+
|
|
33
|
+
# Error for connection timeouts
|
|
34
|
+
class TimeoutError < Error; end
|
|
35
|
+
|
|
36
|
+
# Error for network connectivity issues
|
|
37
|
+
class NetworkError < Error; end
|
|
38
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hyperliquid
|
|
4
|
+
# Client for read-only Info API endpoints
|
|
5
|
+
class Info
|
|
6
|
+
def initialize(client)
|
|
7
|
+
@client = client
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Get all market mid prices
|
|
11
|
+
# @return [Hash] Hash containing mid prices for all markets
|
|
12
|
+
def all_mids
|
|
13
|
+
@client.post(Constants::INFO_ENDPOINT, { type: 'allMids' })
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Get a user's open orders
|
|
17
|
+
# @param user [String] Wallet address
|
|
18
|
+
# @return [Array] Array of open orders for the user
|
|
19
|
+
def open_orders(user)
|
|
20
|
+
@client.post(Constants::INFO_ENDPOINT, { type: 'openOrders', user: user })
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Get a user's fill history
|
|
24
|
+
# @param user [String] Wallet address
|
|
25
|
+
# @return [Array] Array of fill history for the user
|
|
26
|
+
def user_fills(user)
|
|
27
|
+
@client.post(Constants::INFO_ENDPOINT, { type: 'userFills', user: user })
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Get order status by order ID
|
|
31
|
+
# @param user [String] Wallet address
|
|
32
|
+
# @param oid [Integer] Order ID
|
|
33
|
+
# @return [Hash] Order status information
|
|
34
|
+
def order_status(user, oid)
|
|
35
|
+
@client.post(Constants::INFO_ENDPOINT, { type: 'orderStatus', user: user, oid: oid })
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Get user's trading state
|
|
39
|
+
# @param user [String] Wallet address
|
|
40
|
+
# @return [Hash] User's trading state including positions and balances
|
|
41
|
+
def user_state(user)
|
|
42
|
+
@client.post(Constants::INFO_ENDPOINT, { type: 'clearinghouseState', user: user })
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Get metadata for all assets
|
|
46
|
+
# @return [Hash] Metadata for all tradable assets
|
|
47
|
+
def meta
|
|
48
|
+
@client.post(Constants::INFO_ENDPOINT, { type: 'meta' })
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Get metadata for all assets including universe info
|
|
52
|
+
# @return [Hash] Extended metadata for all assets with universe information
|
|
53
|
+
def meta_and_asset_ctxs
|
|
54
|
+
@client.post(Constants::INFO_ENDPOINT, { type: 'metaAndAssetCtxs' })
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Get L2 order book for a coin
|
|
58
|
+
# @param coin [String] Coin symbol (e.g., "BTC", "ETH")
|
|
59
|
+
# @return [Hash] L2 order book data with bids and asks
|
|
60
|
+
def l2_book(coin)
|
|
61
|
+
@client.post(Constants::INFO_ENDPOINT, { type: 'l2Book', coin: coin })
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Get candlestick data
|
|
65
|
+
# @param coin [String] Coin symbol
|
|
66
|
+
# @param interval [String] Time interval (e.g., "1m", "1h", "1d")
|
|
67
|
+
# @param start_time [Integer] Start timestamp in milliseconds
|
|
68
|
+
# @param end_time [Integer] End timestamp in milliseconds
|
|
69
|
+
# @return [Array] Array of candlestick data
|
|
70
|
+
def candles_snapshot(coin, interval, start_time, end_time)
|
|
71
|
+
@client.post(Constants::INFO_ENDPOINT, {
|
|
72
|
+
type: 'candleSnapshot',
|
|
73
|
+
req: {
|
|
74
|
+
coin: coin,
|
|
75
|
+
interval: interval,
|
|
76
|
+
startTime: start_time,
|
|
77
|
+
endTime: end_time
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
data/lib/hyperliquid.rb
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'hyperliquid/version'
|
|
4
|
+
require_relative 'hyperliquid/constants'
|
|
5
|
+
require_relative 'hyperliquid/errors'
|
|
6
|
+
require_relative 'hyperliquid/client'
|
|
7
|
+
require_relative 'hyperliquid/info'
|
|
8
|
+
|
|
9
|
+
# Ruby SDK for Hyperliquid API
|
|
10
|
+
# Provides read-only access to Hyperliquid's decentralized exchange API
|
|
11
|
+
module Hyperliquid
|
|
12
|
+
# Create a new SDK instance
|
|
13
|
+
# @param testnet [Boolean] Whether to use testnet (default: false for mainnet)
|
|
14
|
+
# @param timeout [Integer] Request timeout in seconds (default: 30)
|
|
15
|
+
# @return [Hyperliquid::SDK] A new SDK instance
|
|
16
|
+
def self.new(testnet: false, timeout: Constants::DEFAULT_TIMEOUT)
|
|
17
|
+
SDK.new(testnet: testnet, timeout: timeout)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Main SDK class
|
|
21
|
+
class SDK
|
|
22
|
+
attr_reader :info
|
|
23
|
+
|
|
24
|
+
# Initialize the SDK
|
|
25
|
+
# @param testnet [Boolean] Whether to use testnet (default: false for mainnet)
|
|
26
|
+
# @param timeout [Integer] Request timeout in seconds
|
|
27
|
+
def initialize(testnet: false, timeout: Constants::DEFAULT_TIMEOUT)
|
|
28
|
+
base_url = testnet ? Constants::TESTNET_API_URL : Constants::MAINNET_API_URL
|
|
29
|
+
client = Client.new(base_url: base_url, timeout: timeout)
|
|
30
|
+
|
|
31
|
+
@info = Info.new(client)
|
|
32
|
+
@testnet = testnet
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Check if using testnet
|
|
36
|
+
# @return [Boolean] True if using testnet, false if mainnet
|
|
37
|
+
def testnet?
|
|
38
|
+
@testnet
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Get the base API URL being used
|
|
42
|
+
# @return [String] The base API URL
|
|
43
|
+
def base_url
|
|
44
|
+
@testnet ? Constants::TESTNET_API_URL : Constants::MAINNET_API_URL
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
data/sig/hyperliquid.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hyperliquid
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- carter2099
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: faraday
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: faraday-retry
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.0'
|
|
40
|
+
description: A Ruby SDK for interacting with Hyperliquid's decentralized exchange
|
|
41
|
+
API
|
|
42
|
+
email:
|
|
43
|
+
- carter2077@pm.me
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- ".rspec"
|
|
49
|
+
- ".rubocop.yml"
|
|
50
|
+
- ".ruby-version"
|
|
51
|
+
- CHANGELOG.md
|
|
52
|
+
- CODE_OF_CONDUCT.md
|
|
53
|
+
- LICENSE.txt
|
|
54
|
+
- README.md
|
|
55
|
+
- Rakefile
|
|
56
|
+
- example.rb
|
|
57
|
+
- lib/hyperliquid.rb
|
|
58
|
+
- lib/hyperliquid/client.rb
|
|
59
|
+
- lib/hyperliquid/constants.rb
|
|
60
|
+
- lib/hyperliquid/errors.rb
|
|
61
|
+
- lib/hyperliquid/info.rb
|
|
62
|
+
- lib/hyperliquid/version.rb
|
|
63
|
+
- sig/hyperliquid.rbs
|
|
64
|
+
homepage: https://github.com/carter2099/hyperliquid
|
|
65
|
+
licenses:
|
|
66
|
+
- MIT
|
|
67
|
+
metadata:
|
|
68
|
+
homepage_uri: https://github.com/carter2099/hyperliquid
|
|
69
|
+
source_code_uri: https://github.com/carter2099/hyperliquid
|
|
70
|
+
changelog_uri: https://github.com/carter2099/hyperliquid/blob/main/CHANGELOG.md
|
|
71
|
+
rubygems_mfa_required: 'true'
|
|
72
|
+
rdoc_options: []
|
|
73
|
+
require_paths:
|
|
74
|
+
- lib
|
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
|
+
requirements:
|
|
77
|
+
- - ">="
|
|
78
|
+
- !ruby/object:Gem::Version
|
|
79
|
+
version: 3.4.0
|
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
|
+
requirements:
|
|
82
|
+
- - ">="
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: '0'
|
|
85
|
+
requirements: []
|
|
86
|
+
rubygems_version: 3.6.7
|
|
87
|
+
specification_version: 4
|
|
88
|
+
summary: Ruby SDK for Hyperliquid API
|
|
89
|
+
test_files: []
|