hyperliquid 0.3.0 → 0.4.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: f5ac9afd372d7362c7d984fa21d21ef3822a2b3a2beebeb2067f088055be2b7b
4
- data.tar.gz: b45bb221a66d07fa76d5f53375c24aef11f55a421cf0f33c038c53cbf49b797e
3
+ metadata.gz: 789cad57e75566edeaeb09a8437763d716caa6978c8a2ed9322623eb223c40a7
4
+ data.tar.gz: f2e68884aedf2f33cf4b66d811e02fe1f3fdaf4e1b77793b29b64b52934773ad
5
5
  SHA512:
6
- metadata.gz: d55505a3ebf3595363542005aa5b6e3400678b22bd6689ffb38318f4f8450362d1e3ae2a8041a877dab82d1343f28bc48ba61f6a50a02dd81fbd2989bcad2660
7
- data.tar.gz: 17e7a2df87f5f30b3ce79f6767ba5da9a95d097990b2b990ddc7d71bee2600c15630c3b3f96bcf350a151479eebd8520eb38f74949bad2e9fc6026a23d740d80
6
+ metadata.gz: 271fd73adff54d249d4ca2bf2b9f16aacdba8519ea0891e36b2315781019e231ddc158e2a0caeb401d2b43c3cf34f5cee68a0f1c15b183f41f2a1363a98af05b
7
+ data.tar.gz: 8b0c7fe1e353bdd6aa01d745708c0dfd7b3909bd95185c88ce9cd7fa93204535cccea04f47ad22d786f068c6e3f2cee77a69ce81df6aa1615e700743248e35fa
data/.rubocop.yml CHANGED
@@ -1,6 +1,10 @@
1
1
  AllCops:
2
2
  NewCops: enable
3
3
  TargetRubyVersion: 3.4.3
4
+ SuggestExtensions: false
5
+ Exclude:
6
+ - 'test_*.rb' # Exclude ad-hoc integration test scripts
7
+ - 'vendor/**/*' # Exclude vendored gems (CI bundles here)
4
8
 
5
9
  # Allow longer methods for complex logic
6
10
  Metrics/MethodLength:
@@ -14,7 +18,22 @@ Metrics/AbcSize:
14
18
  Metrics/ClassLength:
15
19
  Enabled: false
16
20
 
17
- # Allow longer blocks in specs - this is normal for tests
21
+ # Allow longer blocks in specs and gemspec - this is normal for tests and gem configs
18
22
  Metrics/BlockLength:
19
23
  Exclude:
20
- - 'spec/**/*'
24
+ - 'spec/**/*'
25
+ - '*.gemspec'
26
+
27
+ # Exchange API methods require many parameters
28
+ Metrics/ParameterLists:
29
+ Exclude:
30
+ - 'lib/hyperliquid/exchange.rb'
31
+
32
+ # Allow higher complexity for order type conversion logic
33
+ Metrics/CyclomaticComplexity:
34
+ Exclude:
35
+ - 'lib/hyperliquid/exchange.rb'
36
+
37
+ Metrics/PerceivedComplexity:
38
+ Exclude:
39
+ - 'lib/hyperliquid/exchange.rb'
data/CHANGELOG.md CHANGED
@@ -1,13 +1,33 @@
1
1
  ## [Ruby Hyperliquid SDK Changelog]
2
2
 
3
- ## [0.1.0] - 2025-08-21
3
+ ## [0.4.0] - 2025-01-27
4
4
 
5
- - Initial release which includes info endpoints for market and user perps data
5
+ - Add Exchange API for authenticated write operations (trading)
6
+ - Order placement: `order`, `bulk_orders`, `market_order`
7
+ - Order cancellation: `cancel`, `cancel_by_cloid`, `bulk_cancel`, `bulk_cancel_by_cloid`
8
+ - Trigger orders (stop loss / take profit) support
9
+ - Vault trading support via optional `vault_address` parameter
10
+ - Order expiration support via `expires_after` parameter
11
+ - Add EIP-712 signing infrastructure matching official Python SDK
12
+ - Phantom agent signing scheme
13
+ - Full parity with Python SDK signature generation
14
+ - Add new dependencies: `eth` (~> 0.5), `msgpack` (~> 1.7)
15
+ - SDK now accepts optional `private_key` and `expires_after` parameters
6
16
 
7
- ## [0.1.1] - 2025-09-23
17
+ ## [0.3.0] - 2025-09-24
8
18
 
9
- - Fixed retry logic, make retry logic disabled by default
19
+ - Full parity with all Hyperliquid Info APIs
20
+ - All Info APIs implemented
21
+ - Code, tests, and docs reflect structure of official Hyperliquid API documentation
10
22
 
11
23
  ## [0.2.0] - 2025-09-24
12
24
 
13
25
  - Add info endpoints for user spot data
26
+
27
+ ## [0.1.1] - 2025-09-23
28
+
29
+ - Fixed retry logic, make retry logic disabled by default
30
+
31
+ ## [0.1.0] - 2025-08-21
32
+
33
+ - Initial release which includes info endpoints for market and user perps data
data/CLAUDE.md ADDED
@@ -0,0 +1,202 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Overview
6
+
7
+ This is a Ruby SDK for the Hyperliquid decentralized exchange API. The current version (0.4.0) supports both **read operations** (Info API) and **authenticated write operations** (Exchange API) for trading.
8
+
9
+ **Target Ruby Version**: 3.4.0+
10
+
11
+ ## Development Commands
12
+
13
+ ### Running Tests
14
+ ```bash
15
+ # Run all tests
16
+ rake spec
17
+
18
+ # Run tests and linting together (default rake task)
19
+ rake
20
+
21
+ # Run a single test file
22
+ bundle exec rspec spec/hyperliquid/cloid_spec.rb
23
+
24
+ # Run a specific test by line number
25
+ bundle exec rspec spec/hyperliquid/cloid_spec.rb:62
26
+ ```
27
+
28
+ ### Linting
29
+ ```bash
30
+ # Run RuboCop linter
31
+ rake rubocop
32
+ ```
33
+
34
+ ### Interactive Console
35
+ ```bash
36
+ # Open an interactive console with the SDK loaded
37
+ bin/console
38
+ ```
39
+
40
+ ### Example Script
41
+ ```bash
42
+ # Run the example usage script
43
+ ruby example.rb
44
+ ```
45
+
46
+ ### Integration Testing (Testnet)
47
+ ```bash
48
+ # Run the testnet integration test (requires private key)
49
+ # Get testnet funds from: https://app.hyperliquid-testnet.xyz
50
+ HYPERLIQUID_PRIVATE_KEY=0x... ruby test_integration.rb
51
+ ```
52
+
53
+ The integration test executes real trades on testnet:
54
+ 1. Spot market roundtrip (buy/sell PURR/USDC)
55
+ 2. Spot limit order (place and cancel)
56
+ 3. Perp market roundtrip (long/close BTC)
57
+ 4. Perp limit order (place short, cancel)
58
+
59
+ ### Setup
60
+ ```bash
61
+ # Install dependencies
62
+ bin/setup
63
+ ```
64
+
65
+ ## Architecture
66
+
67
+ ### Core Components
68
+
69
+ **Hyperliquid::SDK** (`lib/hyperliquid.rb`)
70
+ - Main entry point created via `Hyperliquid.new(testnet:, timeout:, retry_enabled:, private_key:, expires_after:)`
71
+ - Manages environment selection (mainnet vs testnet)
72
+ - Exposes the `info` API client (always available)
73
+ - Exposes the `exchange` API client (when `private_key` provided)
74
+
75
+ **Hyperliquid::Client** (`lib/hyperliquid/client.rb`)
76
+ - Low-level HTTP client built on Faraday
77
+ - Handles all POST requests to the Hyperliquid API
78
+ - Manages retry logic (disabled by default, opt-in via `retry_enabled: true`)
79
+ - Converts HTTP errors into typed exceptions
80
+
81
+ **Hyperliquid::Info** (`lib/hyperliquid/info.rb`)
82
+ - High-level API client for all Info endpoints (read-only)
83
+ - Organized into three sections:
84
+ 1. **General Info**: Market data, user orders, fills, rate limits, portfolios, referrals, fees, staking
85
+ 2. **Perpetuals**: Perp DEXs, metadata, user state, funding rates, open interest
86
+ 3. **Spot**: Spot tokens, balances, deploy auctions, token details
87
+ - All methods accept user wallet addresses and return parsed JSON responses
88
+
89
+ **Hyperliquid::Exchange** (`lib/hyperliquid/exchange.rb`)
90
+ - High-level API client for Exchange endpoints (authenticated write operations)
91
+ - Order placement: `order`, `bulk_orders`, `market_order`
92
+ - Order cancellation: `cancel`, `cancel_by_cloid`, `bulk_cancel`, `bulk_cancel_by_cloid`
93
+ - Supports trigger orders (stop loss / take profit)
94
+ - Supports vault trading via `vault_address` parameter
95
+ - Caches asset metadata for efficient lookups
96
+
97
+ **Hyperliquid::Signing::Signer** (`lib/hyperliquid/signing/signer.rb`)
98
+ - EIP-712 signature generation using phantom agent scheme
99
+ - Matches official Python SDK signing algorithm exactly
100
+ - Supports vault address and expiration in signature
101
+
102
+ **Hyperliquid::Signing::EIP712** (`lib/hyperliquid/signing/eip712.rb`)
103
+ - EIP-712 domain and type definitions
104
+ - L1 chain ID (1337) and source identifiers ('a' mainnet, 'b' testnet)
105
+
106
+ **Hyperliquid::Cloid** (`lib/hyperliquid/cloid.rb`)
107
+ - Type-safe client order ID class
108
+ - Validates 16-byte hex format (0x + 32 hex characters)
109
+ - Factory methods: `from_int`, `from_str`, `from_uuid`, `random`
110
+
111
+ **Hyperliquid::Constants** (`lib/hyperliquid/constants.rb`)
112
+ - API URLs for mainnet and testnet
113
+ - Endpoint paths (`/info`, `/exchange`)
114
+ - Default timeout values
115
+
116
+ **Hyperliquid::Errors** (`lib/hyperliquid/errors.rb`)
117
+ - Typed exception hierarchy for API errors
118
+ - Base class: `Hyperliquid::Error`
119
+ - Specific errors: `ClientError`, `ServerError`, `AuthenticationError`, `RateLimitError`, `BadRequestError`, `NotFoundError`, `TimeoutError`, `NetworkError`
120
+
121
+ ### API Request Pattern
122
+
123
+ **Info API (read-only):**
124
+ 1. SDK method called (e.g., `sdk.info.all_mids`)
125
+ 2. Info class builds request body with `type` field (e.g., `{ type: 'allMids' }`)
126
+ 3. Client POSTs JSON body to `/info` endpoint
127
+ 4. Client parses response and handles errors
128
+ 5. Parsed JSON returned to caller
129
+
130
+ **Exchange API (authenticated):**
131
+ 1. SDK method called (e.g., `sdk.exchange.order(...)`)
132
+ 2. Exchange class builds action payload with order/cancel details
133
+ 3. Signer generates EIP-712 signature over msgpack-encoded action
134
+ 4. Exchange POSTs signed payload to `/exchange` endpoint
135
+ 5. Client parses response and handles errors
136
+ 6. Parsed JSON returned to caller
137
+
138
+ ### Testing
139
+
140
+ - Uses RSpec for testing
141
+ - WebMock for HTTP mocking
142
+ - Spec helper configures WebMock to reset between tests
143
+ - Test files mirror source structure in `spec/`
144
+
145
+ ### Code Style
146
+
147
+ RuboCop configuration (`.rubocop.yml`):
148
+ - Targets Ruby 3.4.0+
149
+ - Allows longer methods (max 50 lines) for complex logic
150
+ - Disables class length checks (Info/Exchange classes implement many endpoints)
151
+ - Excludes block length checks for specs
152
+ - Enables NewCops by default
153
+
154
+ ## Key Implementation Details
155
+
156
+ ### Retry Logic
157
+ - **Disabled by default** for predictable behavior in time-sensitive trading
158
+ - When enabled: max 2 retries, 0.5s base interval, exponential backoff (2x), ±50% randomness
159
+ - Retries on: connection failures, timeouts, 429 (rate limit), 5xx errors
160
+
161
+ ### API Endpoints
162
+ - Info requests POST to `/info` endpoint
163
+ - Exchange requests POST to `/exchange` endpoint
164
+ - Request body includes `type` field indicating the operation
165
+
166
+ ### Time Parameters
167
+ - All timestamps are in **milliseconds** (not seconds)
168
+ - Methods with time ranges support optional `end_time` parameter
169
+ - `expires_after` is an absolute timestamp in milliseconds
170
+
171
+ ### Signature Generation (Python SDK Parity)
172
+ The signing implementation matches the official Python SDK exactly:
173
+ - **Action hash**: `keccak256(msgpack(action) + nonce(8B BE) + vault_flag + [vault_addr] + [expires_flag + expires_after])`
174
+ - **Phantom agent**: `{ source: 'a'|'b', connectionId: action_hash }`
175
+ - **EIP-712 signature** over phantom agent with Exchange domain
176
+
177
+ ### Float to Wire Format
178
+ Numeric values are converted to strings matching Python SDK `float_to_wire`:
179
+ - 8 decimal precision
180
+ - Rounding tolerance validation (1e-12)
181
+ - Trailing zero normalization (no scientific notation)
182
+
183
+ ### Market Order Price Calculation
184
+ Market orders use Python SDK `_slippage_price` algorithm:
185
+ - Apply slippage to mid price
186
+ - Round to 5 significant figures
187
+ - Round to asset-specific decimal places: `(6 for perp, 8 for spot) - szDecimals`
188
+
189
+ ### Client Order IDs (Cloid)
190
+ - Must be 16 bytes in hex format: `0x` + 32 hex characters
191
+ - Use `Hyperliquid::Cloid` class for type safety and validation
192
+ - Factory methods: `from_int(n)`, `from_str(s)`, `from_uuid(uuid)`, `random`
193
+
194
+ ## Development Workflow
195
+
196
+ 1. Make changes to library code in `lib/hyperliquid/`
197
+ 2. Add/update tests in `spec/`
198
+ 3. Run tests: `rake spec`
199
+ 4. Run linter: `rake rubocop`
200
+ 5. Test in console: `bin/console`
201
+ 6. Run example script: `ruby example.rb`
202
+
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A Ruby SDK for interacting with the Hyperliquid decentralized exchange API.
4
4
 
5
- The latest version is v0.2.0 - a read-only implementation focusing on the Info API endpoints for market data, user information, and order book data.
5
+ The SDK supports both read operations (Info API) and authenticated write operations (Exchange API) for trading.
6
6
 
7
7
  ## Installation
8
8
 
@@ -27,14 +27,23 @@ Or install it yourself as:
27
27
  ```ruby
28
28
  require 'hyperliquid'
29
29
 
30
- # Create SDK instance (mainnet by default)
30
+ # Create SDK instance for read-only operations (mainnet by default)
31
31
  sdk = Hyperliquid.new
32
32
 
33
33
  # Or use testnet
34
34
  testnet_sdk = Hyperliquid.new(testnet: true)
35
35
 
36
- # Access the Info API
36
+ # Access the Info API (read operations)
37
37
  info = sdk.info
38
+
39
+ # For trading operations, provide a private key
40
+ trading_sdk = Hyperliquid.new(
41
+ testnet: true,
42
+ private_key: ENV['HYPERLIQUID_PRIVATE_KEY']
43
+ )
44
+
45
+ # Access the Exchange API (write operations)
46
+ exchange = trading_sdk.exchange
38
47
  ```
39
48
 
40
49
  ### Supported APIs
@@ -287,6 +296,155 @@ details = sdk.info.token_details("0x00000000000000000000000000000000")
287
296
  # => { "name" => "TEST", "maxSupply" => "...", "midPx" => "...", ... }
288
297
  ```
289
298
 
299
+ #### Exchange Methods (Trading)
300
+
301
+ **Note:** Exchange methods require initializing the SDK with a `private_key`.
302
+
303
+ - `order(coin:, is_buy:, size:, limit_px:, ...)` - Place a single limit order
304
+ - `bulk_orders(orders:, grouping:, ...)` - Place multiple orders in a batch
305
+ - `market_order(coin:, is_buy:, size:, slippage:, ...)` - Place a market order with slippage
306
+ - `cancel(coin:, oid:, ...)` - Cancel an order by order ID
307
+ - `cancel_by_cloid(coin:, cloid:, ...)` - Cancel an order by client order ID
308
+ - `bulk_cancel(cancels:, ...)` - Cancel multiple orders by order ID
309
+ - `bulk_cancel_by_cloid(cancels:, ...)` - Cancel multiple orders by client order ID
310
+ - `address` - Get the wallet address associated with the private key
311
+
312
+ All exchange methods support an optional `vault_address:` parameter for vault trading.
313
+
314
+ ##### Examples: Exchange (Trading)
315
+
316
+ ```ruby
317
+ # Initialize SDK with private key for trading
318
+ sdk = Hyperliquid.new(
319
+ testnet: true,
320
+ private_key: ENV['HYPERLIQUID_PRIVATE_KEY']
321
+ )
322
+
323
+ # Get wallet address
324
+ address = sdk.exchange.address
325
+ # => "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
326
+
327
+ # Place a limit buy order
328
+ result = sdk.exchange.order(
329
+ coin: 'BTC',
330
+ is_buy: true,
331
+ size: '0.01',
332
+ limit_px: '95000',
333
+ order_type: { limit: { tif: 'Gtc' } } # Good-til-canceled (default)
334
+ )
335
+ # => { "status" => "ok", "response" => { "type" => "order", "data" => { "statuses" => [...] } } }
336
+
337
+ # Place a limit sell order with client order ID
338
+ cloid = Hyperliquid::Cloid.from_int(123) # Or Cloid.random
339
+ result = sdk.exchange.order(
340
+ coin: 'ETH',
341
+ is_buy: false,
342
+ size: '0.5',
343
+ limit_px: '3500',
344
+ cloid: cloid
345
+ )
346
+
347
+ # Place a market order (IoC with slippage)
348
+ result = sdk.exchange.market_order(
349
+ coin: 'BTC',
350
+ is_buy: true,
351
+ size: '0.01',
352
+ slippage: 0.03 # 3% slippage tolerance (default: 5%)
353
+ )
354
+
355
+ # Place multiple orders at once
356
+ orders = [
357
+ { coin: 'BTC', is_buy: true, size: '0.01', limit_px: '94000' },
358
+ { coin: 'BTC', is_buy: false, size: '0.01', limit_px: '96000' }
359
+ ]
360
+ result = sdk.exchange.bulk_orders(orders: orders)
361
+
362
+ # Cancel an order by order ID
363
+ oid = result.dig('response', 'data', 'statuses', 0, 'resting', 'oid')
364
+ sdk.exchange.cancel(coin: 'BTC', oid: oid)
365
+
366
+ # Cancel an order by client order ID
367
+ sdk.exchange.cancel_by_cloid(coin: 'ETH', cloid: cloid)
368
+
369
+ # Cancel multiple orders by order ID
370
+ cancels = [
371
+ { coin: 'BTC', oid: 12345 },
372
+ { coin: 'ETH', oid: 12346 }
373
+ ]
374
+ sdk.exchange.bulk_cancel(cancels: cancels)
375
+
376
+ # Cancel multiple orders by client order ID
377
+ cloid_cancels = [
378
+ { coin: 'BTC', cloid: Hyperliquid::Cloid.from_int(1) },
379
+ { coin: 'ETH', cloid: Hyperliquid::Cloid.from_int(2) }
380
+ ]
381
+ sdk.exchange.bulk_cancel_by_cloid(cancels: cloid_cancels)
382
+
383
+ # Vault trading (trade on behalf of a vault)
384
+ vault_address = '0x...'
385
+ sdk.exchange.order(
386
+ coin: 'BTC',
387
+ is_buy: true,
388
+ size: '1.0',
389
+ limit_px: '95000',
390
+ vault_address: vault_address
391
+ )
392
+ ```
393
+
394
+ **Order Types:**
395
+ - `{ limit: { tif: 'Gtc' } }` - Good-til-canceled (default)
396
+ - `{ limit: { tif: 'Ioc' } }` - Immediate-or-cancel
397
+ - `{ limit: { tif: 'Alo' } }` - Add-liquidity-only (post-only)
398
+
399
+ **Trigger Orders (Stop Loss / Take Profit):**
400
+ ```ruby
401
+ # Stop loss: Sell when price drops to trigger level
402
+ sdk.exchange.order(
403
+ coin: 'BTC',
404
+ is_buy: false,
405
+ size: '0.1',
406
+ limit_px: '89900',
407
+ order_type: {
408
+ trigger: {
409
+ trigger_px: 90_000,
410
+ is_market: true, # Execute as market order when triggered
411
+ tpsl: 'sl' # Stop loss
412
+ }
413
+ }
414
+ )
415
+
416
+ # Take profit: Sell when price rises to trigger level
417
+ sdk.exchange.order(
418
+ coin: 'BTC',
419
+ is_buy: false,
420
+ size: '0.1',
421
+ limit_px: '100100',
422
+ order_type: {
423
+ trigger: {
424
+ trigger_px: 100_000,
425
+ is_market: false, # Execute as limit order when triggered
426
+ tpsl: 'tp' # Take profit
427
+ }
428
+ }
429
+ )
430
+ ```
431
+
432
+ **Client Order IDs (Cloid):**
433
+ ```ruby
434
+ # Create from integer (zero-padded to 16 bytes)
435
+ cloid = Hyperliquid::Cloid.from_int(42)
436
+ # => "0x0000000000000000000000000000002a"
437
+
438
+ # Create from hex string
439
+ cloid = Hyperliquid::Cloid.from_str('0x1234567890abcdef1234567890abcdef')
440
+
441
+ # Create from UUID
442
+ cloid = Hyperliquid::Cloid.from_uuid('550e8400-e29b-41d4-a716-446655440000')
443
+
444
+ # Generate random
445
+ cloid = Hyperliquid::Cloid.random
446
+ ```
447
+
290
448
  ### Configuration
291
449
 
292
450
  ```ruby
@@ -296,12 +454,31 @@ sdk = Hyperliquid.new(timeout: 60)
296
454
  # Enable retry logic for handling transient failures (default: disabled)
297
455
  sdk = Hyperliquid.new(retry_enabled: true)
298
456
 
457
+ # Enable trading with a private key
458
+ sdk = Hyperliquid.new(private_key: ENV['HYPERLIQUID_PRIVATE_KEY'])
459
+
460
+ # Set global order expiration (orders expire after this timestamp)
461
+ expires_at_ms = (Time.now.to_f * 1000).to_i + 30_000 # 30 seconds from now
462
+ sdk = Hyperliquid.new(
463
+ private_key: ENV['HYPERLIQUID_PRIVATE_KEY'],
464
+ expires_after: expires_at_ms
465
+ )
466
+
299
467
  # Combine multiple configuration options
300
- sdk = Hyperliquid.new(testnet: true, timeout: 60, retry_enabled: true)
468
+ sdk = Hyperliquid.new(
469
+ testnet: true,
470
+ timeout: 60,
471
+ retry_enabled: true,
472
+ private_key: ENV['HYPERLIQUID_PRIVATE_KEY'],
473
+ expires_after: expires_at_ms
474
+ )
301
475
 
302
476
  # Check which environment you're using
303
477
  sdk.testnet? # => false
304
478
  sdk.base_url # => "https://api.hyperliquid.xyz"
479
+
480
+ # Check if exchange is available (private_key was provided)
481
+ sdk.exchange # => nil if no private_key, Hyperliquid::Exchange instance otherwise
305
482
  ```
306
483
 
307
484
  #### Retry Configuration
@@ -380,11 +557,11 @@ rake rubocop
380
557
 
381
558
  ## Roadmap
382
559
 
383
- The latest version implements read-only Info API support. Future versions will include:
560
+ The SDK now supports both Info API (read) and Exchange API (trading). Future versions will include:
384
561
 
385
- - Trading API (place orders, cancel orders, etc.)
386
562
  - WebSocket support for real-time data
387
- - Advanced trading features
563
+ - Additional exchange operations (leverage, margin adjustments, transfers)
564
+ - Advanced trading features (TWAP, etc.)
388
565
 
389
566
  ## Contributing
390
567