@hypelens/hypelens-agent-rail 0.1.1 → 0.1.2

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.
@@ -1,13 +0,0 @@
1
- // prepack: copy the shipped extension modules into agent-rail/vendor so the
2
- // published npm package is self-contained. The rail evals these verbatim.
3
- import { copyFileSync, mkdirSync } from 'node:fs';
4
- import { dirname, join } from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
-
7
- const RAIL = join(dirname(fileURLToPath(import.meta.url)), '..');
8
- const EXT = join(RAIL, '..', 'extension');
9
- mkdirSync(join(RAIL, 'vendor'), { recursive: true });
10
- for (const f of [['vendor/hl-sdk.js', 'hl-sdk.js'], ['exchange/hl-signer.js', 'hl-signer.js'], ['exchange/hl-actions.js', 'hl-actions.js'], ['viewmodel.js', 'viewmodel.js']]) {
11
- copyFileSync(join(EXT, f[0]), join(RAIL, 'vendor', f[1]));
12
- console.log('bundled', f[1]);
13
- }
package/server.json DELETED
@@ -1,35 +0,0 @@
1
- {
2
- "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
3
- "name": "io.github.polyparlay/hypelens-agent-rail",
4
- "description": "Real-data Hyperliquid risk intelligence for trading agents: liquidation walls, cascade chains, whale book, and pre-trade liq checks from a live 1,100-wallet position crawl — plus risk-checked, testnet-gated order placement.",
5
- "status": "active",
6
- "repository": {
7
- "url": "https://github.com/polyparlay/hypelens",
8
- "source": "github",
9
- "subfolder": "agent-rail"
10
- },
11
- "version": "0.1.0",
12
- "packages": [
13
- {
14
- "registry_type": "npm",
15
- "registry_base_url": "https://registry.npmjs.org",
16
- "identifier": "hypelens-agent-rail",
17
- "version": "0.1.0",
18
- "transport": { "type": "stdio" },
19
- "environment_variables": [
20
- {
21
- "name": "HYPELENS_AGENT_PK",
22
- "description": "Optional: agent-wallet private key for order placement (risk tools need no key)",
23
- "is_required": false,
24
- "is_secret": true
25
- },
26
- {
27
- "name": "HYPELENS_NET",
28
- "description": "Optional: 'testnet' (default) or 'mainnet' (placement hard-blocked until enabled)",
29
- "is_required": false,
30
- "is_secret": false
31
- }
32
- ]
33
- }
34
- ]
35
- }
package/smithery.yaml DELETED
@@ -1,23 +0,0 @@
1
- # Smithery.ai listing config — https://smithery.ai/docs/config
2
- startCommand:
3
- type: stdio
4
- configSchema:
5
- type: object
6
- properties:
7
- hypelensAgentPk:
8
- type: string
9
- description: "Optional agent-wallet private key for order placement. Risk tools (walls, cascade, pretrade check, whale book) work without any key."
10
- hypelensNet:
11
- type: string
12
- enum: [testnet, mainnet]
13
- default: testnet
14
- description: "Execution network. Mainnet placement is hard-blocked in code until operator-enabled."
15
- commandFunction: |-
16
- (config) => ({
17
- command: 'node',
18
- args: ['bin/hypelens-rail-mcp.js'],
19
- env: {
20
- ...(config.hypelensAgentPk ? { HYPELENS_AGENT_PK: config.hypelensAgentPk } : {}),
21
- ...(config.hypelensNet ? { HYPELENS_NET: config.hypelensNet } : {})
22
- }
23
- })
@@ -1,74 +0,0 @@
1
- import { test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { loadShipped } from '../src/load.js';
4
- import { status, placeOrder, newAgentWallet, approvePayloads } from '../src/exchange.js';
5
- import { _setFixtures } from '../src/core.js';
6
-
7
- test('shipped modules load in node; signer self-test passes', () => {
8
- const { actions, signer, VM } = loadShipped();
9
- assert.equal(signer.selfTest().ok, true, signer.selfTest().error || '');
10
- assert.equal(typeof VM.computeCascade, 'function');
11
- assert.equal(actions.BUILDER, '0x9548B8E9554a1968843B3C380431b10996247c88');
12
- });
13
-
14
- test('every order action carries the pinned builder fee', () => {
15
- const { actions } = loadShipped();
16
- const a = actions.buildOrderAction({ assetIndex: 0, szDecimals: 5, isBuy: true, entryPx: 100000, size: 0.01, slPx: 95000, tpPx: 111000 });
17
- assert.deepEqual(a.builder, { b: actions.BUILDER.toLowerCase(), f: 10 });
18
- assert.equal(a.grouping, 'normalTpsl');
19
- assert.equal(a.orders.length, 3);
20
- assert.equal(a.orders[1].r, true); // SL reduce-only
21
- });
22
-
23
- test('mainnet placement is hard-blocked', async () => {
24
- process.env.HYPELENS_NET = 'mainnet';
25
- process.env.HYPELENS_AGENT_PK = '0x' + '1'.repeat(64);
26
- await assert.rejects(
27
- () => placeOrder({ coin: 'BTC', isBuy: true, size: 0.01, entryPx: 100000, skipRiskCheck: true }),
28
- /MAINNET PLACEMENT DISABLED/);
29
- delete process.env.HYPELENS_NET;
30
- delete process.env.HYPELENS_AGENT_PK;
31
- });
32
-
33
- test('placement without agent key fails closed', async () => {
34
- delete process.env.HYPELENS_AGENT_PK;
35
- await assert.rejects(
36
- () => placeOrder({ coin: 'BTC', isBuy: true, size: 0.01, entryPx: 100000, skipRiskCheck: true }),
37
- /HYPELENS_AGENT_PK not set/);
38
- });
39
-
40
- test('risk gate refuses danger orders without override', async () => {
41
- process.env.HYPELENS_NET = 'testnet';
42
- process.env.HYPELENS_AGENT_PK = '0x' + '1'.repeat(64);
43
- _setFixtures({
44
- feed: {
45
- updated: new Date().toISOString(),
46
- coins: { BTC: { mark: 100000, coverage: { pct: 53 }, positions: [[94800, 50e6, 0, '0xaaa0000000000000000000000000000000000001', 104000]] } }
47
- },
48
- meta: { BTC: { assetIndex: 0, szDecimals: 5, maxLeverage: 40, markPx: 100000, oiNtl: 2e9, dayNtlVlm: 2e9 } }
49
- });
50
- // leverage chosen so liq falls near 94800 wall; if the model puts liq inside
51
- // the wall the order must be refused (no network call is made on refusal)
52
- const r = await placeOrder({ coin: 'BTC', isBuy: true, size: 0.01, entryPx: 100000, leverage: 18 })
53
- .catch((e) => ({ threw: String(e.message) }));
54
- if (r.refused) {
55
- assert.match(r.refused, /wall/);
56
- assert.equal(r.placed, false);
57
- } else {
58
- // liq didn't land in the wall under the shipped model — acceptable; the
59
- // testnet POST path was then exercised or asset lookup failed. Either way
60
- // the call must not report a successful mainnet placement.
61
- assert.notEqual(r.net, 'mainnet');
62
- }
63
- delete process.env.HYPELENS_AGENT_PK;
64
- delete process.env.HYPELENS_NET;
65
- });
66
-
67
- test('agent wallet + approve payloads are well-formed', () => {
68
- const w = newAgentWallet();
69
- assert.match(w.address, /^0x[0-9a-fA-F]{40}$/);
70
- const p = approvePayloads(w.address);
71
- assert.equal(p.approveAgent.action.type, 'approveAgent');
72
- assert.equal(p.approveBuilderFee.action.maxFeeRate, '0.01%');
73
- assert.equal(p.approveBuilderFee.primaryType, 'HyperliquidTransaction:ApproveBuilderFee');
74
- });
@@ -1,86 +0,0 @@
1
- import { test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { binWalls, pretradeCheck, walls, cascade, whaleBook, _setFixtures } from '../src/core.js';
4
-
5
- // Synthetic fixture: BTC at 100,000 with a fat long wall at 95,000.
6
- const FEED = {
7
- updated: new Date().toISOString(),
8
- coins: {
9
- BTC: {
10
- mark: 100000, oiUsd: 2e9, coverage: { pct: 53 },
11
- positions: [
12
- [95000, 30e6, 0, '0xaaa0000000000000000000000000000000000001', 104000],
13
- [94900, 15e6, 0, '0xaaa0000000000000000000000000000000000002', 103000],
14
- [99500, 12e6, 0, '0xaaa0000000000000000000000000000000000003', 101000],
15
- [112000, 20e6, 1, '0xbbb0000000000000000000000000000000000004', 98000],
16
- [140000, 5e6, 1, '0xbbb0000000000000000000000000000000000005', 90000]
17
- ]
18
- }
19
- }
20
- };
21
- const META = { BTC: { assetIndex: 0, szDecimals: 5, maxLeverage: 40, markPx: 100000, oiNtl: 2e9, dayNtlVlm: 2e9 } };
22
-
23
- test('binWalls: bins by 0.4% of mark, sides by price vs mark, sorted by size', () => {
24
- const w = binWalls(FEED.coins.BTC.positions.map((p) => ({ price: p[0], sizeUsd: p[1] })), 100000);
25
- assert.ok(w.length >= 3);
26
- // 95000 → bin 238 (95200), 94900 → bin 237 (94800): adjacent bins, no merge
27
- assert.equal(w[0].sizeUsd, 30e6);
28
- assert.equal(w[0].side, 'long');
29
- assert.ok(Math.abs(w[0].distPct + 5) < 1);
30
- assert.equal(w[1].sizeUsd, 20e6); // the 112000 short wall ranks second
31
- // 140000 is outside ±50%? no — inside; but check the 112000 short bin exists
32
- assert.ok(w.some((x) => x.side === 'short'));
33
- });
34
-
35
- test('walls(): magnet detected only within 1.5% and ≥$10M', async () => {
36
- _setFixtures({ feed: FEED, meta: META });
37
- const r = await walls('BTC');
38
- assert.equal(r.magnet.sizeUsd, 12e6); // 99500 wall is 0.5% away and ≥$10M
39
- assert.equal(r.magnet.side, 'below');
40
- assert.equal(r.coverage_pct, 53);
41
- assert.ok(r.totalLiqBelowUsd > r.totalLiqAboveUsd);
42
- });
43
-
44
- test('pretradeCheck: high leverage lands in the wall → danger + clear-leverage suggestion', async () => {
45
- _setFixtures({ feed: FEED, meta: META });
46
- // long entry 100k: find a leverage whose liq is ~95k (inside the 45M wall)
47
- const r = await pretradeCheck({ coin: 'BTC', dir: 'long', leverage: 18, entryPx: 100000 });
48
- assert.equal(typeof r.liqPx, 'number');
49
- assert.ok(r.liqPx < 100000);
50
- if (r.liqInsideWall) {
51
- assert.equal(r.verdict, 'danger');
52
- assert.ok(r.suggestedClearLeverage == null || r.suggestedClearLeverage < 18);
53
- } else {
54
- assert.ok(['ok', 'warning'].includes(r.verdict));
55
- }
56
- });
57
-
58
- test('pretradeCheck: low leverage clears the walls', async () => {
59
- _setFixtures({ feed: FEED, meta: META });
60
- const r = await pretradeCheck({ coin: 'BTC', dir: 'long', leverage: 2, entryPx: 100000 });
61
- assert.equal(r.liqInsideWall, false);
62
- assert.notEqual(r.verdict, 'danger');
63
- });
64
-
65
- test('pretradeCheck: rejects leverage above max', async () => {
66
- _setFixtures({ feed: FEED, meta: META });
67
- await assert.rejects(() => pretradeCheck({ coin: 'BTC', dir: 'long', leverage: 41 }), /exceeds max/);
68
- });
69
-
70
- test('cascade: returns model output shape from shipped computeCascade', async () => {
71
- _setFixtures({ feed: FEED, meta: META });
72
- const r = await cascade('BTC', 'down');
73
- assert.equal(r.dir, 'down');
74
- if (r.cascade) {
75
- assert.ok(r.cascade.triggerPx > 0);
76
- assert.ok(r.cascade.totalLiqUsd > 0);
77
- }
78
- });
79
-
80
- test('whaleBook: sorted by notional, capped, honest fields present', async () => {
81
- _setFixtures({ feed: FEED, meta: META });
82
- const r = await whaleBook('BTC', 3);
83
- assert.equal(r.positions.length, 3);
84
- assert.ok(r.positions[0].notionalUsd >= r.positions[1].notionalUsd);
85
- assert.ok(r.source.includes('NOT estimates'));
86
- });