hyperliquid 1.9.2 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +26 -0
- data/docs/API.md +374 -152
- data/docs/CONFIGURATION.md +25 -11
- data/docs/DEVELOPMENT.md +93 -15
- data/docs/ERRORS.md +2 -0
- data/docs/EXAMPLES.md +253 -2
- data/docs/WS.md +78 -20
- data/lib/hyperliquid/client.rb +19 -10
- data/lib/hyperliquid/exchange.rb +844 -22
- data/lib/hyperliquid/info.rb +23 -11
- data/lib/hyperliquid/signing/signer.rb +18 -1
- data/lib/hyperliquid/version.rb +1 -1
- data/lib/hyperliquid/ws/client.rb +192 -78
- data/lib/hyperliquid.rb +6 -0
- metadata +16 -34
- data/.rspec +0 -3
- data/.rubocop.yml +0 -49
- data/.ruby-version +0 -1
- data/CLAUDE.md +0 -108
- data/CODE_OF_CONDUCT.md +0 -132
- data/Rakefile +0 -12
- data/example.rb +0 -161
- data/scripts/test_01_spot_market_roundtrip.rb +0 -48
- data/scripts/test_02_spot_limit_order.rb +0 -48
- data/scripts/test_03_perp_market_roundtrip.rb +0 -58
- data/scripts/test_04_perp_limit_order.rb +0 -52
- data/scripts/test_05_update_leverage.rb +0 -50
- data/scripts/test_06_modify_order.rb +0 -67
- data/scripts/test_07_market_close.rb +0 -55
- data/scripts/test_08_usd_class_transfer.rb +0 -36
- data/scripts/test_09_sub_account_lifecycle.rb +0 -51
- data/scripts/test_10_vault.rb +0 -68
- data/scripts/test_11_builder_fee.rb +0 -81
- data/scripts/test_12_staking.rb +0 -70
- data/scripts/test_13_ws_l2_book.rb +0 -83
- data/scripts/test_14_ws_candle.rb +0 -81
- data/scripts/test_15_explorer.rb +0 -71
- data/scripts/test_16_send_to_evm_with_data.rb +0 -79
- data/scripts/test_17_create_vault.rb +0 -73
- data/scripts/test_18_user_portfolio_margin.rb +0 -72
- data/scripts/test_19_spot_user.rb +0 -55
- data/scripts/test_20_explorer_ws.rb +0 -92
- data/scripts/test_all.rb +0 -96
- data/scripts/test_automated.rb +0 -82
- data/scripts/test_helpers.rb +0 -238
- data/sig/hyperliquid.rbs +0 -4
- data/test_integration.rb +0 -17
metadata
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hyperliquid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- carter2099
|
|
8
|
-
bindir:
|
|
8
|
+
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: bigdecimal
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '3.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '3.1'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: eth
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -87,15 +101,9 @@ executables: []
|
|
|
87
101
|
extensions: []
|
|
88
102
|
extra_rdoc_files: []
|
|
89
103
|
files:
|
|
90
|
-
- ".rspec"
|
|
91
|
-
- ".rubocop.yml"
|
|
92
|
-
- ".ruby-version"
|
|
93
104
|
- CHANGELOG.md
|
|
94
|
-
- CLAUDE.md
|
|
95
|
-
- CODE_OF_CONDUCT.md
|
|
96
105
|
- LICENSE.txt
|
|
97
106
|
- README.md
|
|
98
|
-
- Rakefile
|
|
99
107
|
- SECURITY.md
|
|
100
108
|
- docs/API.md
|
|
101
109
|
- docs/CONFIGURATION.md
|
|
@@ -103,7 +111,6 @@ files:
|
|
|
103
111
|
- docs/ERRORS.md
|
|
104
112
|
- docs/EXAMPLES.md
|
|
105
113
|
- docs/WS.md
|
|
106
|
-
- example.rb
|
|
107
114
|
- lib/hyperliquid.rb
|
|
108
115
|
- lib/hyperliquid/client.rb
|
|
109
116
|
- lib/hyperliquid/cloid.rb
|
|
@@ -116,31 +123,6 @@ files:
|
|
|
116
123
|
- lib/hyperliquid/signing/signer.rb
|
|
117
124
|
- lib/hyperliquid/version.rb
|
|
118
125
|
- lib/hyperliquid/ws/client.rb
|
|
119
|
-
- scripts/test_01_spot_market_roundtrip.rb
|
|
120
|
-
- scripts/test_02_spot_limit_order.rb
|
|
121
|
-
- scripts/test_03_perp_market_roundtrip.rb
|
|
122
|
-
- scripts/test_04_perp_limit_order.rb
|
|
123
|
-
- scripts/test_05_update_leverage.rb
|
|
124
|
-
- scripts/test_06_modify_order.rb
|
|
125
|
-
- scripts/test_07_market_close.rb
|
|
126
|
-
- scripts/test_08_usd_class_transfer.rb
|
|
127
|
-
- scripts/test_09_sub_account_lifecycle.rb
|
|
128
|
-
- scripts/test_10_vault.rb
|
|
129
|
-
- scripts/test_11_builder_fee.rb
|
|
130
|
-
- scripts/test_12_staking.rb
|
|
131
|
-
- scripts/test_13_ws_l2_book.rb
|
|
132
|
-
- scripts/test_14_ws_candle.rb
|
|
133
|
-
- scripts/test_15_explorer.rb
|
|
134
|
-
- scripts/test_16_send_to_evm_with_data.rb
|
|
135
|
-
- scripts/test_17_create_vault.rb
|
|
136
|
-
- scripts/test_18_user_portfolio_margin.rb
|
|
137
|
-
- scripts/test_19_spot_user.rb
|
|
138
|
-
- scripts/test_20_explorer_ws.rb
|
|
139
|
-
- scripts/test_all.rb
|
|
140
|
-
- scripts/test_automated.rb
|
|
141
|
-
- scripts/test_helpers.rb
|
|
142
|
-
- sig/hyperliquid.rbs
|
|
143
|
-
- test_integration.rb
|
|
144
126
|
homepage: https://github.com/carter2099/hyperliquid
|
|
145
127
|
licenses:
|
|
146
128
|
- MIT
|
data/.rspec
DELETED
data/.rubocop.yml
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
AllCops:
|
|
2
|
-
NewCops: enable
|
|
3
|
-
TargetRubyVersion: 3.3
|
|
4
|
-
SuggestExtensions: false
|
|
5
|
-
Exclude:
|
|
6
|
-
- 'test_*.rb' # Exclude ad-hoc integration test scripts
|
|
7
|
-
- 'scripts/**/*' # Exclude integration test scripts
|
|
8
|
-
- 'local/**/*' # Exclude local test scripts
|
|
9
|
-
- 'vendor/**/*' # Exclude vendored gems (CI bundles here)
|
|
10
|
-
|
|
11
|
-
# Allow longer methods for complex logic
|
|
12
|
-
Metrics/MethodLength:
|
|
13
|
-
Max: 50
|
|
14
|
-
|
|
15
|
-
# Allow higher complexity for response handling
|
|
16
|
-
Metrics/AbcSize:
|
|
17
|
-
Max: 25
|
|
18
|
-
|
|
19
|
-
# Disable class length checks so rubocop doesn't complain that we implement all of Hyperliquid's endpoints
|
|
20
|
-
Metrics/ClassLength:
|
|
21
|
-
Enabled: false
|
|
22
|
-
|
|
23
|
-
# Allow longer blocks in specs and gemspec - this is normal for tests and gem configs
|
|
24
|
-
Metrics/BlockLength:
|
|
25
|
-
Exclude:
|
|
26
|
-
- 'spec/**/*'
|
|
27
|
-
- '*.gemspec'
|
|
28
|
-
|
|
29
|
-
# Exchange API methods (and multi-sig co-signer helpers that pass through to them) require many parameters
|
|
30
|
-
Metrics/ParameterLists:
|
|
31
|
-
Exclude:
|
|
32
|
-
- 'lib/hyperliquid/exchange.rb'
|
|
33
|
-
- 'lib/hyperliquid/signing/multi_sig.rb'
|
|
34
|
-
|
|
35
|
-
# Allow higher complexity for order type conversion logic and WS message handling
|
|
36
|
-
Metrics/CyclomaticComplexity:
|
|
37
|
-
Exclude:
|
|
38
|
-
- 'lib/hyperliquid/exchange.rb'
|
|
39
|
-
- 'lib/hyperliquid/ws/client.rb'
|
|
40
|
-
|
|
41
|
-
Metrics/PerceivedComplexity:
|
|
42
|
-
Exclude:
|
|
43
|
-
- 'lib/hyperliquid/exchange.rb'
|
|
44
|
-
- 'lib/hyperliquid/ws/client.rb'
|
|
45
|
-
|
|
46
|
-
# Empty blocks are intentional in specs (no-op callbacks for subscription tests)
|
|
47
|
-
Lint/EmptyBlock:
|
|
48
|
-
Exclude:
|
|
49
|
-
- 'spec/**/*'
|
data/.ruby-version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3.4.10
|
data/CLAUDE.md
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to AI coding agents working with this repository. It is the canonical source of truth — keep it in sync as the SDK evolves.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
Ruby SDK for the Hyperliquid decentralized exchange API. Three API surfaces: **Info** (read-only market data), **Exchange** (authenticated trading), and **WebSocket** (real-time streaming). Built on Faraday for HTTP, the `eth` gem for EIP-712 signing, `msgpack` for action serialization, and `ws_lite` for WebSocket connections.
|
|
8
|
-
|
|
9
|
-
Version is the single source of truth in `lib/hyperliquid/version.rb`; required Ruby version is in the gemspec.
|
|
10
|
-
|
|
11
|
-
## Commands
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
bin/setup # install dependencies
|
|
15
|
-
rake # run tests + linting (CI default)
|
|
16
|
-
rake spec # tests only
|
|
17
|
-
rake rubocop # linting only
|
|
18
|
-
bundle exec rspec spec/hyperliquid/cloid_spec.rb # single file
|
|
19
|
-
bundle exec rspec spec/hyperliquid/cloid_spec.rb:62 # single test by line
|
|
20
|
-
bin/console # IRB with SDK loaded
|
|
21
|
-
ruby example.rb # example usage script
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
### Integration Tests (Testnet)
|
|
25
|
-
|
|
26
|
-
Integration scripts live in `scripts/` as standalone files (`test_NN_<name>.rb`). They require a real testnet private key and hit the live testnet API.
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_all.rb # all 20
|
|
30
|
-
HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_automated.rb # CI-friendly subset (14)
|
|
31
|
-
HYPERLIQUID_PRIVATE_KEY=0x... ruby scripts/test_08_usd_class_transfer.rb # single
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
`test_automated.rb` is the unattended runner — same as `test_all.rb` but excludes scripts that require manual testnet preconditions (e.g. `test_09_sub_account_lifecycle` needs $100k traded volume; `test_12_staking` needs HYPE balance). Some included tests (e.g. `test_08`, `test_11`) are also coded to skip-with-warning when known testnet preconditions aren't met, so the suite stays green on a stable wallet.
|
|
35
|
-
|
|
36
|
-
`test_integration.rb` at the project root is a thin convenience wrapper.
|
|
37
|
-
|
|
38
|
-
## Architecture
|
|
39
|
-
|
|
40
|
-
### Request Flow
|
|
41
|
-
|
|
42
|
-
All three API surfaces are reached through `Hyperliquid::SDK` (`lib/hyperliquid.rb`):
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
Hyperliquid.new(...)
|
|
46
|
-
├── sdk.info → Info → Client → POST /info (always available)
|
|
47
|
-
├── sdk.exchange → Exchange → Client → POST /exchange (requires private_key)
|
|
48
|
-
└── sdk.ws → WS::Client → WSS /ws (real-time streaming)
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
- **Info path**: method builds `{ type: 'someType', ... }` body → `Client` POSTs to `/info` → parsed JSON returned.
|
|
52
|
-
- **Exchange path**: method builds action payload → `Signer` generates EIP-712 signature over msgpack-encoded action → `Client` POSTs signed payload to `/exchange` → parsed JSON returned.
|
|
53
|
-
- **Explorer RPC path** (`tx_details`, `user_details`): a separate base URL (`rpc.hyperliquid.xyz` / `rpc.hyperliquid-testnet.xyz`) with endpoint `/explorer`. `Client` holds a second Faraday connection for this, built lazily on first use; methods opt in via `client.post(EXPLORER_ENDPOINT, body, target: :explorer)`. The SDK wires this up automatically based on `testnet:`. Calling `target: :explorer` on a `Client` constructed without `explorer_base_url:` raises `ConfigurationError`. Don't add a public connection accessor — `target:` is the contract.
|
|
54
|
-
- **WebSocket path**: `WS::Client` manages two independent WebSocket connections:
|
|
55
|
-
- **Main API WS** (`wss://api.hyperliquid.xyz/ws`): subscribes to market data channels (`l2Book`, `trades`, `candle`, etc.). Messages arrive as `{channel, data}` envelopes; `compute_identifier` extracts routing keys (e.g. `l2Book:eth`, `candle:btc:1h`). Callbacks are dispatched via a bounded queue (1024, drops on overflow) on a dedicated thread.
|
|
56
|
-
- **Explorer WS** (`wss://rpc.hyperliquid.xyz/ws`): subscribes to block/transaction streams (`explorerBlock`, `explorerTxs`). Messages arrive as **bare arrays** (no envelope), so `identify_explorer_array` duck-types by field presence (`blockTime`/`height` for blocks, `action`/`hash` for txs). Uses a separate queue, dispatch thread, and subscription ID namespace to avoid cross-contamination with main-API WS. Both connections share the same 50s ping keepalive, automatic reconnection (exp backoff, 30s cap), and lifecycle hooks (`on(:open)`, `on(:close)`, `on(:error)`).
|
|
57
|
-
|
|
58
|
-
### Signing (Python SDK Parity)
|
|
59
|
-
|
|
60
|
-
The signing chain in `lib/hyperliquid/signing/` must exactly match the official Python SDK:
|
|
61
|
-
|
|
62
|
-
1. **Action hash**: `keccak256(msgpack(action) + nonce(8B big-endian) + vault_flag + [vault_addr] + [expires_flag + expires_after])`
|
|
63
|
-
2. **Phantom agent**: `{ source: 'a'|'b', connectionId: action_hash }` (`a`=mainnet, `b`=testnet)
|
|
64
|
-
3. **EIP-712 signature** over phantom agent with Exchange domain (chain ID 1337)
|
|
65
|
-
|
|
66
|
-
Any change to signing must maintain parity with the Python SDK or transactions will be rejected by the exchange.
|
|
67
|
-
|
|
68
|
-
User-signed actions (`usd_send`, `withdraw_from_bridge`, `send_to_evm_with_data`, etc.) use direct EIP-712 typed-data signing with the `HyperliquidSignTransaction` domain (chain ID 421614) — not the phantom-agent flow. Each has a typed-data spec in `Signing::EIP712`. The `eth` gem's typed-data signer handles primitive types (`string`, `uint*`, `address`, `bool`) and dynamic `bytes` correctly — `send_to_evm_with_data` was the first to use `bytes`, and its spec includes a fixture-based signature parity test against `eth_account` to lock that in. When adding new user-signed actions with non-string types, add a similar fixture to catch eth-gem regressions.
|
|
69
|
-
|
|
70
|
-
Multi-sig actions (`Exchange#multi_sig`) wrap any inner action with N co-signer signatures. The submitter's outer signature uses `MULTI_SIG_TYPES` over `{hyperliquidChain, multiSigActionHash, nonce}`; the `multiSigActionHash` is `Signer.compute_action_hash` of the multi-sig envelope (with `:type` stripped). Co-signer signing is exposed via `Signing::MultiSig.sign_as_co_signer_l1` (for L1 inner actions — signs `[multi_sig_user, outer_signer, action]` via phantom-agent) and `Signing::MultiSig.sign_as_co_signer_user_signed` (enriches the inner action's typed-data spec with `payloadMultiSigUser`+`outerSigner` address fields). Both mirror the Python SDK byte-for-byte; specs include fixture-based parity tests captured against `eth_account`+`msgpack`. Co-signature *collection* is the caller's responsibility — the SDK does not coordinate signing rooms.
|
|
71
|
-
|
|
72
|
-
### Numeric Conversion
|
|
73
|
-
|
|
74
|
-
- **`float_to_wire`** (in Exchange): converts to string with 8-decimal precision, validates rounding tolerance (`1e-12`), normalizes trailing zeros. No scientific notation.
|
|
75
|
-
- **Market order pricing** (`_slippage_price`): apply slippage (default 5%) to mid price → round to 5 significant figures → round to `(6 for perp, 8 for spot) - szDecimals` decimal places.
|
|
76
|
-
- **Spot vs perp**: assets with index `>= 10_000` are spot (`SPOT_ASSET_THRESHOLD` in Exchange). This affects decimal place calculations.
|
|
77
|
-
|
|
78
|
-
### HIP-3 (Builder-Deployed Perps)
|
|
79
|
-
|
|
80
|
-
Many Info methods accept a `dex:` kwarg (e.g. `meta(dex: 'foo')`, `user_state(user, dex: 'foo')`) to query a builder-deployed perp dex rather than the canonical perp market. `Info#perp_dexs` enumerates available dexes; `Info#perp_dex_limits(dex)` returns per-dex risk parameters.
|
|
81
|
-
|
|
82
|
-
### Testing
|
|
83
|
-
|
|
84
|
-
- **Unit tests** (`spec/`): RSpec + WebMock. WebMock resets between tests. Monkey-patching disabled. Test files mirror `lib/` structure. No live HTTP calls in unit tests. The WS client spec (`spec/hyperliquid/ws/client_spec.rb`) includes comprehensive isolation tests verifying that explorer WS messages never route to main-API callbacks and vice versa — this is critical because the two transports share the same `WS::Client` class.
|
|
85
|
-
- **Integration tests** (`scripts/`): run against testnet with a real private key. Each script is self-contained. Helpers (separators, status dumping, retry-on-oracle-bounce) live in `scripts/test_helpers.rb`. `test_20_explorer_ws.rb` subscribes to `explorerBlock` on testnet and collects 3 block events (60s timeout) to verify the explorer WS transport works end-to-end.
|
|
86
|
-
- **`dump_status` / `check_result` helpers** in `test_helpers.rb` must guard against `result['response']` *itself* being a String for transfer-style actions (`usdClassTransfer`, `approveBuilderFee`) — not just `result['response']['data']`. This was a real bug fixed in 1.1.0; preserve the guards if refactoring those helpers.
|
|
87
|
-
|
|
88
|
-
### Code Style
|
|
89
|
-
|
|
90
|
-
RuboCop targets Ruby 3.3. Key relaxations: methods up to 50 lines, no class length limit (Info/Exchange are large by design), no block length limit in specs, no parameter list limit in Exchange, empty blocks allowed in specs (intentional no-op callbacks). `scripts/`, `test_*.rb`, `local/`, and `vendor/` are excluded from linting. The WS client's `initialize` method was refactored to extract `init_main_ws_state` and `init_explorer_ws_state` helpers to reduce ABC size (33 assignments across two transports).
|
|
91
|
-
|
|
92
|
-
Predicate methods follow Ruby style (`vip?`, `connected?`, `testnet?`) — not `is_vip` / `is_connected`. RuboCop's `Naming/PredicateName` enforces this.
|
|
93
|
-
|
|
94
|
-
### CI
|
|
95
|
-
|
|
96
|
-
GitHub Actions (`.github/workflows/main.yml`): runs `bundle exec rake` (tests + lint) on the Ruby matrix defined in that workflow, for pushes to `main` and on all PRs. The release workflow creates GitHub releases from `CHANGELOG.md` on version tags. Dependabot checks Bundler and GitHub Actions dependencies weekly with a 14-day default cooldown; Bundler major updates use a 30-day cooldown.
|
|
97
|
-
|
|
98
|
-
### rbsecp256k1 git source
|
|
99
|
-
|
|
100
|
-
The `Gemfile` pins `rbsecp256k1` to Carter's fork (`carter2099/rbsecp256k1`, full-SHA `ref:`, version 6.1.0) because rubygems only has 6.0.0, which requires `rubyzip ~> 2.3` (GHSA-47m2-wp7j-p9vc); the fork requires `rubyzip >= 3.4, < 4` and builds libsecp256k1 0.8.0. This affects only the dev/CI lockfile — a gemspec cannot declare git sources, so consumers of the published gem still resolve rubygems rbsecp256k1 6.0.0 via `eth ~> 0.5`. Dependabot is not expected to bump a SHA-pinned git ref; a Dependabot PR touching rbsecp256k1's source/ref or moving rubyzip below 3.4 must not be applied without Carter. Never `bundle update` it or drop the declaration; moving the ref is a deliberate, reviewed change (re-run the signature-parity specs in `spec/hyperliquid/signing/`). Remove the git source once an upstream rbsecp256k1 release on rubygems allows rubyzip 3.
|
|
101
|
-
|
|
102
|
-
## Release Flow
|
|
103
|
-
|
|
104
|
-
Releases happen from `main`. Day-to-day work lands on `dev`, then `dev` is merged into `main` and the version commit is pushed. `CHANGELOG.md` follows Keep-a-Changelog conventions; `lib/hyperliquid/version.rb` is the single source of version truth (gemspec reads it). Releases are atomic: only after the `Ruby` workflow is green on main and the gem is built is the RubyGems OTP requested; `gem push` goes first and the `vX.Y.Z` tag is pushed only after it succeeds. The tag push triggers the GitHub release workflow, so a failed gem push leaves no tag and no GitHub Release.
|
|
105
|
-
|
|
106
|
-
## Additional Docs
|
|
107
|
-
|
|
108
|
-
Detailed API reference, examples, WebSocket guide, configuration, and error handling in `docs/` (`API.md`, `EXAMPLES.md`, `WS.md`, `CONFIGURATION.md`, `ERRORS.md`, `DEVELOPMENT.md`).
|
data/CODE_OF_CONDUCT.md
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
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/Rakefile
DELETED
data/example.rb
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
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
|
-
puts 'Hyperliquid Ruby SDK v0.4.0 - API Examples'
|
|
9
|
-
puts '=' * 50
|
|
10
|
-
|
|
11
|
-
# =============================================================================
|
|
12
|
-
# INFO API (Read-only, no authentication required)
|
|
13
|
-
# =============================================================================
|
|
14
|
-
|
|
15
|
-
# Create a new SDK instance (defaults to mainnet)
|
|
16
|
-
sdk = Hyperliquid.new
|
|
17
|
-
|
|
18
|
-
puts "\n--- INFO API EXAMPLES ---\n"
|
|
19
|
-
|
|
20
|
-
# Example 1: Get all market mid prices
|
|
21
|
-
begin
|
|
22
|
-
puts '1. Getting all market mid prices...'
|
|
23
|
-
mids = sdk.info.all_mids
|
|
24
|
-
puts " Found #{mids.length} markets" if mids.is_a?(Hash)
|
|
25
|
-
puts " BTC mid: #{mids['BTC']}" if mids['BTC']
|
|
26
|
-
rescue Hyperliquid::Error => e
|
|
27
|
-
puts " Error: #{e.message}"
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Example 2: Get metadata for all perpetual assets
|
|
31
|
-
begin
|
|
32
|
-
puts "\n2. Getting perpetual asset metadata..."
|
|
33
|
-
meta = sdk.info.meta
|
|
34
|
-
universe = meta['universe'] || []
|
|
35
|
-
puts " Found #{universe.length} perpetual assets"
|
|
36
|
-
puts " First asset: #{universe.first['name']}" if universe.any?
|
|
37
|
-
rescue Hyperliquid::Error => e
|
|
38
|
-
puts " Error: #{e.message}"
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Example 3: Get L2 order book
|
|
42
|
-
begin
|
|
43
|
-
puts "\n3. Getting L2 order book for BTC..."
|
|
44
|
-
book = sdk.info.l2_book('BTC')
|
|
45
|
-
levels = book['levels'] || []
|
|
46
|
-
puts " Bid levels: #{levels[0]&.length || 0}, Ask levels: #{levels[1]&.length || 0}"
|
|
47
|
-
rescue Hyperliquid::Error => e
|
|
48
|
-
puts " Error: #{e.message}"
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Example 4: Get spot metadata
|
|
52
|
-
begin
|
|
53
|
-
puts "\n4. Getting spot asset metadata..."
|
|
54
|
-
spot_meta = sdk.info.spot_meta
|
|
55
|
-
tokens = spot_meta['tokens'] || []
|
|
56
|
-
puts " Found #{tokens.length} spot tokens"
|
|
57
|
-
rescue Hyperliquid::Error => e
|
|
58
|
-
puts " Error: #{e.message}"
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Example 5: Use testnet
|
|
62
|
-
puts "\n5. Creating testnet SDK..."
|
|
63
|
-
testnet_sdk = Hyperliquid.new(testnet: true)
|
|
64
|
-
puts " Testnet base URL: #{testnet_sdk.base_url}"
|
|
65
|
-
|
|
66
|
-
# Example 6: User-specific endpoints (requires valid wallet address)
|
|
67
|
-
wallet_address = "0x#{'0' * 40}" # Placeholder address
|
|
68
|
-
|
|
69
|
-
begin
|
|
70
|
-
puts "\n6. Getting user state for #{wallet_address[0..13]}..."
|
|
71
|
-
state = sdk.info.user_state(wallet_address)
|
|
72
|
-
puts " Account value: #{state.dig('marginSummary', 'accountValue') || 'N/A'}"
|
|
73
|
-
rescue Hyperliquid::Error => e
|
|
74
|
-
puts " Error: #{e.message}"
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# =============================================================================
|
|
78
|
-
# EXCHANGE API (Authenticated, requires private key)
|
|
79
|
-
# =============================================================================
|
|
80
|
-
|
|
81
|
-
puts "\n--- EXCHANGE API EXAMPLES ---\n"
|
|
82
|
-
puts "(These examples are commented out to prevent accidental execution)\n"
|
|
83
|
-
|
|
84
|
-
# To use the Exchange API, create an SDK with your private key:
|
|
85
|
-
#
|
|
86
|
-
# sdk = Hyperliquid.new(
|
|
87
|
-
# testnet: true, # Use testnet for testing!
|
|
88
|
-
# private_key: ENV['HYPERLIQUID_PRIVATE_KEY']
|
|
89
|
-
# )
|
|
90
|
-
#
|
|
91
|
-
# Your wallet address:
|
|
92
|
-
# puts sdk.exchange.address
|
|
93
|
-
#
|
|
94
|
-
# Place a limit order:
|
|
95
|
-
# result = sdk.exchange.order(
|
|
96
|
-
# coin: 'BTC',
|
|
97
|
-
# is_buy: true,
|
|
98
|
-
# size: 0.001,
|
|
99
|
-
# limit_px: 50000.0,
|
|
100
|
-
# order_type: { limit: { tif: 'Gtc' } },
|
|
101
|
-
# reduce_only: false
|
|
102
|
-
# )
|
|
103
|
-
#
|
|
104
|
-
# Place a market order (uses slippage):
|
|
105
|
-
# result = sdk.exchange.market_order(
|
|
106
|
-
# coin: 'BTC',
|
|
107
|
-
# is_buy: true,
|
|
108
|
-
# size: 0.001,
|
|
109
|
-
# slippage: 0.01 # 1% slippage
|
|
110
|
-
# )
|
|
111
|
-
#
|
|
112
|
-
# Place a stop loss order:
|
|
113
|
-
# result = sdk.exchange.order(
|
|
114
|
-
# coin: 'BTC',
|
|
115
|
-
# is_buy: false,
|
|
116
|
-
# size: 0.001,
|
|
117
|
-
# limit_px: 48000.0,
|
|
118
|
-
# order_type: {
|
|
119
|
-
# trigger: {
|
|
120
|
-
# trigger_px: 49000.0,
|
|
121
|
-
# is_market: false,
|
|
122
|
-
# tpsl: 'sl'
|
|
123
|
-
# }
|
|
124
|
-
# },
|
|
125
|
-
# reduce_only: true
|
|
126
|
-
# )
|
|
127
|
-
#
|
|
128
|
-
# Cancel an order by OID:
|
|
129
|
-
# result = sdk.exchange.cancel(coin: 'BTC', oid: 123456789)
|
|
130
|
-
#
|
|
131
|
-
# Cancel an order by client order ID:
|
|
132
|
-
# result = sdk.exchange.cancel_by_cloid(coin: 'BTC', cloid: '0x...')
|
|
133
|
-
#
|
|
134
|
-
# Bulk cancel multiple orders:
|
|
135
|
-
# result = sdk.exchange.bulk_cancel([
|
|
136
|
-
# { coin: 'BTC', oid: 123 },
|
|
137
|
-
# { coin: 'ETH', oid: 456 }
|
|
138
|
-
# ])
|
|
139
|
-
#
|
|
140
|
-
# Use client order IDs for tracking:
|
|
141
|
-
# cloid = Hyperliquid::Cloid.random
|
|
142
|
-
# result = sdk.exchange.order(
|
|
143
|
-
# coin: 'BTC',
|
|
144
|
-
# is_buy: true,
|
|
145
|
-
# size: 0.001,
|
|
146
|
-
# limit_px: 50000.0,
|
|
147
|
-
# cloid: cloid
|
|
148
|
-
# )
|
|
149
|
-
# puts "Order placed with cloid: #{cloid}"
|
|
150
|
-
#
|
|
151
|
-
# Trade on behalf of a vault:
|
|
152
|
-
# result = sdk.exchange.order(
|
|
153
|
-
# coin: 'BTC',
|
|
154
|
-
# is_buy: true,
|
|
155
|
-
# size: 0.001,
|
|
156
|
-
# limit_px: 50000.0,
|
|
157
|
-
# vault_address: '0x...'
|
|
158
|
-
# )
|
|
159
|
-
|
|
160
|
-
puts 'See commented code above for Exchange API usage patterns.'
|
|
161
|
-
puts "\nSDK examples completed!"
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Test 1: Spot Market Roundtrip (PURR/USDC)
|
|
5
|
-
# Buy and sell PURR at market price.
|
|
6
|
-
|
|
7
|
-
require_relative 'test_helpers'
|
|
8
|
-
|
|
9
|
-
sdk = build_sdk
|
|
10
|
-
separator('TEST 1: Spot Market Roundtrip (PURR/USDC)')
|
|
11
|
-
|
|
12
|
-
spot_coin = 'PURR/USDC'
|
|
13
|
-
spot_size = 5
|
|
14
|
-
|
|
15
|
-
mids = sdk.info.all_mids
|
|
16
|
-
spot_price = mids[spot_coin]&.to_f
|
|
17
|
-
|
|
18
|
-
if spot_price&.positive?
|
|
19
|
-
puts "#{spot_coin} mid: $#{spot_price}"
|
|
20
|
-
puts "Size: #{spot_size} PURR (~$#{(spot_size * spot_price).round(2)})"
|
|
21
|
-
puts "Slippage: #{(SPOT_SLIPPAGE * 100).to_i}%"
|
|
22
|
-
puts
|
|
23
|
-
|
|
24
|
-
puts 'Placing market BUY...'
|
|
25
|
-
result = sdk.exchange.market_order(
|
|
26
|
-
coin: spot_coin,
|
|
27
|
-
is_buy: true,
|
|
28
|
-
size: spot_size,
|
|
29
|
-
slippage: SPOT_SLIPPAGE
|
|
30
|
-
)
|
|
31
|
-
check_result(result, 'Buy')
|
|
32
|
-
|
|
33
|
-
wait_with_countdown(WAIT_SECONDS, 'Waiting before sell...')
|
|
34
|
-
|
|
35
|
-
puts 'Placing market SELL...'
|
|
36
|
-
result = sdk.exchange.market_order(
|
|
37
|
-
coin: spot_coin,
|
|
38
|
-
is_buy: false,
|
|
39
|
-
size: spot_size,
|
|
40
|
-
slippage: SPOT_SLIPPAGE
|
|
41
|
-
)
|
|
42
|
-
check_result(result, 'Sell')
|
|
43
|
-
else
|
|
44
|
-
puts red("SKIPPED: Could not get #{spot_coin} price")
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
test_passed('Test 1 Spot Market Roundtrip')
|
|
48
|
-
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Test 2: Spot Limit Order (Place and Cancel)
|
|
5
|
-
# Place a limit buy well below market, then cancel it.
|
|
6
|
-
|
|
7
|
-
require_relative 'test_helpers'
|
|
8
|
-
|
|
9
|
-
sdk = build_sdk
|
|
10
|
-
separator('TEST 2: Spot Limit Order (Place and Cancel)')
|
|
11
|
-
|
|
12
|
-
spot_coin = 'PURR/USDC'
|
|
13
|
-
spot_size = 5
|
|
14
|
-
|
|
15
|
-
mids = sdk.info.all_mids
|
|
16
|
-
spot_price = mids[spot_coin]&.to_f
|
|
17
|
-
|
|
18
|
-
if spot_price&.positive?
|
|
19
|
-
limit_price = (spot_price * 0.50).round(2)
|
|
20
|
-
puts "#{spot_coin} mid: $#{spot_price}"
|
|
21
|
-
puts "Limit price: $#{limit_price} (50% below mid - won't fill)"
|
|
22
|
-
puts "Size: #{spot_size} PURR"
|
|
23
|
-
puts
|
|
24
|
-
|
|
25
|
-
puts 'Placing limit BUY order...'
|
|
26
|
-
result = sdk.exchange.order(
|
|
27
|
-
coin: spot_coin,
|
|
28
|
-
is_buy: true,
|
|
29
|
-
size: spot_size,
|
|
30
|
-
limit_px: limit_price,
|
|
31
|
-
order_type: { limit: { tif: 'Gtc' } },
|
|
32
|
-
reduce_only: false
|
|
33
|
-
)
|
|
34
|
-
oid = check_result(result, 'Limit order')
|
|
35
|
-
|
|
36
|
-
if oid.is_a?(Integer)
|
|
37
|
-
wait_with_countdown(WAIT_SECONDS, 'Order resting. Waiting before cancel...')
|
|
38
|
-
|
|
39
|
-
puts "Canceling order #{oid}..."
|
|
40
|
-
result = sdk.exchange.cancel(coin: spot_coin, oid: oid)
|
|
41
|
-
check_result(result, 'Cancel')
|
|
42
|
-
end
|
|
43
|
-
else
|
|
44
|
-
puts red("SKIPPED: Could not get #{spot_coin} price")
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
test_passed('Test 2 Spot Limit Order')
|
|
48
|
-
|