hyperliquid 0.4.1 → 0.6.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 +4 -4
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +24 -2
- data/CLAUDE.md +6 -7
- data/README.md +4 -8
- data/SECURITY.md +7 -0
- data/docs/API.md +34 -1
- data/docs/DEVELOPMENT.md +31 -17
- data/docs/EXAMPLES.md +155 -0
- data/lib/hyperliquid/exchange.rb +379 -0
- data/lib/hyperliquid/signing/eip712.rb +73 -0
- data/lib/hyperliquid/signing/signer.rb +27 -2
- data/lib/hyperliquid/version.rb +1 -1
- data/scripts/test_01_spot_market_roundtrip.rb +48 -0
- data/scripts/test_02_spot_limit_order.rb +48 -0
- data/scripts/test_03_perp_market_roundtrip.rb +52 -0
- data/scripts/test_04_perp_limit_order.rb +52 -0
- data/scripts/test_05_update_leverage.rb +39 -0
- data/scripts/test_06_modify_order.rb +67 -0
- data/scripts/test_07_market_close.rb +49 -0
- data/scripts/test_08_usd_class_transfer.rb +23 -0
- data/scripts/test_09_sub_account_lifecycle.rb +51 -0
- data/scripts/test_10_vault_transfer.rb +41 -0
- data/scripts/test_all.rb +86 -0
- data/scripts/test_helpers.rb +100 -0
- data/test_integration.rb +8 -246
- metadata +14 -1
data/test_integration.rb
CHANGED
|
@@ -1,255 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Hyperliquid Ruby SDK - Testnet Integration
|
|
4
|
+
# Hyperliquid Ruby SDK - Testnet Integration Tests
|
|
5
5
|
#
|
|
6
|
-
# This
|
|
7
|
-
#
|
|
8
|
-
# 2. Spot limit order (place and cancel)
|
|
9
|
-
# 3. Perp market roundtrip (long BTC, close position)
|
|
10
|
-
# 4. Perp limit order (place short, cancel)
|
|
11
|
-
#
|
|
12
|
-
# Prerequisites:
|
|
13
|
-
# - Testnet wallet with USDC balance
|
|
14
|
-
# - Get testnet funds from: https://app.hyperliquid-testnet.xyz
|
|
6
|
+
# This is a convenience wrapper that runs all integration tests.
|
|
7
|
+
# Individual tests live in scripts/ and can be run standalone.
|
|
15
8
|
#
|
|
16
9
|
# Usage:
|
|
17
10
|
# HYPERLIQUID_PRIVATE_KEY=0x... ruby test_integration.rb
|
|
18
11
|
#
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
WAIT_SECONDS = 10
|
|
25
|
-
SPOT_SLIPPAGE = 0.40 # 40% for illiquid testnet spot markets
|
|
26
|
-
PERP_SLIPPAGE = 0.05 # 5% for perp markets
|
|
27
|
-
|
|
28
|
-
def separator(title)
|
|
29
|
-
puts
|
|
30
|
-
puts '=' * 60
|
|
31
|
-
puts title
|
|
32
|
-
puts '=' * 60
|
|
33
|
-
puts
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def wait_with_countdown(seconds, message)
|
|
37
|
-
puts message
|
|
38
|
-
seconds.downto(1) do |i|
|
|
39
|
-
print "\r #{i} seconds remaining... "
|
|
40
|
-
sleep 1
|
|
41
|
-
end
|
|
42
|
-
puts "\r Done! "
|
|
43
|
-
puts
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def check_result(result, operation)
|
|
47
|
-
status = result.dig('response', 'data', 'statuses', 0)
|
|
48
|
-
|
|
49
|
-
if status.is_a?(Hash) && status['error']
|
|
50
|
-
puts "FAILED: #{status['error']}"
|
|
51
|
-
return false
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
if status.is_a?(Hash) && status['resting']
|
|
55
|
-
puts "Order resting with OID: #{status['resting']['oid']}"
|
|
56
|
-
return status['resting']['oid']
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
if status == 'success' || (status.is_a?(Hash) && status['filled'])
|
|
60
|
-
puts "#{operation} successful!"
|
|
61
|
-
return true
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
puts "Result: #{status.inspect}"
|
|
65
|
-
true
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# --- Main Script ---
|
|
69
|
-
|
|
70
|
-
private_key = ENV['HYPERLIQUID_PRIVATE_KEY']
|
|
71
|
-
unless private_key
|
|
72
|
-
puts 'Error: Set HYPERLIQUID_PRIVATE_KEY environment variable'
|
|
73
|
-
puts 'Usage: HYPERLIQUID_PRIVATE_KEY=0x... ruby test_integration.rb'
|
|
74
|
-
exit 1
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
sdk = Hyperliquid.new(
|
|
78
|
-
testnet: true,
|
|
79
|
-
private_key: private_key
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
puts 'Hyperliquid Ruby SDK - Testnet Integration Test'
|
|
83
|
-
puts '=' * 60
|
|
84
|
-
puts "Wallet: #{sdk.exchange.address}"
|
|
85
|
-
puts 'Network: Testnet'
|
|
86
|
-
puts "Testnet UI: https://app.hyperliquid-testnet.xyz"
|
|
87
|
-
|
|
88
|
-
# ============================================================
|
|
89
|
-
# TEST 1: Spot Market Roundtrip (PURR/USDC)
|
|
90
|
-
# ============================================================
|
|
91
|
-
separator('TEST 1: Spot Market Roundtrip (PURR/USDC)')
|
|
92
|
-
|
|
93
|
-
spot_coin = 'PURR/USDC'
|
|
94
|
-
spot_size = 5 # PURR has 0 decimals
|
|
95
|
-
|
|
96
|
-
mids = sdk.info.all_mids
|
|
97
|
-
spot_price = mids[spot_coin]&.to_f
|
|
98
|
-
|
|
99
|
-
if spot_price&.positive?
|
|
100
|
-
puts "#{spot_coin} mid: $#{spot_price}"
|
|
101
|
-
puts "Size: #{spot_size} PURR (~$#{(spot_size * spot_price).round(2)})"
|
|
102
|
-
puts "Slippage: #{(SPOT_SLIPPAGE * 100).to_i}%"
|
|
103
|
-
puts
|
|
104
|
-
|
|
105
|
-
# Buy
|
|
106
|
-
puts 'Placing market BUY...'
|
|
107
|
-
result = sdk.exchange.market_order(
|
|
108
|
-
coin: spot_coin,
|
|
109
|
-
is_buy: true,
|
|
110
|
-
size: spot_size,
|
|
111
|
-
slippage: SPOT_SLIPPAGE
|
|
112
|
-
)
|
|
113
|
-
check_result(result, 'Buy')
|
|
114
|
-
|
|
115
|
-
wait_with_countdown(WAIT_SECONDS, 'Waiting before sell...')
|
|
116
|
-
|
|
117
|
-
# Sell
|
|
118
|
-
puts 'Placing market SELL...'
|
|
119
|
-
result = sdk.exchange.market_order(
|
|
120
|
-
coin: spot_coin,
|
|
121
|
-
is_buy: false,
|
|
122
|
-
size: spot_size,
|
|
123
|
-
slippage: SPOT_SLIPPAGE
|
|
124
|
-
)
|
|
125
|
-
check_result(result, 'Sell')
|
|
126
|
-
else
|
|
127
|
-
puts "SKIPPED: Could not get #{spot_coin} price"
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# ============================================================
|
|
131
|
-
# TEST 2: Spot Limit Order (Place and Cancel)
|
|
132
|
-
# ============================================================
|
|
133
|
-
separator('TEST 2: Spot Limit Order (Place and Cancel)')
|
|
134
|
-
|
|
135
|
-
if spot_price&.positive?
|
|
136
|
-
# Place limit buy well below market (won't fill)
|
|
137
|
-
limit_price = (spot_price * 0.50).round(2) # 50% below mid
|
|
138
|
-
puts "#{spot_coin} mid: $#{spot_price}"
|
|
139
|
-
puts "Limit price: $#{limit_price} (50% below mid - won't fill)"
|
|
140
|
-
puts "Size: #{spot_size} PURR"
|
|
141
|
-
puts
|
|
142
|
-
|
|
143
|
-
puts 'Placing limit BUY order...'
|
|
144
|
-
result = sdk.exchange.order(
|
|
145
|
-
coin: spot_coin,
|
|
146
|
-
is_buy: true,
|
|
147
|
-
size: spot_size,
|
|
148
|
-
limit_px: limit_price,
|
|
149
|
-
order_type: { limit: { tif: 'Gtc' } },
|
|
150
|
-
reduce_only: false
|
|
151
|
-
)
|
|
152
|
-
oid = check_result(result, 'Limit order')
|
|
153
|
-
|
|
154
|
-
if oid.is_a?(Integer)
|
|
155
|
-
wait_with_countdown(WAIT_SECONDS, 'Order resting. Waiting before cancel...')
|
|
156
|
-
|
|
157
|
-
puts "Canceling order #{oid}..."
|
|
158
|
-
result = sdk.exchange.cancel(coin: spot_coin, oid: oid)
|
|
159
|
-
check_result(result, 'Cancel')
|
|
160
|
-
end
|
|
161
|
-
else
|
|
162
|
-
puts "SKIPPED: Could not get #{spot_coin} price"
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# ============================================================
|
|
166
|
-
# TEST 3: Perp Market Roundtrip (BTC Long)
|
|
167
|
-
# ============================================================
|
|
168
|
-
separator('TEST 3: Perp Market Roundtrip (BTC Long)')
|
|
169
|
-
|
|
170
|
-
perp_coin = 'BTC'
|
|
171
|
-
btc_price = mids[perp_coin]&.to_f
|
|
172
|
-
|
|
173
|
-
if btc_price&.positive?
|
|
174
|
-
# Get BTC metadata for size precision
|
|
175
|
-
meta = sdk.info.meta
|
|
176
|
-
btc_meta = meta['universe'].find { |a| a['name'] == perp_coin }
|
|
177
|
-
sz_decimals = btc_meta['szDecimals']
|
|
178
|
-
|
|
179
|
-
# Calculate size for ~$20 notional
|
|
180
|
-
perp_size = (20.0 / btc_price).ceil(sz_decimals)
|
|
181
|
-
|
|
182
|
-
puts "#{perp_coin} mid: $#{btc_price.round(2)}"
|
|
183
|
-
puts "Size: #{perp_size} BTC (~$#{(perp_size * btc_price).round(2)})"
|
|
184
|
-
puts "Slippage: #{(PERP_SLIPPAGE * 100).to_i}%"
|
|
185
|
-
puts
|
|
186
|
-
|
|
187
|
-
# Open long
|
|
188
|
-
puts 'Opening LONG position (market buy)...'
|
|
189
|
-
result = sdk.exchange.market_order(
|
|
190
|
-
coin: perp_coin,
|
|
191
|
-
is_buy: true,
|
|
192
|
-
size: perp_size,
|
|
193
|
-
slippage: PERP_SLIPPAGE
|
|
194
|
-
)
|
|
195
|
-
check_result(result, 'Long open')
|
|
196
|
-
|
|
197
|
-
wait_with_countdown(WAIT_SECONDS, 'Position open. Waiting before close...')
|
|
198
|
-
|
|
199
|
-
# Close long (sell to close)
|
|
200
|
-
puts 'Closing LONG position (market sell)...'
|
|
201
|
-
result = sdk.exchange.market_order(
|
|
202
|
-
coin: perp_coin,
|
|
203
|
-
is_buy: false,
|
|
204
|
-
size: perp_size,
|
|
205
|
-
slippage: PERP_SLIPPAGE
|
|
206
|
-
)
|
|
207
|
-
check_result(result, 'Long close')
|
|
208
|
-
else
|
|
209
|
-
puts "SKIPPED: Could not get #{perp_coin} price"
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
# ============================================================
|
|
213
|
-
# TEST 4: Perp Limit Order (Short, then Cancel)
|
|
214
|
-
# ============================================================
|
|
215
|
-
separator('TEST 4: Perp Limit Order (Short, then Cancel)')
|
|
216
|
-
|
|
217
|
-
if btc_price&.positive?
|
|
218
|
-
# Place limit sell well above market (won't fill)
|
|
219
|
-
limit_price = (btc_price * 1.50).round(0).to_i # 50% above mid, whole number tick
|
|
220
|
-
perp_size = (20.0 / btc_price).ceil(sz_decimals)
|
|
221
|
-
|
|
222
|
-
puts "#{perp_coin} mid: $#{btc_price.round(2)}"
|
|
223
|
-
puts "Limit price: $#{limit_price} (50% above mid - won't fill)"
|
|
224
|
-
puts "Size: #{perp_size} BTC"
|
|
225
|
-
puts
|
|
226
|
-
|
|
227
|
-
puts 'Placing limit SELL order (short)...'
|
|
228
|
-
result = sdk.exchange.order(
|
|
229
|
-
coin: perp_coin,
|
|
230
|
-
is_buy: false,
|
|
231
|
-
size: perp_size,
|
|
232
|
-
limit_px: limit_price,
|
|
233
|
-
order_type: { limit: { tif: 'Gtc' } },
|
|
234
|
-
reduce_only: false
|
|
235
|
-
)
|
|
236
|
-
oid = check_result(result, 'Limit short')
|
|
237
|
-
|
|
238
|
-
if oid.is_a?(Integer)
|
|
239
|
-
wait_with_countdown(WAIT_SECONDS, 'Order resting. Waiting before cancel...')
|
|
240
|
-
|
|
241
|
-
puts "Canceling order #{oid}..."
|
|
242
|
-
result = sdk.exchange.cancel(coin: perp_coin, oid: oid)
|
|
243
|
-
check_result(result, 'Cancel')
|
|
244
|
-
end
|
|
245
|
-
else
|
|
246
|
-
puts "SKIPPED: Could not get #{perp_coin} price"
|
|
247
|
-
end
|
|
12
|
+
# Run a single test:
|
|
13
|
+
# HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_08_usd_class_transfer.rb
|
|
14
|
+
#
|
|
15
|
+
# See scripts/test_all.rb for the full list.
|
|
248
16
|
|
|
249
|
-
|
|
250
|
-
# Summary
|
|
251
|
-
# ============================================================
|
|
252
|
-
separator('INTEGRATION TEST COMPLETE')
|
|
253
|
-
puts 'All tests executed. Check your testnet wallet for trade history:'
|
|
254
|
-
puts 'https://app.hyperliquid-testnet.xyz'
|
|
255
|
-
puts
|
|
17
|
+
load File.join(__dir__, 'scripts', 'test_all.rb')
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hyperliquid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- carter2099
|
|
@@ -82,6 +82,7 @@ files:
|
|
|
82
82
|
- LICENSE.txt
|
|
83
83
|
- README.md
|
|
84
84
|
- Rakefile
|
|
85
|
+
- SECURITY.md
|
|
85
86
|
- docs/API.md
|
|
86
87
|
- docs/CONFIGURATION.md
|
|
87
88
|
- docs/DEVELOPMENT.md
|
|
@@ -98,6 +99,18 @@ files:
|
|
|
98
99
|
- lib/hyperliquid/signing/eip712.rb
|
|
99
100
|
- lib/hyperliquid/signing/signer.rb
|
|
100
101
|
- lib/hyperliquid/version.rb
|
|
102
|
+
- scripts/test_01_spot_market_roundtrip.rb
|
|
103
|
+
- scripts/test_02_spot_limit_order.rb
|
|
104
|
+
- scripts/test_03_perp_market_roundtrip.rb
|
|
105
|
+
- scripts/test_04_perp_limit_order.rb
|
|
106
|
+
- scripts/test_05_update_leverage.rb
|
|
107
|
+
- scripts/test_06_modify_order.rb
|
|
108
|
+
- scripts/test_07_market_close.rb
|
|
109
|
+
- scripts/test_08_usd_class_transfer.rb
|
|
110
|
+
- scripts/test_09_sub_account_lifecycle.rb
|
|
111
|
+
- scripts/test_10_vault_transfer.rb
|
|
112
|
+
- scripts/test_all.rb
|
|
113
|
+
- scripts/test_helpers.rb
|
|
101
114
|
- sig/hyperliquid.rbs
|
|
102
115
|
- test_integration.rb
|
|
103
116
|
homepage: https://github.com/carter2099/hyperliquid
|