@hypelens/hypelens-agent-rail 0.1.12 → 0.1.14
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.
- package/README.md +6 -8
- package/package.json +2 -2
- package/skill/hypelens-agent-rail/SKILL.md +33 -24
- package/src/core.js +19 -1
- package/src/exchange.js +5 -5
- package/src/exchange.js.bak-clearerr +211 -0
- package/src/mcp.js +1 -1
- /package/skill/{hypelens-agent-rail/SKILL.md.bak → SKILL.md.bak.aside} +0 -0
package/README.md
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
# @hypelens/hypelens-agent-rail
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Install + setup
|
|
6
|
-
|
|
2
|
+
**Place, cancel, close Hyperliquid perps.** Agent wallet once · auto-round · **1bp on fills — no sub.**
|
|
3
|
+
## Install → approve → place
|
|
7
4
|
```bash
|
|
8
5
|
npx -y @hypelens/hypelens-agent-rail
|
|
9
6
|
```
|
|
10
|
-
|
|
11
|
-
1. `hl_new_agent_wallet` — store as `HYPELENS_AGENT_PK`
|
|
7
|
+
1. `hl_new_agent_wallet` → `HYPELENS_AGENT_PK`
|
|
12
8
|
2. `hl_approve_payloads` — master signs approveAgent + ApproveBuilderFee **0.01%** once
|
|
13
9
|
3. `hl_place_order` / `hl_cancel_order` / `hl_close_position` / `hl_positions` / `hl_balances`
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
**First place (testnet):** after approve, call `hl_place_order` with `{coin:"BTC", isBuy:true, sizeUsd:12, leverage:2}` — size/px auto-round; no feed required.
|
|
12
|
+
|
|
13
|
+
Default **testnet**. `HYPELENS_NET=mainnet` for live. Fee: **1bp on fills** — no sub.
|
|
16
14
|
|
|
17
15
|
## Optional after install: sizing edge
|
|
18
16
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypelens/hypelens-agent-rail",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Place, cancel, and close Hyperliquid perps from OpenClaw/MCP. Agent wallet once, auto-round, 1bp on fills (no sub). npx @hypelens/hypelens-agent-rail",
|
|
3
|
+
"version": "0.1.14",
|
|
4
|
+
"description": "Place, cancel, and close Hyperliquid perps from OpenClaw/MCP. Agent wallet once, auto-round, 1bp on fills (no sub). First place: hl_place_order BTC sizeUsd 12. npx @hypelens/hypelens-agent-rail",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -1,50 +1,59 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hypelens-agent-rail
|
|
3
3
|
description: >-
|
|
4
|
-
|
|
5
|
-
once, auto-round, 1bp builder
|
|
6
|
-
|
|
4
|
+
hyperliquid place cancel close — Hyperliquid perps rail for OpenClaw/MCP.
|
|
5
|
+
Agent wallet once, auto-round, 1bp builder on fills (no sub). Keywords:
|
|
6
|
+
hyperliquid place cancel close order perps hl_place_order hl_cancel_order
|
|
7
|
+
hl_close_position trading-agent.
|
|
7
8
|
metadata:
|
|
8
9
|
openclaw:
|
|
10
|
+
emoji: "📈"
|
|
9
11
|
requires:
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
+
bins:
|
|
13
|
+
- npx
|
|
14
|
+
envVars:
|
|
15
|
+
- name: HYPELENS_NET
|
|
16
|
+
required: false
|
|
17
|
+
description: testnet (default) or mainnet
|
|
18
|
+
- name: HYPELENS_AGENT_PK
|
|
19
|
+
required: false
|
|
20
|
+
description: agent-wallet pk required only to place/cancel/close
|
|
21
|
+
- name: HYPELENS_FEED_URL
|
|
22
|
+
required: false
|
|
23
|
+
description: optional denser heat (advisory if unset — does not block place)
|
|
12
24
|
---
|
|
13
25
|
|
|
14
|
-
# HypeLens Agent Rail
|
|
26
|
+
# HypeLens Agent Rail — Place / Cancel / Close Hyperliquid Perps
|
|
15
27
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Approve builder once, then place/cancel/close Hyperliquid perps and read balances/positions. **1bp (0.01%) on fills only — no subscription.**
|
|
28
|
+
**Place, cancel, and close Hyperliquid perps orders** from OpenClaw/MCP. Agent wallet once, auto-round, **1bp (0.01%) on fills only — no subscription.**
|
|
19
29
|
|
|
20
30
|
```bash
|
|
21
31
|
npx -y @hypelens/hypelens-agent-rail
|
|
22
32
|
```
|
|
23
33
|
|
|
24
|
-
**ClawHub / OpenClaw:** `clawhub install hypelens-agent-rail`
|
|
34
|
+
**ClawHub / OpenClaw:** `clawhub install hypelens-agent-rail` · **skills.sh:** `npx skills add polyparlay/hypelens`
|
|
25
35
|
|
|
26
|
-
##
|
|
36
|
+
## Quickstart (place-ready)
|
|
27
37
|
|
|
28
|
-
1. `
|
|
29
|
-
2. `
|
|
30
|
-
3. `
|
|
38
|
+
1. Call `hl_quickstart` first
|
|
39
|
+
2. `hl_new_agent_wallet` → store as `HYPELENS_AGENT_PK`
|
|
40
|
+
3. `hl_approve_payloads(agentAddress)` → master signs approveAgent + ApproveBuilderFee **1bp** once
|
|
41
|
+
4. `hl_place_order` / `hl_cancel_order` / `hl_close_position` / `hl_positions` / `hl_balances`
|
|
31
42
|
|
|
32
|
-
## Sizing rule
|
|
33
43
|
|
|
34
|
-
|
|
44
|
+
**First place (testnet):** after approve, call hl_place_order with coin BTC, isBuy true, sizeUsd 12, leverage 2 — size/px auto-round; feed optional.
|
|
45
|
+
Default net is **testnet**. Set `HYPELENS_NET=mainnet` for live. Missing intel feed is **advisory** — does **not** block place.
|
|
35
46
|
|
|
36
|
-
##
|
|
47
|
+
## Sizing rule
|
|
37
48
|
|
|
38
|
-
|
|
49
|
+
Warn if notional `sizeUsd` is more than **20% of account equity**. Size from balance; confirm mark before place.
|
|
39
50
|
|
|
40
51
|
## Tools
|
|
41
52
|
|
|
42
|
-
- **Place path:** `
|
|
53
|
+
- **Place path:** `hl_quickstart`, `hl_exchange_status`, `hl_place_order`, `hl_cancel_order`, `hl_close_position`, `hl_balances`, `hl_positions`
|
|
43
54
|
- **Setup:** `hl_new_agent_wallet`, `hl_approve_payloads`
|
|
44
|
-
- **Optional edge:** `hl_walls`, `hl_whale_book`, `hl_cascade`, `hl_pretrade_check`
|
|
55
|
+
- **Optional edge (after install):** `hl_walls`, `hl_whale_book`, `hl_cascade`, `hl_pretrade_check`
|
|
45
56
|
|
|
46
|
-
##
|
|
57
|
+
## Keywords
|
|
47
58
|
|
|
48
|
-
|
|
49
|
-
- `HYPELENS_AGENT_PK` — required to place/cancel/close
|
|
50
|
-
- `HYPELENS_FEED_URL` / `HYPELENS_FEED_FILE` — optional denser heat (advisory if unset)
|
|
59
|
+
hyperliquid · place · cancel · close · order · perps · hl_place_order · mcp · openclaw · builder
|
package/src/core.js
CHANGED
|
@@ -443,10 +443,25 @@ export async function pretradeCheckFull(args) {
|
|
|
443
443
|
};
|
|
444
444
|
}
|
|
445
445
|
|
|
446
|
-
/** First-discovery: place-ready bootstrap (heat/sizing optional after
|
|
446
|
+
/** First-discovery: place-ready bootstrap FIRST (heat/sizing optional after).
|
|
447
|
+
* Optional sample_walls fetched after bootstrap object is assembled.
|
|
448
|
+
*/
|
|
447
449
|
export async function quickstart({ coin = 'BTC' } = {}) {
|
|
448
450
|
const { actions } = loadShipped();
|
|
449
451
|
const net = process.env.HYPELENS_NET || 'testnet';
|
|
452
|
+
const bootstrap = {
|
|
453
|
+
ready: 'place/cancel/close/positions @ 1bp on fills — no subscription',
|
|
454
|
+
install: 'npx -y @hypelens/hypelens-agent-rail',
|
|
455
|
+
steps: [
|
|
456
|
+
'hl_new_agent_wallet → store as HYPELENS_AGENT_PK',
|
|
457
|
+
'hl_approve_payloads(agentAddress) → master signs approveAgent + ApproveBuilderFee 1bp once',
|
|
458
|
+
'hl_place_order / hl_cancel_order / hl_close_position / hl_positions / hl_balances',
|
|
459
|
+
'Optional after: hl_walls / hl_whale_book / hl_cascade / hl_pretrade_check (advisory if no full feed)'
|
|
460
|
+
],
|
|
461
|
+
fee: '1bp (0.01%) on fills only',
|
|
462
|
+
package: '@hypelens/hypelens-agent-rail',
|
|
463
|
+
first_place_example: { coin: 'BTC', isBuy: true, sizeUsd: 12, leverage: 2, note: 'testnet default; size/px auto-round; feed optional' }
|
|
464
|
+
};
|
|
450
465
|
let sample_walls = null;
|
|
451
466
|
let sample_error = null;
|
|
452
467
|
try {
|
|
@@ -455,6 +470,7 @@ export async function quickstart({ coin = 'BTC' } = {}) {
|
|
|
455
470
|
sample_error = e && e.message ? e.message : String(e);
|
|
456
471
|
}
|
|
457
472
|
return {
|
|
473
|
+
bootstrap,
|
|
458
474
|
value_prop: AGENT_VALUE,
|
|
459
475
|
install: 'npx -y @hypelens/hypelens-agent-rail',
|
|
460
476
|
builder_fee: '1bp (0.01%)',
|
|
@@ -478,6 +494,8 @@ export async function quickstart({ coin = 'BTC' } = {}) {
|
|
|
478
494
|
'5. Optional after install: hl_walls / hl_whale_book / hl_cascade / hl_pretrade_check for sizing edge'
|
|
479
495
|
],
|
|
480
496
|
sizing_rule: 'Warn if sizeUsd > 20% of account equity; size from balance; confirm mark before place.',
|
|
497
|
+
first_place_example: bootstrap.first_place_example,
|
|
498
|
+
next_if_blocked: 'If place errors on missing key: hl_new_agent_wallet → HYPELENS_AGENT_PK → hl_approve_payloads → hl_place_order(first_place_example)',
|
|
481
499
|
why_route_volume_here: AGENT_VALUE,
|
|
482
500
|
sample_walls,
|
|
483
501
|
sample_error,
|
package/src/exchange.js
CHANGED
|
@@ -26,12 +26,12 @@ function assertPlacementAllowed(actions) {
|
|
|
26
26
|
if (net() === 'mainnet' && !actions.MAINNET_PLACEMENT_ENABLED) {
|
|
27
27
|
throw new Error('MAINNET PLACEMENT DISABLED — Set HYPELENS_NET=testnet.');
|
|
28
28
|
}
|
|
29
|
-
if (!process.env.HYPELENS_AGENT_PK) throw new Error('HYPELENS_AGENT_PK not set —
|
|
29
|
+
if (!process.env.HYPELENS_AGENT_PK) throw new Error('HYPELENS_AGENT_PK not set — call hl_quickstart, then hl_new_agent_wallet → set HYPELENS_AGENT_PK, hl_approve_payloads (master signs 1bp), then hl_place_order({coin:"BTC", isBuy:true, sizeUsd:12, leverage:2}) on testnet');
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function agentAddress() {
|
|
33
33
|
const { signer } = loadShipped();
|
|
34
|
-
if (!process.env.HYPELENS_AGENT_PK) throw new Error('HYPELENS_AGENT_PK not set');
|
|
34
|
+
if (!process.env.HYPELENS_AGENT_PK) throw new Error('HYPELENS_AGENT_PK not set — hl_new_agent_wallet → export HYPELENS_AGENT_PK=<pk> before place/balances/cancel/close');
|
|
35
35
|
return signer.addressFromPrivateKey(process.env.HYPELENS_AGENT_PK);
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -163,7 +163,7 @@ export async function cancelOrder({ coin, oid = null, cloid = null }) {
|
|
|
163
163
|
let action;
|
|
164
164
|
if (cloid) action = actions.buildCancelByCloidAction([{ assetIndex, cloid }]);
|
|
165
165
|
else if (oid != null) action = actions.buildCancelAction([{ assetIndex, oid: Number(oid) }]);
|
|
166
|
-
else throw new Error('oid or cloid required');
|
|
166
|
+
else throw new Error('oid or cloid required — pass oid or cloid to hl_cancel_order');
|
|
167
167
|
const posted = await postL1(action);
|
|
168
168
|
return { cancelled: posted.ok, net: posted.net, response: posted.response, coin: resolveCoin(coin).toUpperCase(), oid, cloid };
|
|
169
169
|
}
|
|
@@ -175,10 +175,10 @@ export async function closePosition({ coin, size = null, px = null, user } = {})
|
|
|
175
175
|
const name = resolveCoin(coin).toUpperCase();
|
|
176
176
|
const { positions } = await getPositions({ user, coin: name });
|
|
177
177
|
const pos = positions[0];
|
|
178
|
-
if (!pos || !pos.szi) throw new Error('no open position for ' + name);
|
|
178
|
+
if (!pos || !pos.szi) throw new Error('no open position for ' + name + ' — check hl_positions first');
|
|
179
179
|
const absSz = Math.abs(pos.szi);
|
|
180
180
|
const closeSz = size != null ? Number(size) : absSz;
|
|
181
|
-
if (!(closeSz > 0) || closeSz > absSz + 1e-12) throw new Error('bad close size');
|
|
181
|
+
if (!(closeSz > 0) || closeSz > absSz + 1e-12) throw new Error('bad close size — omit size for full close, or size <= abs(szi)');
|
|
182
182
|
const isBuy = pos.szi < 0; // short -> buy to close
|
|
183
183
|
let entryPx = px;
|
|
184
184
|
if (entryPx == null) {
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// HypeLens Agent Rail — EXECUTION (builder-code monetized).
|
|
2
|
+
// Reuses Module 3: hl-actions.js builds actions (builder fee pinned on place/close),
|
|
3
|
+
// hl-signer.js signs through the vendored SDK. Default HYPELENS_NET=testnet.
|
|
4
|
+
// Fee model: 1bp (0.01%) on fills only — no subscription. BUILDER_F=10.
|
|
5
|
+
// Risk/heat advisory when feed missing; danger-wall refuses unless override.
|
|
6
|
+
import { loadShipped } from './load.js';
|
|
7
|
+
import { pretradeCheckFull, fullFeedConfigured, resolveCoin } from './core.js';
|
|
8
|
+
|
|
9
|
+
const net = () => process.env.HYPELENS_NET || 'testnet';
|
|
10
|
+
|
|
11
|
+
export function status() {
|
|
12
|
+
const { actions, signer } = loadShipped();
|
|
13
|
+
const st = signer.selfTest();
|
|
14
|
+
return {
|
|
15
|
+
net: net(),
|
|
16
|
+
mainnetPlacementEnabled: actions.MAINNET_PLACEMENT_ENABLED,
|
|
17
|
+
builder: actions.BUILDER, builderFeeTenthsBp: actions.BUILDER_F, maxFeeRate: actions.MAX_BUILDER_FEE_RATE,
|
|
18
|
+
feeModel: '1bp on fills only — no subscription',
|
|
19
|
+
signerReady: st.ok, signerError: st.ok ? null : st.error,
|
|
20
|
+
hasAgentKey: Boolean(process.env.HYPELENS_AGENT_PK),
|
|
21
|
+
fullFeedConfigured: fullFeedConfigured()
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function assertPlacementAllowed(actions) {
|
|
26
|
+
if (net() === 'mainnet' && !actions.MAINNET_PLACEMENT_ENABLED) {
|
|
27
|
+
throw new Error('MAINNET PLACEMENT DISABLED — Set HYPELENS_NET=testnet.');
|
|
28
|
+
}
|
|
29
|
+
if (!process.env.HYPELENS_AGENT_PK) throw new Error('HYPELENS_AGENT_PK not set — run the approve flow first (see approvePayloads)');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function agentAddress() {
|
|
33
|
+
const { signer } = loadShipped();
|
|
34
|
+
if (!process.env.HYPELENS_AGENT_PK) throw new Error('HYPELENS_AGENT_PK not set');
|
|
35
|
+
return signer.addressFromPrivateKey(process.env.HYPELENS_AGENT_PK);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function approvePayloads(agentAddressArg) {
|
|
39
|
+
const { actions } = loadShipped();
|
|
40
|
+
return {
|
|
41
|
+
approveAgent: actions.buildApproveAgent(net(), agentAddressArg),
|
|
42
|
+
approveBuilderFee: actions.buildApproveBuilderFee(net()),
|
|
43
|
+
note: 'Sign both with the MASTER wallet (EIP-712), POST each as {action, signature, nonce} to ' + actions.NET[net()].exchange
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function newAgentWallet() {
|
|
48
|
+
const { signer, sdk } = loadShipped();
|
|
49
|
+
const pk = sdk.randomPrivateKey();
|
|
50
|
+
return { privateKey: pk, address: signer.addressFromPrivateKey(pk), note: 'store as HYPELENS_AGENT_PK; approve via approvePayloads(address)' };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function assetMeta(coin) {
|
|
54
|
+
const { actions } = loadShipped();
|
|
55
|
+
const r = await fetch(actions.NET[net()].info, {
|
|
56
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
57
|
+
body: JSON.stringify({ type: 'meta' })
|
|
58
|
+
});
|
|
59
|
+
const meta = await r.json();
|
|
60
|
+
const name = resolveCoin(coin).toUpperCase();
|
|
61
|
+
const i = meta.universe.findIndex((u) => u.name === name);
|
|
62
|
+
if (i < 0) throw new Error('coin not on ' + net() + ': ' + coin);
|
|
63
|
+
return { assetIndex: i, szDecimals: meta.universe[i].szDecimals, name };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function postL1(action) {
|
|
67
|
+
const { actions, signer } = loadShipped();
|
|
68
|
+
assertPlacementAllowed(actions);
|
|
69
|
+
const nonce = actions.nonce();
|
|
70
|
+
const signed = await signer.signL1(process.env.HYPELENS_AGENT_PK, action, nonce, net() === 'testnet', null);
|
|
71
|
+
const res = await fetch(actions.NET[net()].exchange, {
|
|
72
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
73
|
+
body: JSON.stringify({ action: signed.action, signature: signed.signature, nonce: signed.nonce })
|
|
74
|
+
});
|
|
75
|
+
const body = await res.json().catch(() => ({}));
|
|
76
|
+
return { ok: res.ok && body.status === 'ok', net: net(), response: body };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function clearinghouse(user) {
|
|
80
|
+
const { actions } = loadShipped();
|
|
81
|
+
const r = await fetch(actions.NET[net()].info, {
|
|
82
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
83
|
+
body: JSON.stringify({ type: 'clearinghouseState', user })
|
|
84
|
+
});
|
|
85
|
+
if (!r.ok) throw new Error('clearinghouseState HTTP ' + r.status);
|
|
86
|
+
return r.json();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Balances / margin summary for the agent (or explicit user). */
|
|
90
|
+
export async function getBalances({ user } = {}) {
|
|
91
|
+
const u = user || agentAddress();
|
|
92
|
+
const state = await clearinghouse(u);
|
|
93
|
+
const ms = state.marginSummary || {};
|
|
94
|
+
const equity = Number(ms.accountValue || 0);
|
|
95
|
+
return {
|
|
96
|
+
net: net(),
|
|
97
|
+
user: u,
|
|
98
|
+
marginSummary: ms,
|
|
99
|
+
withdrawable: state.withdrawable ?? null,
|
|
100
|
+
equity,
|
|
101
|
+
sizingWarnThresholdUsd: equity * 0.2,
|
|
102
|
+
sizing_rule: 'Warn if sizeUsd > 20% of account equity; size from balance; confirm mark before place.',
|
|
103
|
+
feeModel: '1bp on fills only — no subscription'
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Open perp positions (assetPositions). */
|
|
108
|
+
export async function getPositions({ user, coin } = {}) {
|
|
109
|
+
const u = user || agentAddress();
|
|
110
|
+
const state = await clearinghouse(u);
|
|
111
|
+
let positions = (state.assetPositions || []).map((ap) => {
|
|
112
|
+
const p = ap.position || ap;
|
|
113
|
+
return {
|
|
114
|
+
coin: p.coin,
|
|
115
|
+
szi: Number(p.szi || 0),
|
|
116
|
+
entryPx: p.entryPx != null ? Number(p.entryPx) : null,
|
|
117
|
+
positionValue: p.positionValue != null ? Number(p.positionValue) : null,
|
|
118
|
+
unrealizedPnl: p.unrealizedPnl != null ? Number(p.unrealizedPnl) : null,
|
|
119
|
+
leverage: p.leverage || null,
|
|
120
|
+
liquidationPx: p.liquidationPx != null ? Number(p.liquidationPx) : null,
|
|
121
|
+
marginUsed: p.marginUsed != null ? Number(p.marginUsed) : null
|
|
122
|
+
};
|
|
123
|
+
}).filter((p) => p.szi !== 0);
|
|
124
|
+
if (coin) {
|
|
125
|
+
const c = resolveCoin(coin).toUpperCase();
|
|
126
|
+
positions = positions.filter((p) => (p.coin || '').toUpperCase() === c);
|
|
127
|
+
}
|
|
128
|
+
return { net: net(), user: u, positions };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function placeOrder({ coin, isBuy, size, entryPx, slPx = null, tpPx = null, leverage = null, override = false, skipRiskCheck = false }) {
|
|
132
|
+
const { actions } = loadShipped();
|
|
133
|
+
assertPlacementAllowed(actions);
|
|
134
|
+
let risk = null;
|
|
135
|
+
if (!skipRiskCheck && leverage) {
|
|
136
|
+
risk = await pretradeCheckFull({ coin, dir: isBuy ? 'long' : 'short', leverage, entryPx });
|
|
137
|
+
if (risk.refuseReason === 'full_feed_unconfigured') {
|
|
138
|
+
risk = { ...risk, advisory: true, feedAdvisory: true, note: (risk.note || '') + ' Place allowed without full feed; heat is advisory.' };
|
|
139
|
+
}
|
|
140
|
+
if (risk.verdict === 'danger' && risk.refuseReason !== 'full_feed_unconfigured' && !override) {
|
|
141
|
+
const wallSz = risk.wall && risk.wall.sizeUsd != null
|
|
142
|
+
? Math.round(risk.wall.sizeUsd / 1e6)
|
|
143
|
+
: (risk.wall && risk.wall.sizeUsdCoarse != null ? Math.round(risk.wall.sizeUsdCoarse / 1e6) : '?');
|
|
144
|
+
return {
|
|
145
|
+
placed: false,
|
|
146
|
+
refused: 'liq price ' + risk.liqPx + ' lands inside a $' + wallSz + 'M wall — pass override:true to force',
|
|
147
|
+
risk,
|
|
148
|
+
builderFeeAttached: false
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const { assetIndex, szDecimals } = await assetMeta(coin);
|
|
153
|
+
const action = actions.buildOrderAction({ assetIndex, szDecimals, isBuy, entryPx, size, slPx, tpPx });
|
|
154
|
+
const posted = await postL1(action);
|
|
155
|
+
return { placed: posted.ok, net: posted.net, response: posted.response, risk, builderFeeAttached: posted.ok };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Cancel by oid and/or cloid. */
|
|
159
|
+
export async function cancelOrder({ coin, oid = null, cloid = null }) {
|
|
160
|
+
const { actions } = loadShipped();
|
|
161
|
+
assertPlacementAllowed(actions);
|
|
162
|
+
const { assetIndex } = await assetMeta(coin);
|
|
163
|
+
let action;
|
|
164
|
+
if (cloid) action = actions.buildCancelByCloidAction([{ assetIndex, cloid }]);
|
|
165
|
+
else if (oid != null) action = actions.buildCancelAction([{ assetIndex, oid: Number(oid) }]);
|
|
166
|
+
else throw new Error('oid or cloid required');
|
|
167
|
+
const posted = await postL1(action);
|
|
168
|
+
return { cancelled: posted.ok, net: posted.net, response: posted.response, coin: resolveCoin(coin).toUpperCase(), oid, cloid };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** IOC reduce-only close for a coin (uses position size if size omitted). */
|
|
172
|
+
export async function closePosition({ coin, size = null, px = null, user } = {}) {
|
|
173
|
+
const { actions } = loadShipped();
|
|
174
|
+
assertPlacementAllowed(actions);
|
|
175
|
+
const name = resolveCoin(coin).toUpperCase();
|
|
176
|
+
const { positions } = await getPositions({ user, coin: name });
|
|
177
|
+
const pos = positions[0];
|
|
178
|
+
if (!pos || !pos.szi) throw new Error('no open position for ' + name);
|
|
179
|
+
const absSz = Math.abs(pos.szi);
|
|
180
|
+
const closeSz = size != null ? Number(size) : absSz;
|
|
181
|
+
if (!(closeSz > 0) || closeSz > absSz + 1e-12) throw new Error('bad close size');
|
|
182
|
+
const isBuy = pos.szi < 0; // short -> buy to close
|
|
183
|
+
let entryPx = px;
|
|
184
|
+
if (entryPx == null) {
|
|
185
|
+
const { actions: a2 } = loadShipped();
|
|
186
|
+
const r = await fetch(a2.NET[net()].info, {
|
|
187
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
188
|
+
body: JSON.stringify({ type: 'metaAndAssetCtxs' })
|
|
189
|
+
});
|
|
190
|
+
const [meta, ctxs] = await r.json();
|
|
191
|
+
const i = meta.universe.findIndex((u) => u.name === name);
|
|
192
|
+
if (i < 0) throw new Error('coin not in meta: ' + name);
|
|
193
|
+
entryPx = Number(ctxs[i].markPx);
|
|
194
|
+
// slip 0.5% through for IOC fill
|
|
195
|
+
entryPx = isBuy ? entryPx * 1.005 : entryPx * 0.995;
|
|
196
|
+
}
|
|
197
|
+
const { assetIndex, szDecimals } = await assetMeta(name);
|
|
198
|
+
const action = actions.buildCloseAction({ assetIndex, szDecimals, isBuy, entryPx, size: closeSz });
|
|
199
|
+
const posted = await postL1(action);
|
|
200
|
+
return {
|
|
201
|
+
closed: posted.ok,
|
|
202
|
+
net: posted.net,
|
|
203
|
+
response: posted.response,
|
|
204
|
+
coin: name,
|
|
205
|
+
size: closeSz,
|
|
206
|
+
isBuy,
|
|
207
|
+
entryPx,
|
|
208
|
+
builderFeeAttached: posted.ok,
|
|
209
|
+
feeModel: '1bp on fills only — no subscription'
|
|
210
|
+
};
|
|
211
|
+
}
|
package/src/mcp.js
CHANGED
|
@@ -13,7 +13,7 @@ const wrap = (fn) => async (args) => {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export async function main() {
|
|
16
|
-
const server = new McpServer({ name: 'hypelens-agent-rail', version: '0.1.
|
|
16
|
+
const server = new McpServer({ name: 'hypelens-agent-rail', version: '0.1.13' });
|
|
17
17
|
|
|
18
18
|
server.tool('hl_quickstart',
|
|
19
19
|
'CALL FIRST: place-ready bootstrap — install, agent wallet + ApproveBuilderFee 1bp once, then place/cancel/close/positions. 1bp on fills, no sub. Heat optional after.',
|
|
File without changes
|