hyperliquid 1.9.0 → 2.0.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +63 -0
  3. data/README.md +26 -0
  4. data/docs/API.md +374 -152
  5. data/docs/CONFIGURATION.md +25 -11
  6. data/docs/DEVELOPMENT.md +93 -15
  7. data/docs/ERRORS.md +2 -0
  8. data/docs/EXAMPLES.md +253 -2
  9. data/docs/WS.md +78 -20
  10. data/lib/hyperliquid/client.rb +19 -10
  11. data/lib/hyperliquid/exchange.rb +844 -22
  12. data/lib/hyperliquid/info.rb +23 -11
  13. data/lib/hyperliquid/signing/signer.rb +18 -1
  14. data/lib/hyperliquid/version.rb +1 -1
  15. data/lib/hyperliquid/ws/client.rb +192 -78
  16. data/lib/hyperliquid.rb +6 -0
  17. metadata +16 -34
  18. data/.rspec +0 -3
  19. data/.rubocop.yml +0 -49
  20. data/.ruby-version +0 -1
  21. data/CLAUDE.md +0 -104
  22. data/CODE_OF_CONDUCT.md +0 -132
  23. data/Rakefile +0 -12
  24. data/example.rb +0 -161
  25. data/scripts/test_01_spot_market_roundtrip.rb +0 -48
  26. data/scripts/test_02_spot_limit_order.rb +0 -48
  27. data/scripts/test_03_perp_market_roundtrip.rb +0 -58
  28. data/scripts/test_04_perp_limit_order.rb +0 -52
  29. data/scripts/test_05_update_leverage.rb +0 -50
  30. data/scripts/test_06_modify_order.rb +0 -67
  31. data/scripts/test_07_market_close.rb +0 -55
  32. data/scripts/test_08_usd_class_transfer.rb +0 -36
  33. data/scripts/test_09_sub_account_lifecycle.rb +0 -51
  34. data/scripts/test_10_vault.rb +0 -68
  35. data/scripts/test_11_builder_fee.rb +0 -81
  36. data/scripts/test_12_staking.rb +0 -70
  37. data/scripts/test_13_ws_l2_book.rb +0 -83
  38. data/scripts/test_14_ws_candle.rb +0 -81
  39. data/scripts/test_15_explorer.rb +0 -71
  40. data/scripts/test_16_send_to_evm_with_data.rb +0 -79
  41. data/scripts/test_17_create_vault.rb +0 -73
  42. data/scripts/test_18_user_portfolio_margin.rb +0 -72
  43. data/scripts/test_19_spot_user.rb +0 -55
  44. data/scripts/test_20_explorer_ws.rb +0 -92
  45. data/scripts/test_all.rb +0 -96
  46. data/scripts/test_automated.rb +0 -82
  47. data/scripts/test_helpers.rb +0 -238
  48. data/sig/hyperliquid.rbs +0 -4
  49. data/test_integration.rb +0 -17
@@ -1,81 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Test 14: WebSocket candle Subscription
5
- #
6
- # Subscribes to ETH 1m candles on testnet, prints 3 updates
7
- # with OHLCV data, then cleanly disconnects.
8
- #
9
- # No private key required (read-only WebSocket).
10
- #
11
- # Usage:
12
- # ruby scripts/test_14_ws_candle.rb
13
-
14
- require_relative '../lib/hyperliquid'
15
-
16
- def green(text)
17
- "\e[32m#{text}\e[0m"
18
- end
19
-
20
- def red(text)
21
- "\e[31m#{text}\e[0m"
22
- end
23
-
24
- puts
25
- puts '=' * 60
26
- puts 'TEST 14: WebSocket candle Subscription'
27
- puts '=' * 60
28
- puts
29
- puts 'Network: Testnet'
30
- puts 'Subscribing to ETH 1m candles (3 updates, then disconnect)'
31
- puts
32
-
33
- sdk = Hyperliquid.new(testnet: true)
34
- updates = []
35
- mutex = Mutex.new
36
- done = ConditionVariable.new
37
-
38
- sdk.ws.on(:open) { puts 'WebSocket connected.' }
39
- sdk.ws.on(:error) { |e| puts red("WebSocket error: #{e}") }
40
-
41
- sdk.ws.subscribe({ type: 'candle', coin: 'ETH', interval: '1m' }) do |data|
42
- mutex.synchronize do
43
- next if updates.length >= 3
44
-
45
- update_num = updates.length + 1
46
- puts "Update #{update_num}/3:"
47
- puts " Symbol: #{data['s']}"
48
- puts " Interval: #{data['i']}"
49
- puts " Open: #{data['o']}"
50
- puts " High: #{data['h']}"
51
- puts " Low: #{data['l']}"
52
- puts " Close: #{data['c']}"
53
- puts " Volume: #{data['v']}"
54
- puts
55
-
56
- updates << data
57
- done.signal if updates.length >= 3
58
- end
59
- end
60
-
61
- # Wait for 3 updates or timeout after 90 seconds (candles can be slow)
62
- success = false
63
- mutex.synchronize do
64
- deadline = Time.now + 90
65
- while updates.length < 3 && Time.now < deadline
66
- remaining = deadline - Time.now
67
- break if remaining <= 0
68
-
69
- done.wait(mutex, remaining)
70
- end
71
- success = updates.length >= 3
72
- end
73
-
74
- sdk.ws.close
75
-
76
- if success
77
- puts green('Test 14 WebSocket candle passed!')
78
- else
79
- puts red("Test 14 FAILED: only received #{updates.length}/3 updates within 90s")
80
- exit 1
81
- end
@@ -1,71 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Test 15: Explorer RPC (tx_details + user_details)
5
- #
6
- # Calls the explorer RPC for the wallet's recent transactions:
7
- # 1. user_details(wallet_address) -> list of recent txs
8
- # 2. tx_details(first tx hash) -> full details of one tx
9
- #
10
- # Requires HYPERLIQUID_PRIVATE_KEY (used to derive wallet address; no signing).
11
- # A wallet with no testnet activity at all will produce a soft warning rather
12
- # than fail — explorer testnet endpoint occasionally has its own state quirks.
13
- #
14
- # Usage:
15
- # HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_15_explorer.rb
16
-
17
- require_relative 'test_helpers'
18
-
19
- separator('TEST 15: Explorer RPC (tx_details + user_details)')
20
-
21
- sdk = build_sdk
22
-
23
- begin
24
- user_details_resp = sdk.info.user_details(sdk.exchange.address)
25
- rescue Hyperliquid::Error => e
26
- puts red("user_details FAILED: #{e.class}: #{e.message}")
27
- exit 1
28
- end
29
-
30
- unless user_details_resp.is_a?(Hash) && user_details_resp['type'] == 'userDetails'
31
- puts red("user_details: unexpected response shape: #{user_details_resp.inspect}")
32
- exit 1
33
- end
34
-
35
- txs = user_details_resp['txs'] || []
36
- puts green("user_details OK: #{txs.length} txs returned")
37
-
38
- if txs.empty?
39
- puts 'No txs on this wallet — skipping tx_details lookup.'
40
- test_passed('Test 15 explorer RPC')
41
- exit 0
42
- end
43
-
44
- first_hash = txs.first['hash']
45
- unless first_hash.is_a?(String) && first_hash.start_with?('0x')
46
- puts red("First tx hash missing or malformed: #{first_hash.inspect}")
47
- exit 1
48
- end
49
-
50
- puts "Looking up tx_details for #{first_hash}..."
51
- begin
52
- tx_resp = sdk.info.tx_details(first_hash)
53
- rescue Hyperliquid::Error => e
54
- puts red("tx_details FAILED: #{e.class}: #{e.message}")
55
- exit 1
56
- end
57
-
58
- unless tx_resp.is_a?(Hash) && tx_resp['type'] == 'txDetails'
59
- puts red("tx_details: unexpected response shape: #{tx_resp.inspect}")
60
- exit 1
61
- end
62
-
63
- tx = tx_resp['tx']
64
- unless tx.is_a?(Hash) && tx['hash'] == first_hash
65
- puts red("tx_details: hash mismatch or missing tx: #{tx.inspect}")
66
- exit 1
67
- end
68
-
69
- puts green("tx_details OK: action=#{tx.dig('action', 'type')} time=#{tx['time']}")
70
-
71
- test_passed('Test 15 explorer RPC')
@@ -1,79 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Test 16: sendToEvmWithData (user-signed exchange action)
5
- #
6
- # Mirrors the TS SDK's `tests/api/exchange/sendToEvmWithData.test.ts`:
7
- # 1. Top up spot USDC by 2 (via usd_class_transfer perp -> spot).
8
- # 2. Submit one real sendToEvmWithData action sending 1 USDC to 0x...01
9
- # on HyperEVM testnet (chain 998) with empty calldata.
10
- # 3. Assert response is { status: 'ok', response: { type: 'default' } }.
11
- #
12
- # This funds-moving test sends 1 testnet USDC to 0x...01 (effectively burned —
13
- # no contract is there to redirect it). Same bar the TS SDK uses for its own
14
- # test suite.
15
- #
16
- # Skips with a warning if the wallet is on unified account (usd_class_transfer
17
- # is disabled in that mode and the top-up cannot run).
18
- #
19
- # Usage:
20
- # HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_16_send_to_evm_with_data.rb
21
-
22
- require_relative 'test_helpers'
23
-
24
- sdk = build_sdk
25
- separator('TEST 16: sendToEvmWithData')
26
-
27
- abstraction = sdk.info.user_abstraction(sdk.exchange.address)
28
- if abstraction == 'unifiedAccount'
29
- puts "SKIPPED: Wallet has unified account active (abstraction=#{abstraction.inspect})."
30
- puts ' usd_class_transfer top-up step is disabled by the exchange when balances are unified.'
31
- test_passed('Test 16 sendToEvmWithData')
32
- exit 0
33
- end
34
-
35
- puts 'Topping up spot USDC by $2 (perp -> spot)...'
36
- top_up = sdk.exchange.usd_class_transfer(amount: '2', to_perp: false)
37
- dump_status(top_up)
38
- if api_error?(top_up)
39
- puts red('Top-up failed; aborting before sendToEvmWithData.')
40
- test_passed('Test 16 sendToEvmWithData')
41
- exit 1
42
- end
43
- puts green('Top-up successful.')
44
- puts
45
-
46
- wait_with_countdown(WAIT_SECONDS, 'Waiting before sendToEvmWithData...')
47
-
48
- puts 'Sending 1 USDC to 0x...01 on HyperEVM testnet (chain 998) with empty calldata...'
49
- result = sdk.exchange.send_to_evm_with_data(
50
- token: 'USDC',
51
- amount: '1',
52
- source_dex: 'spot',
53
- destination_recipient: '0x0000000000000000000000000000000000000001',
54
- address_encoding: 'hex',
55
- destination_chain_id: 998,
56
- gas_limit: 200_000,
57
- data: '0x'
58
- )
59
-
60
- if api_error?(result)
61
- puts red("sendToEvmWithData FAILED: #{result.inspect}")
62
- test_passed('Test 16 sendToEvmWithData')
63
- exit 1
64
- end
65
-
66
- unless result.is_a?(Hash) && result['status'] == 'ok'
67
- $test_failed = true
68
- puts red("Unexpected status: #{result.inspect}")
69
- end
70
-
71
- response_type = result.dig('response', 'type')
72
- unless response_type == 'default'
73
- $test_failed = true
74
- puts red("Expected response.type 'default', got: #{response_type.inspect}")
75
- end
76
-
77
- puts green("sendToEvmWithData OK: status=ok, response.type=#{response_type}") unless $test_failed
78
-
79
- test_passed('Test 16 sendToEvmWithData')
@@ -1,73 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Test 17: createVault (L1 exchange action)
5
- #
6
- # Creates a new vault with the calling wallet as leader, using a $100 testnet
7
- # USDC seed (server-enforced minimum). On success, prints the new vault address
8
- # from response.data.
9
- #
10
- # WARNING: This is a real testnet action. The $100 seed is locked into the
11
- # created vault per Hyperliquid's vault lockup rules. Run intentionally —
12
- # do NOT wire into test_automated.rb.
13
- #
14
- # Skips with a warning if the wallet has < $100 perp USDC available.
15
- #
16
- # Usage:
17
- # HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_17_create_vault.rb
18
-
19
- require_relative 'test_helpers'
20
-
21
- sdk = build_sdk
22
- separator('TEST 17: createVault')
23
-
24
- state = sdk.info.user_state(sdk.exchange.address)
25
- withdrawable = state['withdrawable'].to_f
26
- puts "Wallet: #{sdk.exchange.address}"
27
- puts "Withdrawable: $#{format('%.2f', withdrawable)}"
28
- puts
29
-
30
- if withdrawable < 100
31
- puts red("SKIPPED: Need >= $100 perp USDC to seed a vault (have $#{format('%.2f', withdrawable)}).")
32
- test_passed('Test 17 createVault')
33
- exit 0
34
- end
35
-
36
- vault_name = "AgentVault#{Time.now.to_i}"
37
- vault_description = 'Vault created by hyperliquid-run integration test (test_17).'
38
-
39
- puts "Creating vault \"#{vault_name}\" with $100 seed..."
40
- result = sdk.exchange.create_vault(
41
- name: vault_name,
42
- description: vault_description,
43
- initial_usd: 100
44
- )
45
-
46
- if api_error?(result)
47
- puts red("createVault FAILED: #{result.inspect}")
48
- test_passed('Test 17 createVault')
49
- exit 1
50
- end
51
-
52
- unless result.is_a?(Hash) && result['status'] == 'ok'
53
- $test_failed = true
54
- puts red("Unexpected status: #{result.inspect}")
55
- end
56
-
57
- response_type = result.dig('response', 'type')
58
- unless response_type == 'createVault'
59
- $test_failed = true
60
- puts red("Expected response.type 'createVault', got: #{response_type.inspect}")
61
- end
62
-
63
- vault_address = result.dig('response', 'data')
64
- unless vault_address.is_a?(String) && vault_address.start_with?('0x') && vault_address.length == 42
65
- $test_failed = true
66
- puts red("Expected response.data to be a 0x-prefixed 40-hex-char vault address, got: #{vault_address.inspect}")
67
- end
68
-
69
- unless $test_failed
70
- puts green("createVault OK: status=ok, response.type=#{response_type}, vault=#{vault_address}")
71
- end
72
-
73
- test_passed('Test 17 createVault')
@@ -1,72 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Test 18: userPortfolioMargin (user-signed exchange action)
5
- #
6
- # Toggles cross-portfolio-margin mode for the calling wallet. Sends two actions:
7
- # enable, then disable, leaving the wallet in its original state.
8
- #
9
- # WARNING: Toggling portfolio margin alters margining math on existing perp positions.
10
- # Run only on a wallet without significant open exposure on testnet.
11
- #
12
- # Usage:
13
- # HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_18_user_portfolio_margin.rb
14
-
15
- require_relative 'test_helpers'
16
-
17
- sdk = build_sdk
18
- separator('TEST 18: userPortfolioMargin')
19
-
20
- user = sdk.exchange.address
21
- puts "Wallet: #{user}"
22
- puts
23
-
24
- puts 'Enabling portfolio margin...'
25
- result = sdk.exchange.user_portfolio_margin(user: user, enabled: true)
26
-
27
- # The protocol enforces a $10k account value or $5m volume threshold for portfolio
28
- # margin eligibility. The agent testnet wallet typically meets neither, so this
29
- # precondition failure is not an SDK bug — downgrade to warning and exit cleanly,
30
- # matching the test_08 / test_11 pattern.
31
- if result.is_a?(Hash) && result['status'] == 'err' &&
32
- result['response'].to_s.include?('Portfolio margin requires')
33
- puts red("WARNING: #{result['response']}")
34
- puts ' Skipping — this is a testnet precondition, not an SDK failure.'
35
- puts ' The action correctly serialized and signed (server returned a structured'
36
- puts ' rejection, not a signing error).'
37
- test_passed('Test 18 user_portfolio_margin')
38
- exit 0
39
- end
40
-
41
- if api_error?(result)
42
- puts red("user_portfolio_margin (enable) FAILED: #{result.inspect}")
43
- test_passed('Test 18 user_portfolio_margin')
44
- exit 1
45
- end
46
-
47
- unless result.is_a?(Hash) && result['status'] == 'ok' && result.dig('response', 'type') == 'default'
48
- $test_failed = true
49
- puts red("Unexpected enable response: #{result.inspect}")
50
- end
51
-
52
- puts green("Enable OK: #{result.inspect}") unless $test_failed
53
-
54
- wait_with_countdown(WAIT_SECONDS, 'Settling before toggle back...')
55
-
56
- puts 'Disabling portfolio margin...'
57
- result = sdk.exchange.user_portfolio_margin(user: user, enabled: false)
58
-
59
- if api_error?(result)
60
- puts red("user_portfolio_margin (disable) FAILED: #{result.inspect}")
61
- test_passed('Test 18 user_portfolio_margin')
62
- exit 1
63
- end
64
-
65
- unless result.is_a?(Hash) && result['status'] == 'ok' && result.dig('response', 'type') == 'default'
66
- $test_failed = true
67
- puts red("Unexpected disable response: #{result.inspect}")
68
- end
69
-
70
- puts green("Disable OK: #{result.inspect}") unless $test_failed
71
-
72
- test_passed('Test 18 user_portfolio_margin')
@@ -1,55 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Test 19: spotUser (L1 exchange action)
5
- #
6
- # Toggles spot-dusting opt-out for the calling wallet. Sends two actions:
7
- # opt_out: true, then opt_out: false, leaving the wallet in its original state
8
- # (spot dusting opted-in by default).
9
- #
10
- # Usage:
11
- # HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_19_spot_user.rb
12
-
13
- require_relative 'test_helpers'
14
-
15
- sdk = build_sdk
16
- separator('TEST 19: spotUser (spot-dusting opt-out)')
17
-
18
- puts "Wallet: #{sdk.exchange.address}"
19
- puts
20
-
21
- puts 'Opting out of spot dusting...'
22
- result = sdk.exchange.spot_user(opt_out: true)
23
-
24
- if api_error?(result)
25
- puts red("spot_user (opt_out: true) FAILED: #{result.inspect}")
26
- test_passed('Test 19 spot_user')
27
- exit 1
28
- end
29
-
30
- unless result.is_a?(Hash) && result['status'] == 'ok' && result.dig('response', 'type') == 'default'
31
- $test_failed = true
32
- puts red("Unexpected opt-out response: #{result.inspect}")
33
- end
34
-
35
- puts green("Opt-out OK: #{result.inspect}") unless $test_failed
36
-
37
- wait_with_countdown(WAIT_SECONDS, 'Settling before toggle back...')
38
-
39
- puts 'Opting back in to spot dusting...'
40
- result = sdk.exchange.spot_user(opt_out: false)
41
-
42
- if api_error?(result)
43
- puts red("spot_user (opt_out: false) FAILED: #{result.inspect}")
44
- test_passed('Test 19 spot_user')
45
- exit 1
46
- end
47
-
48
- unless result.is_a?(Hash) && result['status'] == 'ok' && result.dig('response', 'type') == 'default'
49
- $test_failed = true
50
- puts red("Unexpected opt-in response: #{result.inspect}")
51
- end
52
-
53
- puts green("Opt-in OK: #{result.inspect}") unless $test_failed
54
-
55
- test_passed('Test 19 spot_user')
@@ -1,92 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Integration test for Explorer WebSocket subscription
5
- # Connects to testnet explorer WS, subscribes to explorerBlock, and collects 3 block events
6
- #
7
- # Usage: ruby test_20_explorer_ws.rb
8
-
9
- require_relative 'test_helpers'
10
-
11
- puts '=' * 70
12
- puts 'test_20: Explorer WebSocket subscription'
13
- puts '=' * 70
14
-
15
- # Initialize SDK with testnet
16
- sdk = Hyperliquid::SDK.new(testnet: true)
17
-
18
- puts "\n1. Testing explorer WebSocket connection and subscription..."
19
-
20
- blocks_received = []
21
- max_blocks = 3
22
- timeout_seconds = 60
23
- start_time = Time.now
24
-
25
- puts " Subscribing to explorerBlock channel (expecting #{max_blocks} blocks within #{timeout_seconds}s)..."
26
- puts " Explorer WS URL: wss://rpc.hyperliquid-testnet.xyz/ws"
27
-
28
- # Subscribe to explorer blocks
29
- sub_id = sdk.ws.subscribe_explorer_block do |block_array|
30
- # Explorer messages arrive as arrays
31
- block_array.each do |block|
32
- blocks_received << block
33
- elapsed = Time.now - start_time
34
- puts " Block ##{blocks_received.size}: height=#{block['height']} hash=#{block['hash']} " \
35
- "numTxs=#{block['numTxs']} proposer=#{block['proposer'][0..10]}... (#{elapsed.round(1)}s elapsed)"
36
-
37
- break if blocks_received.size >= max_blocks
38
- end
39
- end
40
-
41
- puts " Subscription ID: #{sub_id}"
42
-
43
- # Wait for blocks with timeout
44
- puts "\n2. Waiting for block events..."
45
- loop do
46
- break if blocks_received.size >= max_blocks
47
-
48
- elapsed = Time.now - start_time
49
- if elapsed > timeout_seconds
50
- puts "\n ⚠ Timeout: Only received #{blocks_received.size}/#{max_blocks} blocks after #{timeout_seconds}s"
51
- puts ' This may indicate testnet explorer is not producing blocks or WS connection issues'
52
- break
53
- end
54
-
55
- sleep 0.5
56
- end
57
-
58
- # Cleanup
59
- puts "\n3. Cleanup..."
60
- sdk.ws.unsubscribe(sub_id)
61
- puts " Unsubscribed from #{sub_id}"
62
-
63
- sdk.ws.close
64
- puts ' WebSocket connection closed'
65
-
66
- # Verify results
67
- puts "\n4. Verification..."
68
- if blocks_received.empty?
69
- puts ' ❌ FAIL: No blocks received'
70
- puts ' Explorer WebSocket may not be working on testnet, or connection failed'
71
- exit 1
72
- end
73
-
74
- puts " ✓ Received #{blocks_received.size} block(s)"
75
-
76
- # Verify block structure
77
- blocks_received.each_with_index do |block, idx|
78
- required_fields = %w[height hash numTxs proposer blockTime]
79
- missing = required_fields - block.keys
80
- unless missing.empty?
81
- puts " ⚠ Block ##{idx + 1} missing fields: #{missing.join(', ')}"
82
- end
83
- end
84
-
85
- if blocks_received.size >= max_blocks
86
- puts " ✓ PASS: Successfully received #{max_blocks} explorer blocks"
87
- exit 0
88
- else
89
- puts " ⚠ PARTIAL: Received #{blocks_received.size}/#{max_blocks} blocks"
90
- puts ' This is acceptable if testnet explorer is slow, but should be investigated'
91
- exit 0 # Don't fail the test suite for partial results
92
- end
data/scripts/test_all.rb DELETED
@@ -1,96 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Hyperliquid Ruby SDK - Testnet Integration Tests (Runner)
5
- #
6
- # Runs all integration test scripts in order.
7
- # Each script can also be run individually for debugging.
8
- #
9
- # Prerequisites:
10
- # - Testnet wallet with USDC balance
11
- # - Get testnet funds from: https://app.hyperliquid-testnet.xyz
12
- #
13
- # Usage:
14
- # HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_all.rb
15
- #
16
- # Run a single test:
17
- # HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_08_usd_class_transfer.rb
18
- #
19
- # Note: These scripts execute real trades on testnet. No real funds are at risk.
20
-
21
- SCRIPTS = [
22
- 'test_01_spot_market_roundtrip.rb',
23
- 'test_02_spot_limit_order.rb',
24
- 'test_03_perp_market_roundtrip.rb',
25
- 'test_04_perp_limit_order.rb',
26
- 'test_05_update_leverage.rb',
27
- 'test_06_modify_order.rb',
28
- 'test_07_market_close.rb',
29
- 'test_08_usd_class_transfer.rb',
30
- 'test_09_sub_account_lifecycle.rb',
31
- 'test_10_vault.rb',
32
- 'test_11_builder_fee.rb',
33
- 'test_12_staking.rb',
34
- 'test_13_ws_l2_book.rb',
35
- 'test_14_ws_candle.rb',
36
- 'test_15_explorer.rb',
37
- 'test_16_send_to_evm_with_data.rb',
38
- 'test_17_create_vault.rb',
39
- 'test_18_user_portfolio_margin.rb',
40
- 'test_19_spot_user.rb',
41
- 'test_20_explorer_ws.rb'
42
- ].freeze
43
-
44
- def green(text)
45
- "\e[32m#{text}\e[0m"
46
- end
47
-
48
- def red(text)
49
- "\e[31m#{text}\e[0m"
50
- end
51
-
52
- unless ENV['HYPERLIQUID_PRIVATE_KEY']
53
- puts red('Error: Set HYPERLIQUID_PRIVATE_KEY environment variable')
54
- puts 'Usage: HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_all.rb'
55
- exit 1
56
- end
57
-
58
- scripts_dir = __dir__
59
- passed = []
60
- failed = []
61
-
62
- SCRIPTS.each do |script|
63
- path = File.join(scripts_dir, script)
64
- puts
65
- puts '#' * 60
66
- puts "# Running: #{script}"
67
- puts '#' * 60
68
-
69
- success = system(RbConfig.ruby, path)
70
-
71
- if success
72
- passed << script
73
- else
74
- failed << script
75
- puts red("!!! #{script} exited with error !!!")
76
- end
77
- end
78
-
79
- puts
80
- puts '=' * 60
81
- puts 'INTEGRATION TEST SUMMARY'
82
- puts '=' * 60
83
- puts
84
- puts "Passed: #{passed.length}/#{SCRIPTS.length}"
85
- passed.each { |s| puts green(" [PASS] #{s}") }
86
- if failed.any?
87
- puts
88
- puts "Failed: #{failed.length}/#{SCRIPTS.length}"
89
- failed.each { |s| puts red(" [FAIL] #{s}") }
90
- end
91
- puts
92
- puts 'Check your testnet wallet for trade history:'
93
- puts 'https://app.hyperliquid-testnet.xyz'
94
- puts
95
-
96
- exit(failed.empty? ? 0 : 1)