Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion skills/hyperliquid-plugin/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "hyperliquid",
"description": "Hyperliquid on-chain perpetuals DEX — check positions, get market prices, place and cancel perpetual orders on Hyperliquid L1 (chain_id 999).",
"version": "0.3.7",
"version": "0.3.8",
"author": {
"name": "GeoGu360",
"github": "GeoGu360"
Expand Down
2 changes: 1 addition & 1 deletion skills/hyperliquid-plugin/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion skills/hyperliquid-plugin/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "hyperliquid-plugin"
version = "0.3.7"
version = "0.3.8"
edition = "2021"

[[bin]]
Expand Down
20 changes: 15 additions & 5 deletions skills/hyperliquid-plugin/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: hyperliquid-plugin
description: Hyperliquid DEX — trade perps & spot, deposit from Arbitrum, withdraw to Arbitrum, transfer between perp and spot accounts, manage gas on HyperEVM.
version: "0.3.7"
version: "0.3.8"
author: GeoGu360
tags:
- perps
Expand All @@ -26,7 +26,7 @@ tags:
# Check for skill updates (1-hour cache)
UPDATE_CACHE="$HOME/.plugin-store/update-cache/hyperliquid-plugin"
CACHE_MAX=3600
LOCAL_VER="0.3.7"
LOCAL_VER="0.3.8"
DO_CHECK=true

if [ -f "$UPDATE_CACHE" ]; then
Expand Down Expand Up @@ -99,15 +99,15 @@ case "${OS}_${ARCH}" in
mingw*_aarch64|msys*_aarch64|cygwin*_aarch64) TARGET="aarch64-pc-windows-msvc"; EXT=".exe" ;;
esac
mkdir -p ~/.local/bin
curl -fsSL "https://github.com/okx/plugin-store/releases/download/plugins/hyperliquid-plugin@0.3.7/hyperliquid-plugin-${TARGET}${EXT}" -o ~/.local/bin/.hyperliquid-plugin-core${EXT}
curl -fsSL "https://github.com/okx/plugin-store/releases/download/plugins/hyperliquid-plugin@0.3.8/hyperliquid-plugin-${TARGET}${EXT}" -o ~/.local/bin/.hyperliquid-plugin-core${EXT}
chmod +x ~/.local/bin/.hyperliquid-plugin-core${EXT}

# Symlink CLI name to universal launcher
ln -sf "$LAUNCHER" ~/.local/bin/hyperliquid-plugin

# Register version
mkdir -p "$HOME/.plugin-store/managed"
echo "0.3.7" > "$HOME/.plugin-store/managed/hyperliquid-plugin"
echo "0.3.8" > "$HOME/.plugin-store/managed/hyperliquid-plugin"
```

### Report install (auto-injected, runs once)
Expand All @@ -127,7 +127,7 @@ if [ ! -f "$REPORT_FLAG" ]; then
# Report to Vercel stats
curl -s -X POST "https://plugin-store-dun.vercel.app/install" \
-H "Content-Type: application/json" \
-d '{"name":"hyperliquid-plugin","version":"0.3.7"}' >/dev/null 2>&1 || true
-d '{"name":"hyperliquid-plugin","version":"0.3.8"}' >/dev/null 2>&1 || true
# Report to OKX API (with HMAC-signed device token)
curl -s -X POST "https://www.okx.com/priapi/v1/wallet/plugins/download/report" \
-H "Content-Type: application/json" \
Expand Down Expand Up @@ -444,6 +444,9 @@ All prices (trigger + worst-fill limit) are automatically rounded to the coin's
- Both are reduce-only market trigger orders with 10% slippage tolerance
- If entry partially fills, children activate proportionally

**Strategy attribution (`--strategy-id`):**
When `--strategy-id <id>` is provided (non-empty), the plugin calls `onchainos wallet report-plugin-info` after the order succeeds with a JSON payload containing `wallet`, `proxyAddress` (empty for HL), `order_id` (HL `oid`), `tx_hashes` (empty at submit time), `market_id` (coin), `asset_id` (empty), `side`, `amount`, `symbol` (`USDC`), `price`, `timestamp`, `strategy_id`, `plugin_name: hyperliquid-plugin`. Omit or pass `""` to skip. Failures log to stderr and do not affect the trade result.

---

### 4. `close` — Market-Close an Open Position
Expand Down Expand Up @@ -475,6 +478,9 @@ hyperliquid close --coin BTC --size 0.005 --confirm

**Display:** `coin`, `side`, `size`, `result` status.

**Strategy attribution (`--strategy-id`):**
Same behavior as `order` — when provided and non-empty, the plugin reports the close order to the OKX backend via `onchainos wallet report-plugin-info`. `side` is the close direction (closing a long → `SELL`, closing a short → `BUY`). Omit to skip.

---

### 5. `tpsl` — Set Stop-Loss / Take-Profit on Existing Position
Expand Down Expand Up @@ -958,6 +964,10 @@ All data returned by `hyperliquid positions`, `hyperliquid prices`, and exchange

## Changelog

### v0.3.8 (2026-04-22)

- **feat**: Strategy attribution reporting — `order` and `close` each accept an optional `--strategy-id <id>`. When provided and non-empty, the plugin invokes `onchainos wallet report-plugin-info` after the order succeeds with a JSON payload containing `wallet`, `proxyAddress` (empty for HL), `order_id` (HL `oid` as string), `tx_hashes` (empty array — HL does not produce an on-chain tx hash at submit time; the settlement `hash` is available later via `userFillsByTime` lookup by `oid`), `market_id` (coin symbol), `asset_id` (empty), `side` (`BUY`/`SELL`), `amount`, `symbol` (`USDC`, the collateral asset), `price`, `timestamp`, `strategy_id`, `plugin_name: hyperliquid-plugin`. Omitting the flag skips reporting entirely. Report failures log to stderr as warnings and do not affect the trade result.

### v0.3.6 (2026-04-17)

- **feat**: `quickstart` — new command; checks Arbitrum USDC balance + Hyperliquid account value + open positions in parallel via onchainos, returns structured JSON with `status` and `next_command` to guide first-time users from zero to first trade
Expand Down
2 changes: 1 addition & 1 deletion skills/hyperliquid-plugin/plugin.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
schema_version: 1
name: hyperliquid-plugin
version: "0.3.7"
version: "0.3.8"
description: "Trade perpetuals on Hyperliquid — check positions, get prices, place market/limit orders with TP/SL brackets, close positions, deposit USDC"
author:
name: GeoGu360
Expand Down
44 changes: 43 additions & 1 deletion skills/hyperliquid-plugin/src/commands/close.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clap::Args;
use crate::api::{get_asset_meta, get_all_mids, get_clearinghouse_state};
use crate::config::{info_url, exchange_url, normalize_coin, now_ms, CHAIN_ID, ARBITRUM_CHAIN_ID};
use crate::onchainos::{onchainos_hl_sign, resolve_wallet};
use crate::onchainos::{onchainos_hl_sign, report_plugin_info, resolve_wallet};
use crate::signing::{build_close_action, round_px, submit_exchange_request};

#[derive(Args)]
Expand All @@ -25,6 +25,10 @@ pub struct CloseArgs {
/// Confirm and submit (without this flag, shows a preview)
#[arg(long)]
pub confirm: bool,

/// Strategy ID for attribution — reported to OKX backend alongside the order
#[arg(long)]
pub strategy_id: Option<String>,
}

pub async fn run(args: CloseArgs) -> anyhow::Result<()> {
Expand Down Expand Up @@ -193,6 +197,44 @@ pub async fn run(args: CloseArgs) -> anyhow::Result<()> {
}
};

let statuses = result["response"]["data"]["statuses"]
.as_array()
.and_then(|a| a.first())
.cloned()
.unwrap_or(serde_json::Value::Null);
let avg_px = statuses["filled"]["avgPx"].as_str().map(|s| s.to_string());
let oid = statuses["filled"]["oid"]
.as_u64()
.or_else(|| statuses["resting"]["oid"].as_u64());

if let (Some(sid), Some(oid_val)) = (
args.strategy_id.as_deref().filter(|s| !s.is_empty()),
oid,
) {
let ts_now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let report_payload = serde_json::json!({
"wallet": wallet,
"proxyAddress": "",
"order_id": oid_val.to_string(),
"tx_hashes": [],
"market_id": coin,
"asset_id": "",
"side": closing_side.to_uppercase(),
"amount": close_size,
"symbol": "USDC",
"price": avg_px.clone().unwrap_or_default(),
"timestamp": ts_now,
"strategy_id": sid,
"plugin_name": "hyperliquid-plugin",
});
if let Err(e) = report_plugin_info(&report_payload) {
eprintln!("[hyperliquid] Warning: report-plugin-info failed: {}", e);
}
}

println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
Expand Down
34 changes: 33 additions & 1 deletion skills/hyperliquid-plugin/src/commands/order.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clap::Args;
use crate::api::{get_asset_meta, get_all_mids, get_clearinghouse_state, get_spot_clearinghouse_state};
use crate::config::{info_url, exchange_url, normalize_coin, now_ms, CHAIN_ID, ARBITRUM_CHAIN_ID, USDC_ARBITRUM};
use crate::onchainos::{onchainos_hl_sign, resolve_wallet};
use crate::onchainos::{onchainos_hl_sign, report_plugin_info, resolve_wallet};
use crate::rpc::{ARBITRUM_RPC, erc20_balance};
use crate::signing::{
build_bracketed_order_action, build_limit_order_action, build_market_order_action,
Expand Down Expand Up @@ -69,6 +69,10 @@ pub struct OrderArgs {
/// Confirm and submit the order (without this flag, prints a preview)
#[arg(long)]
pub confirm: bool,

/// Strategy ID for attribution — reported to OKX backend alongside the order
#[arg(long)]
pub strategy_id: Option<String>,
}

/// Format a size value to exactly `decimals` decimal places, trimming trailing zeros.
Expand Down Expand Up @@ -477,6 +481,34 @@ pub async fn run(args: OrderArgs) -> anyhow::Result<()> {
.as_u64()
.or_else(|| statuses["resting"]["oid"].as_u64());

if let (Some(sid), Some(oid_val)) = (
args.strategy_id.as_deref().filter(|s| !s.is_empty()),
oid,
) {
let ts_now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let report_payload = serde_json::json!({
"wallet": wallet,
"proxyAddress": "",
"order_id": oid_val.to_string(),
"tx_hashes": [],
"market_id": coin,
"asset_id": "",
"side": if is_buy { "BUY" } else { "SELL" },
"amount": size_str,
"symbol": "USDC",
"price": avg_px.clone().unwrap_or_else(|| args.price.clone().unwrap_or_default()),
"timestamp": ts_now,
"strategy_id": sid,
"plugin_name": "hyperliquid-plugin",
});
if let Err(e) = report_plugin_info(&report_payload) {
eprintln!("[hyperliquid] Warning: report-plugin-info failed: {}", e);
}
}

println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
Expand Down
27 changes: 27 additions & 0 deletions skills/hyperliquid-plugin/src/onchainos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,3 +477,30 @@ pub fn onchainos_hl_sign_usd_class_transfer(
"vaultAddress": null
}))
}

/// Report plugin-level order metadata to the OKX backend for strategy attribution.
///
/// Shells out to `onchainos wallet report-plugin-info --plugin-parameter <json> --chain 42161`.
/// Non-fatal at the call site: the trade has already been submitted by the time this runs,
/// so callers should log and continue on error rather than propagate.
pub fn report_plugin_info(payload: &Value) -> anyhow::Result<()> {
let payload_str = serde_json::to_string(payload)
.map_err(|e| anyhow::anyhow!("serializing report-plugin-info payload: {}", e))?;
let output = Command::new("onchainos")
.args([
"wallet", "report-plugin-info",
"--plugin-parameter", &payload_str,
"--chain", "42161",
])
.output()
.map_err(|e| anyhow::anyhow!("Failed to spawn onchainos wallet report-plugin-info: {}", e))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!(
"onchainos report-plugin-info failed ({}): {}",
output.status,
stderr.trim()
);
}
Ok(())
}
Loading