hyperliquid 0.5.0 → 0.7.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.
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Test 3: Perp Market Roundtrip (BTC Long)
5
+ # Open a long BTC position, then close it.
6
+
7
+ require_relative 'test_helpers'
8
+
9
+ sdk = build_sdk
10
+ separator('TEST 3: Perp Market Roundtrip (BTC Long)')
11
+
12
+ perp_coin = 'BTC'
13
+ mids = sdk.info.all_mids
14
+ btc_price = mids[perp_coin]&.to_f
15
+
16
+ if btc_price&.positive?
17
+ meta = sdk.info.meta
18
+ btc_meta = meta['universe'].find { |a| a['name'] == perp_coin }
19
+ sz_decimals = btc_meta['szDecimals']
20
+
21
+ perp_size = (20.0 / btc_price).ceil(sz_decimals)
22
+
23
+ puts "#{perp_coin} mid: $#{btc_price.round(2)}"
24
+ puts "Size: #{perp_size} BTC (~$#{(perp_size * btc_price).round(2)})"
25
+ puts "Slippage: #{(PERP_SLIPPAGE * 100).to_i}%"
26
+ puts
27
+
28
+ puts 'Opening LONG position (market buy)...'
29
+ result = sdk.exchange.market_order(
30
+ coin: perp_coin,
31
+ is_buy: true,
32
+ size: perp_size,
33
+ slippage: PERP_SLIPPAGE
34
+ )
35
+ check_result(result, 'Long open')
36
+
37
+ wait_with_countdown(WAIT_SECONDS, 'Position open. Waiting before close...')
38
+
39
+ puts 'Closing LONG position (market sell)...'
40
+ result = sdk.exchange.market_order(
41
+ coin: perp_coin,
42
+ is_buy: false,
43
+ size: perp_size,
44
+ slippage: PERP_SLIPPAGE
45
+ )
46
+ check_result(result, 'Long close')
47
+ else
48
+ puts red("SKIPPED: Could not get #{perp_coin} price")
49
+ end
50
+
51
+ test_passed('Test 3 Perp Market Roundtrip')
52
+
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Test 4: Perp Limit Order (Short, then Cancel)
5
+ # Place a limit sell well above market, then cancel it.
6
+
7
+ require_relative 'test_helpers'
8
+
9
+ sdk = build_sdk
10
+ separator('TEST 4: Perp Limit Order (Short, then Cancel)')
11
+
12
+ perp_coin = 'BTC'
13
+ mids = sdk.info.all_mids
14
+ btc_price = mids[perp_coin]&.to_f
15
+
16
+ if btc_price&.positive?
17
+ meta = sdk.info.meta
18
+ btc_meta = meta['universe'].find { |a| a['name'] == perp_coin }
19
+ sz_decimals = btc_meta['szDecimals']
20
+
21
+ limit_price = (btc_price * 1.50).round(0).to_i
22
+ perp_size = (20.0 / btc_price).ceil(sz_decimals)
23
+
24
+ puts "#{perp_coin} mid: $#{btc_price.round(2)}"
25
+ puts "Limit price: $#{limit_price} (50% above mid - won't fill)"
26
+ puts "Size: #{perp_size} BTC"
27
+ puts
28
+
29
+ puts 'Placing limit SELL order (short)...'
30
+ result = sdk.exchange.order(
31
+ coin: perp_coin,
32
+ is_buy: false,
33
+ size: perp_size,
34
+ limit_px: limit_price,
35
+ order_type: { limit: { tif: 'Gtc' } },
36
+ reduce_only: false
37
+ )
38
+ oid = check_result(result, 'Limit short')
39
+
40
+ if oid.is_a?(Integer)
41
+ wait_with_countdown(WAIT_SECONDS, 'Order resting. Waiting before cancel...')
42
+
43
+ puts "Canceling order #{oid}..."
44
+ result = sdk.exchange.cancel(coin: perp_coin, oid: oid)
45
+ check_result(result, 'Cancel')
46
+ end
47
+ else
48
+ puts red("SKIPPED: Could not get #{perp_coin} price")
49
+ end
50
+
51
+ test_passed('Test 4 Perp Limit Order')
52
+
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Test 5: Update Leverage (BTC)
5
+ # Set cross, isolated, then reset leverage.
6
+
7
+ require_relative 'test_helpers'
8
+
9
+ sdk = build_sdk
10
+ separator('TEST 5: Update Leverage (BTC)')
11
+
12
+ perp_coin = 'BTC'
13
+ mids = sdk.info.all_mids
14
+ btc_price = mids[perp_coin]&.to_f
15
+
16
+ if btc_price&.positive?
17
+ puts 'Setting BTC to 5x cross leverage...'
18
+ result = sdk.exchange.update_leverage(coin: perp_coin, leverage: 5, is_cross: true)
19
+ api_error?(result) || puts(green('5x cross leverage set'))
20
+ puts
21
+
22
+ wait_with_countdown(WAIT_SECONDS, 'Waiting before next leverage update...')
23
+
24
+ puts 'Setting BTC to 3x isolated leverage...'
25
+ result = sdk.exchange.update_leverage(coin: perp_coin, leverage: 3, is_cross: false)
26
+ api_error?(result) || puts(green('3x isolated leverage set'))
27
+ puts
28
+
29
+ wait_with_countdown(WAIT_SECONDS, 'Waiting before resetting leverage...')
30
+
31
+ puts 'Resetting BTC to 1x cross leverage...'
32
+ result = sdk.exchange.update_leverage(coin: perp_coin, leverage: 1, is_cross: true)
33
+ api_error?(result) || puts(green('1x cross leverage set'))
34
+ else
35
+ puts red("SKIPPED: Could not get #{perp_coin} price")
36
+ end
37
+
38
+ test_passed('Test 5 Update Leverage')
39
+
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Test 6: Modify Order (BTC)
5
+ # Place a limit buy, modify its price, then cancel.
6
+
7
+ require_relative 'test_helpers'
8
+
9
+ sdk = build_sdk
10
+ separator('TEST 6: Modify Order (BTC)')
11
+
12
+ perp_coin = 'BTC'
13
+ mids = sdk.info.all_mids
14
+ btc_price = mids[perp_coin]&.to_f
15
+
16
+ if btc_price&.positive?
17
+ meta = sdk.info.meta
18
+ btc_meta = meta['universe'].find { |a| a['name'] == perp_coin }
19
+ sz_decimals = btc_meta['szDecimals']
20
+
21
+ original_price = (btc_price * 0.50).round(0).to_i
22
+ modified_price = (btc_price * 0.51).round(0).to_i
23
+ perp_size = (20.0 / btc_price).ceil(sz_decimals)
24
+
25
+ puts "#{perp_coin} mid: $#{btc_price.round(2)}"
26
+ puts "Original limit: $#{original_price} (50% below mid)"
27
+ puts "Modified limit: $#{modified_price} (49% below mid)"
28
+ puts "Size: #{perp_size} BTC"
29
+ puts
30
+
31
+ puts 'Placing limit BUY order...'
32
+ result = sdk.exchange.order(
33
+ coin: perp_coin,
34
+ is_buy: true,
35
+ size: perp_size,
36
+ limit_px: original_price,
37
+ order_type: { limit: { tif: 'Gtc' } }
38
+ )
39
+ oid = check_result(result, 'Limit buy')
40
+
41
+ if oid.is_a?(Integer)
42
+ wait_with_countdown(WAIT_SECONDS, 'Order resting. Waiting before modify...')
43
+
44
+ puts "Modifying order #{oid} (price: $#{original_price} -> $#{modified_price})..."
45
+ result = sdk.exchange.modify_order(
46
+ oid: oid,
47
+ coin: perp_coin,
48
+ is_buy: true,
49
+ size: perp_size,
50
+ limit_px: modified_price
51
+ )
52
+ new_oid = check_result(result, 'Modify')
53
+ new_oid = oid unless new_oid.is_a?(Integer)
54
+ puts
55
+
56
+ wait_with_countdown(WAIT_SECONDS, 'Waiting before cancel...')
57
+
58
+ puts "Canceling modified order #{new_oid}..."
59
+ result = sdk.exchange.cancel(coin: perp_coin, oid: new_oid)
60
+ check_result(result, 'Cancel')
61
+ end
62
+ else
63
+ puts red("SKIPPED: Could not get #{perp_coin} price")
64
+ end
65
+
66
+ test_passed('Test 6 Modify Order')
67
+
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Test 7: Market Close (BTC)
5
+ # Open a long position, then close it using market_close (auto-detect size).
6
+
7
+ require_relative 'test_helpers'
8
+
9
+ sdk = build_sdk
10
+ separator('TEST 7: Market Close (BTC)')
11
+
12
+ perp_coin = 'BTC'
13
+ mids = sdk.info.all_mids
14
+ btc_price = mids[perp_coin]&.to_f
15
+
16
+ if btc_price&.positive?
17
+ meta = sdk.info.meta
18
+ btc_meta = meta['universe'].find { |a| a['name'] == perp_coin }
19
+ sz_decimals = btc_meta['szDecimals']
20
+
21
+ perp_size = (20.0 / btc_price).ceil(sz_decimals)
22
+
23
+ puts "#{perp_coin} mid: $#{btc_price.round(2)}"
24
+ puts "Size: #{perp_size} BTC"
25
+ puts
26
+
27
+ puts 'Opening LONG position (market buy)...'
28
+ result = sdk.exchange.market_order(
29
+ coin: perp_coin,
30
+ is_buy: true,
31
+ size: perp_size,
32
+ slippage: PERP_SLIPPAGE
33
+ )
34
+ check_result(result, 'Long open')
35
+
36
+ wait_with_countdown(WAIT_SECONDS, 'Position open. Waiting before market_close...')
37
+
38
+ puts 'Closing position using market_close (auto-detect size)...'
39
+ result = sdk.exchange.market_close(
40
+ coin: perp_coin,
41
+ slippage: PERP_SLIPPAGE
42
+ )
43
+ check_result(result, 'Market close')
44
+ else
45
+ puts red("SKIPPED: Could not get #{perp_coin} price")
46
+ end
47
+
48
+ test_passed('Test 7 Market Close')
49
+
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Test 8: USD Class Transfer (Perp <-> Spot)
5
+ # Transfer $10 from perp to spot, then back.
6
+
7
+ require_relative 'test_helpers'
8
+
9
+ sdk = build_sdk
10
+ separator('TEST 8: USD Class Transfer (Perp <-> Spot)')
11
+
12
+ puts 'Transferring $10 from perp to spot...'
13
+ result = sdk.exchange.usd_class_transfer(amount: '10', to_perp: false)
14
+ api_error?(result) || puts(green('Transfer to spot successful!'))
15
+ puts
16
+
17
+ wait_with_countdown(WAIT_SECONDS, 'Waiting before transferring back...')
18
+
19
+ puts 'Transferring $10 from spot to perp...'
20
+ result = sdk.exchange.usd_class_transfer(amount: '10', to_perp: true)
21
+ api_error?(result) || puts(green('Transfer to perp successful!'))
22
+
23
+ test_passed('Test 8 USD Class Transfer')
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Test 9: Sub Account Lifecycle
5
+ # Create a sub-account, deposit $10, then withdraw $10.
6
+
7
+ require_relative 'test_helpers'
8
+
9
+ sdk = build_sdk
10
+ separator('TEST 9: Sub Account Lifecycle')
11
+
12
+ puts 'Creating sub-account "ruby-sdk-test"...'
13
+ result = sdk.exchange.create_sub_account(name: 'ruby-sdk-test')
14
+
15
+ if api_error?(result)
16
+ exit 1
17
+ end
18
+
19
+ puts green('Sub-account created!')
20
+ puts
21
+
22
+ sub_account_address = result.dig('response', 'data', 'subAccountUser')
23
+ if sub_account_address
24
+ puts "Sub-account address: #{sub_account_address}"
25
+ puts
26
+
27
+ wait_with_countdown(WAIT_SECONDS, 'Waiting before deposit...')
28
+
29
+ puts 'Depositing $10 to sub-account...'
30
+ result = sdk.exchange.sub_account_transfer(
31
+ sub_account_user: sub_account_address,
32
+ is_deposit: true,
33
+ usd: 10
34
+ )
35
+ api_error?(result) || puts(green('Deposit successful!'))
36
+ puts
37
+
38
+ wait_with_countdown(WAIT_SECONDS, 'Waiting before withdrawal...')
39
+
40
+ puts 'Withdrawing $10 from sub-account...'
41
+ result = sdk.exchange.sub_account_transfer(
42
+ sub_account_user: sub_account_address,
43
+ is_deposit: false,
44
+ usd: 10
45
+ )
46
+ api_error?(result) || puts(green('Withdrawal successful!'))
47
+ else
48
+ puts red('SKIPPED: Could not extract sub-account address from response')
49
+ end
50
+
51
+ test_passed('Test 9 Sub Account Lifecycle')
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Test 10: Vault Status / Deposit / Withdraw
5
+ #
6
+ # Default: Show vault status (equity, entry date, unlock date)
7
+ # Options: ruby test_10_vault_transfer.rb deposit
8
+ # ruby test_10_vault_transfer.rb withdraw
9
+
10
+ require_relative 'test_helpers'
11
+
12
+ sdk = build_sdk
13
+ separator('TEST 10: Vault Status / Deposit / Withdraw')
14
+
15
+ vault_addr = '0xa15099a30bbf2e68942d6f4c43d70d04faeab0a0'
16
+ action = ARGV[0] # nil, "deposit", or "withdraw"
17
+
18
+ puts "Vault: #{vault_addr}"
19
+ puts
20
+
21
+ vault = sdk.info.vault_details(vault_addr, sdk.exchange.address)
22
+ follower = vault['followerState']
23
+
24
+ if follower
25
+ equity = follower['vaultEquity']
26
+ entry_time = follower['vaultEntryTime']
27
+ lockup_until = follower['lockupUntil']
28
+
29
+ puts "Vault equity: $#{equity}"
30
+ puts "Entry date: #{Time.at(entry_time / 1000.0).utc}" if entry_time
31
+ puts "Unlock date: #{Time.at(lockup_until / 1000.0).utc}" if lockup_until
32
+ else
33
+ puts 'No position in this vault.'
34
+ end
35
+ puts
36
+
37
+ case action
38
+ when 'deposit'
39
+ puts 'Depositing $10 to vault...'
40
+ result = sdk.exchange.vault_transfer(
41
+ vault_address: vault_addr,
42
+ is_deposit: true,
43
+ usd: 10
44
+ )
45
+ api_error?(result) || puts(green('Vault deposit successful!'))
46
+ when 'withdraw'
47
+ equity_f = follower&.dig('vaultEquity')&.to_f || 0
48
+ if equity_f > 1
49
+ withdraw_amount = equity_f.floor
50
+ puts "Withdrawing $#{withdraw_amount} from vault..."
51
+ result = sdk.exchange.vault_transfer(
52
+ vault_address: vault_addr,
53
+ is_deposit: false,
54
+ usd: withdraw_amount
55
+ )
56
+ api_error?(result) || puts(green('Vault withdrawal successful!'))
57
+ else
58
+ puts red("Insufficient vault equity to withdraw ($#{equity_f})")
59
+ end
60
+ else
61
+ puts 'Pass "deposit" or "withdraw" as an argument to perform a transfer.'
62
+ puts ' ruby scripts/test_10_vault_transfer.rb deposit'
63
+ puts ' ruby scripts/test_10_vault_transfer.rb withdraw'
64
+ end
65
+
66
+ test_passed('Test 10 Vault Status')
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Test 11: Builder Fee (Approve + Order with Builder)
5
+ # Approve a builder fee, then place an order with builder param, then cancel.
6
+
7
+ require_relative 'test_helpers'
8
+
9
+ sdk = build_sdk
10
+ separator('TEST 11: Builder Fee (Approve + Order with Builder)')
11
+
12
+ builder_address = '0x250F311Ae04D3CEA03443C76340069eD26C47D7D'
13
+ max_fee_rate = '0.01%'
14
+ perp_coin = 'BTC'
15
+
16
+ # Step 1: Approve builder fee
17
+ puts "Approving builder fee for #{builder_address} (max #{max_fee_rate})..."
18
+ result = sdk.exchange.approve_builder_fee(builder: builder_address, max_fee_rate: max_fee_rate)
19
+ api_error?(result) || puts(green('Builder fee approved'))
20
+ puts
21
+
22
+ wait_with_countdown(WAIT_SECONDS, 'Waiting before placing order with builder...')
23
+
24
+ # Step 2: Verify approval via Info API
25
+ puts 'Checking builder fee approval...'
26
+ approval = sdk.info.max_builder_fee(sdk.exchange.address, builder_address)
27
+ puts "Max builder fee: #{approval.inspect}"
28
+ puts
29
+
30
+ # Step 3: Place order with builder param
31
+ mids = sdk.info.all_mids
32
+ btc_price = mids[perp_coin]&.to_f
33
+
34
+ if btc_price&.positive?
35
+ meta = sdk.info.meta
36
+ btc_meta = meta['universe'].find { |a| a['name'] == perp_coin }
37
+ sz_decimals = btc_meta['szDecimals']
38
+
39
+ limit_price = (btc_price * 1.50).round(0).to_i
40
+ perp_size = (20.0 / btc_price).ceil(sz_decimals)
41
+
42
+ puts "#{perp_coin} mid: $#{btc_price.round(2)}"
43
+ puts "Limit price: $#{limit_price} (50% above mid - won't fill)"
44
+ puts "Size: #{perp_size} BTC"
45
+ puts "Builder: #{builder_address} (fee: 10 = 1bp)"
46
+ puts
47
+
48
+ puts 'Placing limit SELL order with builder fee...'
49
+ result = sdk.exchange.order(
50
+ coin: perp_coin,
51
+ is_buy: false,
52
+ size: perp_size,
53
+ limit_px: limit_price,
54
+ order_type: { limit: { tif: 'Gtc' } },
55
+ builder: { b: builder_address, f: 10 }
56
+ )
57
+ oid = check_result(result, 'Limit short with builder')
58
+
59
+ if oid.is_a?(Integer)
60
+ wait_with_countdown(WAIT_SECONDS, 'Order resting. Waiting before cancel...')
61
+
62
+ puts "Canceling order #{oid}..."
63
+ result = sdk.exchange.cancel(coin: perp_coin, oid: oid)
64
+ check_result(result, 'Cancel')
65
+ end
66
+ else
67
+ puts red("SKIPPED: Could not get #{perp_coin} price")
68
+ end
69
+
70
+ test_passed('Test 11 Builder Fee')
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Test 12: Staking Status / Delegate / Undelegate
5
+ #
6
+ # Default: Show staking summary and delegations
7
+ # Options: ruby test_12_staking.rb delegate
8
+ # ruby test_12_staking.rb undelegate
9
+
10
+ require_relative 'test_helpers'
11
+
12
+ sdk = build_sdk
13
+ separator('TEST 12: Staking Status / Delegate / Undelegate')
14
+
15
+ validator = '0x946bf3135c7d15e4462b510f74b6e304aabb5b21'
16
+ action = ARGV[0] # nil, "delegate", or "undelegate"
17
+ delegate_amount = 10_000_000 # 0.1 HYPE (wei = float * 1e8)
18
+
19
+ puts "Validator: #{validator}"
20
+ puts
21
+
22
+ # Show staking summary
23
+ summary = sdk.info.delegator_summary(sdk.exchange.address)
24
+ puts "Staking summary:"
25
+ puts " Delegated: #{summary['delegated']}" if summary['delegated']
26
+ puts " Undelegatable: #{summary['undelegatable']}" if summary['undelegatable']
27
+ puts " Total pending: #{summary['totalPending']}" if summary['totalPending']
28
+ puts " N delegations: #{summary['nDelegations']}" if summary['nDelegations']
29
+ puts
30
+
31
+ # Show delegations for this validator
32
+ delegations = sdk.info.delegations(sdk.exchange.address)
33
+ validator_delegation = delegations&.find { |d| d['validator']&.downcase == validator.downcase }
34
+
35
+ if validator_delegation
36
+ puts "Delegation to #{validator}:"
37
+ puts " Amount: #{validator_delegation['amount']}"
38
+ lockup = validator_delegation['lockedUntilTimestamp']
39
+ puts " Locked until: #{Time.at(lockup / 1000.0).utc}" if lockup
40
+ else
41
+ puts "No active delegation to #{validator}."
42
+ end
43
+ puts
44
+
45
+ case action
46
+ when 'delegate'
47
+ puts "Delegating 0.1 HYPE to #{validator}..."
48
+ result = sdk.exchange.token_delegate(
49
+ validator: validator,
50
+ wei: delegate_amount,
51
+ is_undelegate: false
52
+ )
53
+ api_error?(result) || puts(green('Delegation successful!'))
54
+ when 'undelegate'
55
+ puts "Undelegating 0.1 HYPE from #{validator}..."
56
+ result = sdk.exchange.token_delegate(
57
+ validator: validator,
58
+ wei: delegate_amount,
59
+ is_undelegate: true
60
+ )
61
+ api_error?(result) || puts(green('Undelegation successful!'))
62
+ else
63
+ puts 'Pass "delegate" or "undelegate" as an argument to perform an action.'
64
+ puts ' ruby scripts/test_12_staking.rb delegate'
65
+ puts ' ruby scripts/test_12_staking.rb undelegate'
66
+ end
67
+
68
+ test_passed('Test 12 Staking')
@@ -0,0 +1,88 @@
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
+ ].freeze
35
+
36
+ def green(text)
37
+ "\e[32m#{text}\e[0m"
38
+ end
39
+
40
+ def red(text)
41
+ "\e[31m#{text}\e[0m"
42
+ end
43
+
44
+ unless ENV['HYPERLIQUID_PRIVATE_KEY']
45
+ puts red('Error: Set HYPERLIQUID_PRIVATE_KEY environment variable')
46
+ puts 'Usage: HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_all.rb'
47
+ exit 1
48
+ end
49
+
50
+ scripts_dir = __dir__
51
+ passed = []
52
+ failed = []
53
+
54
+ SCRIPTS.each do |script|
55
+ path = File.join(scripts_dir, script)
56
+ puts
57
+ puts '#' * 60
58
+ puts "# Running: #{script}"
59
+ puts '#' * 60
60
+
61
+ success = system(RbConfig.ruby, path)
62
+
63
+ if success
64
+ passed << script
65
+ else
66
+ failed << script
67
+ puts red("!!! #{script} exited with error !!!")
68
+ end
69
+ end
70
+
71
+ puts
72
+ puts '=' * 60
73
+ puts 'INTEGRATION TEST SUMMARY'
74
+ puts '=' * 60
75
+ puts
76
+ puts "Passed: #{passed.length}/#{SCRIPTS.length}"
77
+ passed.each { |s| puts green(" [PASS] #{s}") }
78
+ if failed.any?
79
+ puts
80
+ puts "Failed: #{failed.length}/#{SCRIPTS.length}"
81
+ failed.each { |s| puts red(" [FAIL] #{s}") }
82
+ end
83
+ puts
84
+ puts 'Check your testnet wallet for trade history:'
85
+ puts 'https://app.hyperliquid-testnet.xyz'
86
+ puts
87
+
88
+ exit(failed.empty? ? 0 : 1)