hypervibes 0.0.0-stage → 0.4.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.
- package/.build/client/assets/_authenticated-C87LKvH0.js +1 -0
- package/.build/client/assets/_authenticated.account-D-nTh_fC.js +1 -0
- package/.build/client/assets/_authenticated.agents-HwcveLyB.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId-DuE8Lv62.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.chart-Bb_XkvQx.js +4 -0
- package/.build/client/assets/_authenticated.agents._agentId.chat-ClMMKw_N.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.index-DNJwBwn9.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.indicators-BmVjLtRI.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.indicators_._indicatorId-CMqaaFQf.js +2 -0
- package/.build/client/assets/_authenticated.agents._agentId.indicators_._indicatorId-DtgkoNV5.js +6 -0
- package/.build/client/assets/_authenticated.agents._agentId.memories-O-4f6a4G.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.memories_._memoryRef-BUPtUcj3.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.notifications-2lyhXCSk.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.notifications_._notificationId-Da3ZVT4v.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.settings-C5cWHoYw.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.sub-agents-HwcveLyB.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.sub-agents._subagentId-CZ4MeRy4.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.sub-agents._subagentId_.runs._runId-dVbo5kMU.js +3 -0
- package/.build/client/assets/_authenticated.agents._agentId.sub-agents.index-DeWTOYJD.js +1 -0
- package/.build/client/assets/_authenticated.agents._agentId.sub-agents.new-D02TflCE.js +37 -0
- package/.build/client/assets/_authenticated.agents._agentId.transactions-D3a8Glgj.js +1 -0
- package/.build/client/assets/_authenticated.agents.index-CEqeDEgP.js +1 -0
- package/.build/client/assets/_authenticated.agents.new-DLRWK5Uk.js +1 -0
- package/.build/client/assets/_authenticated.providers-D0iPLXra.js +1 -0
- package/.build/client/assets/agent-chat-page-C5eYFgxw.js +1 -0
- package/.build/client/assets/agent-enablement-control-CUzOp9ce.js +1 -0
- package/.build/client/assets/agent-events-DC8eDZf3.js +1 -0
- package/.build/client/assets/agent-input-aolvZ47U.js +1 -0
- package/.build/client/assets/agent-notifications-page-B0mr-Zz8.js +1 -0
- package/.build/client/assets/agent-queries-CNMBImun.js +1 -0
- package/.build/client/assets/app-DjmsvbUC.css +2 -0
- package/.build/client/assets/artifact-content-COO4VTSZ.js +1 -0
- package/.build/client/assets/auth-client-K13li9H1.js +3 -0
- package/.build/client/assets/call-DxlGie4_.js +31 -0
- package/.build/client/assets/ccip-BgPlVPYP.js +1 -0
- package/.build/client/assets/chart-contracts-Cr5SFCbE.js +1 -0
- package/.build/client/assets/chart-queries-Bpvrgd0-.js +1 -0
- package/.build/client/assets/chat-bubble-CAJkT-vL.js +1 -0
- package/.build/client/assets/config-dialog-C9x9gKW9.js +1 -0
- package/.build/client/assets/contracts-HhAL7Tt7.js +1 -0
- package/.build/client/assets/createServerFn-DfZIp2Fu.js +3 -0
- package/.build/client/assets/custom-BeqXuIIS.js +5 -0
- package/.build/client/assets/decimal-B0DEG-1V.js +1 -0
- package/.build/client/assets/esm-BTb2nBdW.js +1 -0
- package/.build/client/assets/identicon-Lb9pS4O1.js +1 -0
- package/.build/client/assets/index-B4hvjMmI.js +10 -0
- package/.build/client/assets/indicator-input-fields-CtmrfVfS.js +1 -0
- package/.build/client/assets/indicator-queries-CzZ6u1Mh.js +1 -0
- package/.build/client/assets/indicator-renderer-CfccsIt2.js +1 -0
- package/.build/client/assets/instrument-label-BZYlWXR5.js +1 -0
- package/.build/client/assets/instrument-picker-DLPpryOm.js +1 -0
- package/.build/client/assets/journal-decimal-DBvd_Ibi.js +1 -0
- package/.build/client/assets/jsx-runtime-CIazjfqY.js +1 -0
- package/.build/client/assets/lazyRouteComponent-CJgUa7_r.js +1 -0
- package/.build/client/assets/lightweight-charts.production-BiBiFZSH.js +1 -0
- package/.build/client/assets/login-MNgYxCOY.js +3 -0
- package/.build/client/assets/memory-badge-BbN-DJQi.js +1 -0
- package/.build/client/assets/memory-contracts-Doi_Q1XJ.js +1 -0
- package/.build/client/assets/memory-queries-BttL580R.js +1 -0
- package/.build/client/assets/model-picker-YCQNNmyw.js +1 -0
- package/.build/client/assets/notification-queries-B12DG3aS.js +1 -0
- package/.build/client/assets/preload-helper-Czpn1I53.js +1 -0
- package/.build/client/assets/provider-logo-Ddb77aBD.js +1 -0
- package/.build/client/assets/provider-queries-DxPwhDOU.js +1 -0
- package/.build/client/assets/query-nvK_95Jo.js +1 -0
- package/.build/client/assets/random-uuid-DJR3ygJS.js +1 -0
- package/.build/client/assets/react-dom-C6bOPxes.js +1 -0
- package/.build/client/assets/risk-queries-D_6PHwus.js +1 -0
- package/.build/client/assets/run-subagent-dialog-B4yQe9T9.js +1 -0
- package/.build/client/assets/secp256k1-BbZnIgDP.js +1 -0
- package/.build/client/assets/setup-checklist-CUW-K484.js +1 -0
- package/.build/client/assets/sha2-hT-Q4pur.js +1 -0
- package/.build/client/assets/subagent-enabled-toggle-Bk5wfScg.js +1 -0
- package/.build/client/assets/subagent-input-TQke90I1.js +1 -0
- package/.build/client/assets/subagent-mutations-DfsDbmFu.js +1 -0
- package/.build/client/assets/subagent-queries-DVv5JCfs.js +1 -0
- package/.build/client/assets/subagent-run-history-D55Uh5VK.js +1 -0
- package/.build/client/assets/toggle-timestamp-B50FTPmA.js +1 -0
- package/.build/client/assets/useBaseQuery-Bxruo_mV.js +1 -0
- package/.build/client/assets/useMutation-DrF53EQG.js +1 -0
- package/.build/client/assets/useQuery-BMSU16jY.js +1 -0
- package/.build/client/assets/useRouter-c0I1aURm.js +1 -0
- package/.build/client/assets/useSelector-ERD9RroG.js +2 -0
- package/.build/client/assets/useSuspenseQuery-vODFwDb8.js +1 -0
- package/.build/client/assets/utils-Bpv4vmSW.js +1 -0
- package/.build/client/assets/worker-triggers-DtXyn40O.js +1 -0
- package/.build/server/assets/_authenticated-Bch0_2QH.js +264 -0
- package/.build/server/assets/_authenticated-DGV5cZYE.js +22 -0
- package/.build/server/assets/_authenticated.account-6qu6oQ1y.js +685 -0
- package/.build/server/assets/_authenticated.agents-DYDnQxli.js +5 -0
- package/.build/server/assets/_authenticated.agents._agentId-4HvB3mhd.js +17 -0
- package/.build/server/assets/_authenticated.agents._agentId-Cl608F1T.js +260 -0
- package/.build/server/assets/_authenticated.agents._agentId-G2jAHjma.js +15 -0
- package/.build/server/assets/_authenticated.agents._agentId.chart-BIA8wy73.js +2644 -0
- package/.build/server/assets/_authenticated.agents._agentId.chart-D4aj8Qy9.js +22 -0
- package/.build/server/assets/_authenticated.agents._agentId.chat-ClsVle4g.js +13 -0
- package/.build/server/assets/_authenticated.agents._agentId.chat-JV3FDsTp.js +18 -0
- package/.build/server/assets/_authenticated.agents._agentId.index-Ct4kbu2f.js +1044 -0
- package/.build/server/assets/_authenticated.agents._agentId.index-DJgrupvW.js +18 -0
- package/.build/server/assets/_authenticated.agents._agentId.indicators-DrKd0aP5.js +184 -0
- package/.build/server/assets/_authenticated.agents._agentId.indicators-wiVaY8fW.js +18 -0
- package/.build/server/assets/_authenticated.agents._agentId.indicators_._indicatorId-C4Ox2R-a.js +25 -0
- package/.build/server/assets/_authenticated.agents._agentId.indicators_._indicatorId-D8Q0hCW0.js +489 -0
- package/.build/server/assets/_authenticated.agents._agentId.memories-BUTyVLYy.js +42 -0
- package/.build/server/assets/_authenticated.agents._agentId.memories-E8-nSEMT.js +362 -0
- package/.build/server/assets/_authenticated.agents._agentId.memories_._memoryRef-C-SC6wzq.js +14 -0
- package/.build/server/assets/_authenticated.agents._agentId.memories_._memoryRef-ZCrdrc8C.js +490 -0
- package/.build/server/assets/_authenticated.agents._agentId.notifications-DQeQqTzh.js +14 -0
- package/.build/server/assets/_authenticated.agents._agentId.notifications-DlFyYyHh.js +16 -0
- package/.build/server/assets/_authenticated.agents._agentId.notifications_._notificationId-B0RosY_R.js +15 -0
- package/.build/server/assets/_authenticated.agents._agentId.notifications_._notificationId-C1NikQ9e.js +13 -0
- package/.build/server/assets/_authenticated.agents._agentId.settings-DDwjT3Ui.js +42 -0
- package/.build/server/assets/_authenticated.agents._agentId.settings-PEzEvOfo.js +751 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents-z34-J3Tu.js +5 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId-D-6TOi1U.js +1371 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId-DipzVdnr.js +13 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId_.runs._runId-BZR3VLV7.js +610 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents._subagentId_.runs._runId-DsYJKfGm.js +12 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents.index-BF8Cuzcr.js +231 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents.index-D2x0qCnt.js +12 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents.new-CYm_vuDD.js +13 -0
- package/.build/server/assets/_authenticated.agents._agentId.sub-agents.new-CiVShIka.js +497 -0
- package/.build/server/assets/_authenticated.agents._agentId.transactions-BC_XfGqw.js +95 -0
- package/.build/server/assets/_authenticated.agents._agentId.transactions-WtBW3xmh.js +495 -0
- package/.build/server/assets/_authenticated.agents.index-C8zmu8T8.js +78 -0
- package/.build/server/assets/_authenticated.agents.new-Bt-tBR47.js +302 -0
- package/.build/server/assets/_authenticated.providers-C0pb-ujV.js +672 -0
- package/.build/server/assets/_tanstack-start-manifest_v-Baw5u9sk.js +354 -0
- package/.build/server/assets/account-XvMwzRXh.js +66 -0
- package/.build/server/assets/account-queries-AJJVkLAV.js +32 -0
- package/.build/server/assets/agent-chat-page-DChiz8KQ.js +686 -0
- package/.build/server/assets/agent-enablement-control-C90f0uIb.js +143 -0
- package/.build/server/assets/agent-events-DK9Yz5__.js +217 -0
- package/.build/server/assets/agent-notifications-page-BdyKAyds.js +276 -0
- package/.build/server/assets/agent-queries-Cmx8RiUx.js +51 -0
- package/.build/server/assets/agents-CZq-0mna.js +106 -0
- package/.build/server/assets/agents-CfjmtW7A.js +278 -0
- package/.build/server/assets/artifact-content-B6uHmPz7.js +35 -0
- package/.build/server/assets/artifacts-HtLwHs8q.js +42 -0
- package/.build/server/assets/auth-DTikZAVz.js +12 -0
- package/.build/server/assets/auth-client-Vb3O0v7P.js +1897 -0
- package/.build/server/assets/auth-user-DkLo8ojH.js +108 -0
- package/.build/server/assets/chart-queries-Bc3AfsEb.js +109 -0
- package/.build/server/assets/charts-C4dKuwTJ.js +130 -0
- package/.build/server/assets/chat-bubble-ik3itVbx.js +109 -0
- package/.build/server/assets/config-dialog-9gSAjwwT.js +55 -0
- package/.build/server/assets/contracts-CG-nZuYJ.js +155 -0
- package/.build/server/assets/createServerRpc-DNqhdU4z.js +12 -0
- package/.build/server/assets/decimal-BthtZ77w.js +97 -0
- package/.build/server/assets/empty-plugin-adapters-MOWBbCMw.js +5 -0
- package/.build/server/assets/errors-DaZqUftX.js +151 -0
- package/.build/server/assets/gateway-CHkruO_a.js +193 -0
- package/.build/server/assets/identicon-CyawcVnx.js +41 -0
- package/.build/server/assets/indicator-input-fields-bzuhIh6t.js +75 -0
- package/.build/server/assets/indicator-queries-4dY1-LXx.js +171 -0
- package/.build/server/assets/indicator-renderer-B2muPS-8.js +395 -0
- package/.build/server/assets/indicators-ENxAHN42.js +192 -0
- package/.build/server/assets/insight-contracts-B5rmUDfO.js +14 -0
- package/.build/server/assets/instrument-label-C-tTGGsD.js +40 -0
- package/.build/server/assets/instrument-picker-CRroLwVq.js +381 -0
- package/.build/server/assets/journal-9aE-um9f.js +107 -0
- package/.build/server/assets/journal-decimal-Bh9EL029.js +66 -0
- package/.build/server/assets/login-BsEFUvTX.js +125 -0
- package/.build/server/assets/memories-2WF2fW1G.js +100 -0
- package/.build/server/assets/memory-badge-DVYLTtRR.js +53 -0
- package/.build/server/assets/memory-contracts-DQGN2IKV.js +22 -0
- package/.build/server/assets/memory-queries--4RbPRrk.js +92 -0
- package/.build/server/assets/model-picker-CA1uYb--.js +225 -0
- package/.build/server/assets/notification-queries-0CJUkBt9.js +52 -0
- package/.build/server/assets/notifications-Cn7BiCyw.js +72 -0
- package/.build/server/assets/provider-logo-BRdbOr9I.js +32 -0
- package/.build/server/assets/provider-queries-BaZWLw8J.js +78 -0
- package/.build/server/assets/providers-CyoN_jgW.js +89 -0
- package/.build/server/assets/risk-Bqrb6hKQ.js +34 -0
- package/.build/server/assets/risk-queries-JQpByHT2.js +18 -0
- package/.build/server/assets/router-Hkr5TH_I.js +419 -0
- package/.build/server/assets/run-subagent-dialog-C7iUOZa-.js +59 -0
- package/.build/server/assets/setup-DqMlwgNr.js +30 -0
- package/.build/server/assets/setup-checklist-BICTmBo2.js +60 -0
- package/.build/server/assets/start-C-GPoWRt.js +4 -0
- package/.build/server/assets/subagent-enabled-toggle-Djte00oo.js +22 -0
- package/.build/server/assets/subagent-input-BHQKs2cW.js +57 -0
- package/.build/server/assets/subagent-mutations-dUo3B_SG.js +32 -0
- package/.build/server/assets/subagent-queries-sAwBjFxC.js +146 -0
- package/.build/server/assets/subagent-run-history-C_xmucO-.js +186 -0
- package/.build/server/assets/subagents-CIoilC66.js +180 -0
- package/.build/server/assets/toggle-timestamp-CToUVn67.js +28 -0
- package/.build/server/assets/trading-contracts-zWq1Hxnb.js +26 -0
- package/.build/server/assets/validation-BX-krL0i.js +33 -0
- package/.build/server/assets/worker-triggers-CJF5HWzR.js +51 -0
- package/.build/server/server.js +7501 -0
- package/LICENSE +661 -0
- package/README.md +145 -2
- package/bin/hypervibes.js +24 -0
- package/dist/agents/agent-enablement.js +10 -0
- package/dist/agents/agent-input.js +38 -0
- package/dist/agents/agent-service.js +1004 -0
- package/dist/agents/cancel-task-scope.js +52 -0
- package/dist/agents/candle-intervals.js +1 -0
- package/dist/agents/default-subagents.js +36 -0
- package/dist/agents/execution-context-contracts.js +1 -0
- package/dist/agents/execution-context.js +206 -0
- package/dist/agents/execution-warnings.js +33 -0
- package/dist/agents/orchestrator-insights.js +14 -0
- package/dist/agents/run-inspection.js +99 -0
- package/dist/agents/subagent-dashboard.js +1 -0
- package/dist/agents/subagent-extension.js +156 -0
- package/dist/agents/subagent-history.js +5 -0
- package/dist/agents/subagent-input.js +73 -0
- package/dist/agents/subagent-service.js +1177 -0
- package/dist/agents/subagent-templates.js +20 -0
- package/dist/agents/template-manifest.js +34 -0
- package/dist/agents/templates/basic-analysis.json +40 -0
- package/dist/agents/templates/basic-review.json +48 -0
- package/dist/agents/templates/basic-trading.json +45 -0
- package/dist/agents/templates/indicator-developer.json +16 -0
- package/dist/agents/time-guidance.js +29 -0
- package/dist/agents/worker-events.js +18 -0
- package/dist/agents/worker-inbox.js +28 -0
- package/dist/agents/worker-triggers.js +23 -0
- package/dist/auth-options.js +98 -0
- package/dist/auth-user.js +15 -0
- package/dist/auth.js +28 -0
- package/dist/cache/svg-logo-cache.js +155 -0
- package/dist/canonical.js +13 -0
- package/dist/charts/chart-service.js +236 -0
- package/dist/charts/contracts.js +118 -0
- package/dist/charts/durable-extension.js +27 -0
- package/dist/charts/trading-contracts.js +14 -0
- package/dist/config.js +85 -0
- package/dist/db/agent-schema.js +26 -0
- package/dist/db/auth-schema.js +122 -0
- package/dist/db/database.js +31 -0
- package/dist/db/exchange-schema.js +23 -0
- package/dist/db/gateway-schema.js +24 -0
- package/dist/db/indicator-schema.js +19 -0
- package/dist/db/migrations/20261015221717_auth/migration.sql +62 -0
- package/dist/db/migrations/20261015231224_agents/migration.sql +35 -0
- package/dist/db/migrations/20261016040641_provider_connections/migration.sql +19 -0
- package/dist/db/migrations/20261016061831_trade_journal/migration.sql +48 -0
- package/dist/db/migrations/20261016080320_order_execution/migration.sql +62 -0
- package/dist/db/migrations/20261016181110_subagents/migration.sql +51 -0
- package/dist/db/migrations/20261017222959_research_artifacts/migration.sql +37 -0
- package/dist/db/migrations/20261018061531_memories_and_insights/migration.sql +162 -0
- package/dist/db/migrations/20261020091156_worker_event_inbox/migration.sql +54 -0
- package/dist/db/migrations/20261021051229_chart_workspace/migration.sql +33 -0
- package/dist/db/migrations/20261021222116_gateways/migration.sql +15 -0
- package/dist/db/migrations/20261022015813_pine_indicators/migration.sql +43 -0
- package/dist/db/migrations/20261022101559_notifications/migration.sql +51 -0
- package/dist/db/migrations.js +12 -0
- package/dist/db/order-provenance-schema.js +17 -0
- package/dist/db/order-schema.js +32 -0
- package/dist/db/profile-lock.js +12 -0
- package/dist/db/provider-schema.js +16 -0
- package/dist/db/shared-database.js +128 -0
- package/dist/db/subagent-schema.js +79 -0
- package/dist/gateway/bridge-service.js +223 -0
- package/dist/gateway/gateway-store.js +77 -0
- package/dist/gateway/handlers.js +122 -0
- package/dist/gateway/telegram/connector.js +115 -0
- package/dist/gateway/telegram/transport.js +96 -0
- package/dist/hyperliquid/account-info.js +117 -0
- package/dist/hyperliquid/account-status.js +64 -0
- package/dist/hyperliquid/agent-dashboard.js +56 -0
- package/dist/hyperliquid/chart-contracts.js +9 -0
- package/dist/hyperliquid/constants.js +25 -0
- package/dist/hyperliquid/crypto.js +29 -0
- package/dist/hyperliquid/currency-logo.js +7 -0
- package/dist/hyperliquid/decimal.js +98 -0
- package/dist/hyperliquid/durable-extension.js +39 -0
- package/dist/hyperliquid/errors.js +8 -0
- package/dist/hyperliquid/exchange.js +118 -0
- package/dist/hyperliquid/journal-decimal.js +48 -0
- package/dist/hyperliquid/journal-model.js +167 -0
- package/dist/hyperliquid/journal-service.js +648 -0
- package/dist/hyperliquid/live-dashboard.js +245 -0
- package/dist/hyperliquid/logo-cache.js +27 -0
- package/dist/hyperliquid/market-charts.js +242 -0
- package/dist/hyperliquid/market-snapshot.js +45 -0
- package/dist/hyperliquid/orders/order-contracts.js +42 -0
- package/dist/hyperliquid/orders/order-math.js +89 -0
- package/dist/hyperliquid/orders/order-outcomes.js +74 -0
- package/dist/hyperliquid/orders/order-prepare.js +109 -0
- package/dist/hyperliquid/orders/order-service.js +432 -0
- package/dist/hyperliquid/orders/order-store.js +134 -0
- package/dist/hyperliquid/orders/order-tools.js +40 -0
- package/dist/hyperliquid/orders/protection.js +35 -0
- package/dist/hyperliquid/orders/risk-settings.js +44 -0
- package/dist/hyperliquid/orders/trade-admission.js +117 -0
- package/dist/hyperliquid/orders/trade-planning.js +60 -0
- package/dist/hyperliquid/service.js +529 -0
- package/dist/hyperliquid/shared-feeds.js +40 -0
- package/dist/hyperliquid/signing.js +89 -0
- package/dist/indicators/builtin-catalog.js +59 -0
- package/dist/indicators/builtins/adx.pine +22 -0
- package/dist/indicators/builtins/atr.pine +11 -0
- package/dist/indicators/builtins/bollinger.pine +10 -0
- package/dist/indicators/builtins/ema.pine +5 -0
- package/dist/indicators/builtins/macd.pine +18 -0
- package/dist/indicators/builtins/rsi.pine +18 -0
- package/dist/indicators/builtins/sma.pine +5 -0
- package/dist/indicators/candle-window.js +36 -0
- package/dist/indicators/chart-contracts.js +64 -0
- package/dist/indicators/contracts.js +14 -0
- package/dist/indicators/durable-extension.js +32 -0
- package/dist/indicators/indicator-service.js +300 -0
- package/dist/indicators/indicator-store.js +137 -0
- package/dist/indicators/normalize.js +149 -0
- package/dist/indicators/result-schema.js +79 -0
- package/dist/indicators/runtime-wrapper.js +52 -0
- package/dist/indicators/runtime.js +201 -0
- package/dist/indicators/validation.js +124 -0
- package/dist/indicators/worker-queue.js +48 -0
- package/dist/initialize-config.js +160 -0
- package/dist/main.js +59 -0
- package/dist/memory/decision-audit.js +47 -0
- package/dist/memory/durable-extension.js +40 -0
- package/dist/memory/execution-audit.js +83 -0
- package/dist/memory/insight-contracts.js +33 -0
- package/dist/memory/insight-service.js +117 -0
- package/dist/memory/memory-contracts.js +113 -0
- package/dist/memory/memory-service.js +179 -0
- package/dist/memory/operations.js +46 -0
- package/dist/memory/tool-schemas.js +44 -0
- package/dist/memory/validation.js +42 -0
- package/dist/notifications/contracts.js +1 -0
- package/dist/notifications/delivery.js +11 -0
- package/dist/notifications/durable-extension.js +20 -0
- package/dist/notifications/notification-service.js +324 -0
- package/dist/providers/credential-store.js +86 -0
- package/dist/providers/ollama-cloud.js +148 -0
- package/dist/providers/provider-logo-url.js +18 -0
- package/dist/providers/provider-logo.js +31 -0
- package/dist/providers/provider-service.js +295 -0
- package/dist/research/artifacts.js +151 -0
- package/dist/research/call-queue.js +43 -0
- package/dist/research/contracts.js +37 -0
- package/dist/research/evidence.js +43 -0
- package/dist/research/journal.js +30 -0
- package/dist/research/research-service.js +191 -0
- package/dist/research/tool-activity.js +49 -0
- package/dist/research/tool-response.js +25 -0
- package/dist/runtime.js +145 -0
- package/dist/web/agent-events.js +418 -0
- package/dist/web/app.js +206 -0
- package/dist/web/artifact-downloads.js +23 -0
- package/dist/web/middleware.js +25 -0
- package/dist/web/random-uuid.js +14 -0
- package/dist/web/start.js +56 -0
- package/package.json +93 -3
- package/scripts/check-node.mjs +24 -0
- package/static/android-chrome-192x192.png +0 -0
- package/static/android-chrome-512x512.png +0 -0
- package/static/apple-touch-icon.png +0 -0
- package/static/favicon-16x16.png +0 -0
- package/static/favicon-32x32.png +0 -0
- package/static/favicon.ico +0 -0
- package/static/github.svg +3 -0
- package/static/hypervibes.png +0 -0
- package/static/logo.png +0 -0
- package/static/site.webmanifest +1 -0
- package/static/telegram.svg +1 -0
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import { SharedHyperliquidFeeds } from "./shared-feeds.js";
|
|
2
|
+
import { MarketCharts } from "./market-charts.js";
|
|
3
|
+
import { loadMarketSnapshot } from "./market-snapshot.js";
|
|
4
|
+
import { hvWalletAddresses } from "../db/auth-schema.js";
|
|
5
|
+
import { HttpTransport, InfoClient } from "@nktkas/hyperliquid";
|
|
6
|
+
import { loadAccountStatus } from "./account-status.js";
|
|
7
|
+
import { loadAgentDashboard } from "./agent-dashboard.js";
|
|
8
|
+
import { LiveDashboards } from "./live-dashboard.js";
|
|
9
|
+
import { agentId } from "../agents/agent-input.js";
|
|
10
|
+
import { and, eq, isNotNull, isNull, sql } from "drizzle-orm";
|
|
11
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
12
|
+
import { hvAgents } from "../db/agent-schema.js";
|
|
13
|
+
import { hvHyperliquidSettings } from "../db/exchange-schema.js";
|
|
14
|
+
import { accountModeError, loadOwnedTradingAccounts, maxBuilderFee, } from "./account-info.js";
|
|
15
|
+
import { addDecimals } from "./decimal.js";
|
|
16
|
+
import { agentSubaccountName, API_WALLET_APPROVAL_MS, BUILDER_FEE_BPS_TO_TENTHS, BUILDER_RECIPIENT, DEFAULT_BUILDER_FEE_BPS, MAX_BUILDER_FEE_BPS, MAX_BUILDER_FEE_TENTHS_OF_BP, MIN_BUILDER_FEE_BPS, REVOKED_BUILDER_FEE_RATE, } from "./constants.js";
|
|
17
|
+
import { decryptSecret, encryptSecret, generateWalletPrivateKey } from "./crypto.js";
|
|
18
|
+
import { exchangeErrorMessage, exchangeSuccess, relaySignedAction, SubaccountCreator } from "./exchange.js";
|
|
19
|
+
import { HyperliquidError } from "./errors.js";
|
|
20
|
+
import { parseSignedAction, signatureMatches, validApproveAgentAction, validApproveBuilderFeeAction, } from "./signing.js";
|
|
21
|
+
/** Owns every user's Hyperliquid settings and the shared exchange clients. */
|
|
22
|
+
export class HyperliquidService {
|
|
23
|
+
db;
|
|
24
|
+
encryptionKey;
|
|
25
|
+
transport;
|
|
26
|
+
journal;
|
|
27
|
+
log;
|
|
28
|
+
creator;
|
|
29
|
+
live;
|
|
30
|
+
feeds;
|
|
31
|
+
charts;
|
|
32
|
+
started = false;
|
|
33
|
+
stopped = false;
|
|
34
|
+
reconcileChain = Promise.resolve();
|
|
35
|
+
reconcileTimer;
|
|
36
|
+
constructor(db, encryptionKey, transport = new HttpTransport(), feedFactory, journal, nonceManager, log, feeds = new SharedHyperliquidFeeds()) {
|
|
37
|
+
this.db = db;
|
|
38
|
+
this.encryptionKey = encryptionKey;
|
|
39
|
+
this.transport = transport;
|
|
40
|
+
this.journal = journal;
|
|
41
|
+
this.log = log;
|
|
42
|
+
this.feeds = feeds;
|
|
43
|
+
this.creator = new SubaccountCreator(transport, nonceManager);
|
|
44
|
+
this.live = new LiveDashboards(transport, feedFactory ?? feeds.account);
|
|
45
|
+
this.charts = new MarketCharts(transport, feeds.candle);
|
|
46
|
+
}
|
|
47
|
+
start() {
|
|
48
|
+
if (this.started || this.stopped)
|
|
49
|
+
return;
|
|
50
|
+
this.started = true;
|
|
51
|
+
this.log?.info("Starting Hyperliquid service");
|
|
52
|
+
void this.reconcileLinkedAccounts();
|
|
53
|
+
}
|
|
54
|
+
/** Reconciles journal and live feeds with the linked trading accounts.
|
|
55
|
+
* Called once at start and on every account-linking event; failures retry. */
|
|
56
|
+
reconcileLinkedAccounts(strict = false) {
|
|
57
|
+
const reconciliation = this.reconcileChain.then(async () => {
|
|
58
|
+
if (this.stopped)
|
|
59
|
+
return;
|
|
60
|
+
try {
|
|
61
|
+
const rows = await this.db.select({ address: hvAgents.tradingAccountAddress }).from(hvAgents)
|
|
62
|
+
.where(and(isNotNull(hvAgents.tradingAccountAddress), sql `${hvAgents.state} NOT IN ('stopping', 'stopped')`)).all();
|
|
63
|
+
if (this.stopped)
|
|
64
|
+
return;
|
|
65
|
+
const addresses = rows.flatMap((row) => row.address ? [row.address] : []);
|
|
66
|
+
await this.journal?.reconcile(addresses);
|
|
67
|
+
await this.live.reconcile(addresses);
|
|
68
|
+
this.log?.debug({ accounts: addresses.length }, "Reconciled linked Hyperliquid accounts");
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
this.log?.error(error, "Unable to refresh linked Hyperliquid accounts; retrying");
|
|
72
|
+
if (!this.stopped) {
|
|
73
|
+
this.reconcileTimer = setTimeout(() => { void this.reconcileLinkedAccounts(); }, 30_000);
|
|
74
|
+
this.reconcileTimer.unref();
|
|
75
|
+
}
|
|
76
|
+
if (strict)
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
this.reconcileChain = reconciliation.catch(() => { });
|
|
81
|
+
return reconciliation;
|
|
82
|
+
}
|
|
83
|
+
async close() {
|
|
84
|
+
this.stopped = true;
|
|
85
|
+
clearTimeout(this.reconcileTimer);
|
|
86
|
+
await this.reconcileChain;
|
|
87
|
+
try {
|
|
88
|
+
await Promise.all([this.live.close(), this.charts.close()]);
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
await this.journal?.close();
|
|
92
|
+
this.feeds.close();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async dashboardAccount(userId, id) {
|
|
96
|
+
const agent = await this.db.select().from(hvAgents).where(and(eq(hvAgents.id, agentId(id)), eq(hvAgents.userId, userId))).get();
|
|
97
|
+
if (!agent)
|
|
98
|
+
throw new HyperliquidError("Agent not found", 404);
|
|
99
|
+
if (agent.state === "stopping" || agent.state === "stopped")
|
|
100
|
+
throw new HyperliquidError("Agent is being deleted", 409);
|
|
101
|
+
if (!agent.tradingAccountAddress)
|
|
102
|
+
throw new HyperliquidError("Agent is missing its required Hyperliquid account", 409);
|
|
103
|
+
return agent.tradingAccountAddress;
|
|
104
|
+
}
|
|
105
|
+
/** Internal Agent-bound research capability; no exchange mutation or signing. */
|
|
106
|
+
async catchUpJournal(id, end, signal) {
|
|
107
|
+
const agent = await this.db.select({ account: hvAgents.tradingAccountAddress }).from(hvAgents).where(and(eq(hvAgents.id, id), sql `${hvAgents.state} NOT IN ('stopping', 'stopped')`)).get();
|
|
108
|
+
if (!agent?.account)
|
|
109
|
+
throw new HyperliquidError("Agent has no trading account", 409);
|
|
110
|
+
return this.journal ? this.journal.ensureThrough(agent.account, end, signal) : { attempted: false, limitation: "Journal synchronization is unavailable" };
|
|
111
|
+
}
|
|
112
|
+
async agentTradingAccount(userId, id) {
|
|
113
|
+
if (this.stopped)
|
|
114
|
+
throw new HyperliquidError("Hyperliquid service is closed", 503);
|
|
115
|
+
return this.dashboardAccount(userId, id);
|
|
116
|
+
}
|
|
117
|
+
/** Signing authority is resolved by the host, never from tool arguments. */
|
|
118
|
+
async orderAuthority(userId, id, signal, needsBuilder) {
|
|
119
|
+
const account = (await this.agentTradingAccount(userId, id))?.toLowerCase();
|
|
120
|
+
if (!account)
|
|
121
|
+
throw new HyperliquidError("Assign a trading account first.", 409);
|
|
122
|
+
const wallets = await this.db.select().from(hvWalletAddresses).where(eq(hvWalletAddresses.userId, userId)).all();
|
|
123
|
+
const primary = wallets.filter((wallet) => wallet.isPrimary);
|
|
124
|
+
const wallet = primary.length === 1 ? primary[0] : wallets.length === 1 ? wallets[0] : undefined;
|
|
125
|
+
if (!wallet || !/^0x[0-9a-fA-F]{40}$/.test(wallet.address))
|
|
126
|
+
throw new HyperliquidError("A unique owner wallet is required for order signing.", 409);
|
|
127
|
+
const owner = wallet.address.toLowerCase();
|
|
128
|
+
const info = new InfoClient({ transport: this.transport });
|
|
129
|
+
const role = await info.userRole({ user: account }, signal);
|
|
130
|
+
if (!(account === owner && role.role === "user") && !(role.role === "subAccount" && role.data.master.toLowerCase() === owner))
|
|
131
|
+
throw new HyperliquidError("Assigned trading account ownership could not be verified.", 409);
|
|
132
|
+
const row = await this.settings(userId);
|
|
133
|
+
const privateKey = await this.decryptSigner(userId);
|
|
134
|
+
const signer = privateKeyToAccount(privateKey);
|
|
135
|
+
if (signer.address.toLowerCase() !== row?.apiWalletAddress?.toLowerCase())
|
|
136
|
+
throw new HyperliquidError("Stored signer identity does not match its configuration.", 409);
|
|
137
|
+
const signerRole = await info.userRole({ user: signer.address }, signal);
|
|
138
|
+
if (signerRole.role !== "agent" || signerRole.data.user.toLowerCase() !== owner)
|
|
139
|
+
throw new HyperliquidError("Trading signer approval could not be verified on the exchange.", 409);
|
|
140
|
+
const fee = row?.builderFeeTenthsOfBp ?? 0;
|
|
141
|
+
const remoteFee = needsBuilder ? await maxBuilderFee(this.transport, owner, BUILDER_RECIPIENT, signal) : 0;
|
|
142
|
+
if (needsBuilder && (!row?.builderFeeApprovedAt || !Number.isInteger(fee) || fee < 1 || fee > MAX_BUILDER_FEE_TENTHS_OF_BP || !Number.isSafeInteger(remoteFee) || remoteFee < fee))
|
|
143
|
+
throw new HyperliquidError("Builder fee approval is missing or insufficient.", 409);
|
|
144
|
+
return { account, privateKey, ...(account === owner ? {} : { vaultAddress: account }), builder: { b: BUILDER_RECIPIENT, f: needsBuilder ? fee : 0 } };
|
|
145
|
+
}
|
|
146
|
+
async marketSnapshot(instrument, signal) {
|
|
147
|
+
if (this.stopped)
|
|
148
|
+
throw new HyperliquidError("Hyperliquid service is closed", 503);
|
|
149
|
+
return loadMarketSnapshot(this.transport, instrument, signal);
|
|
150
|
+
}
|
|
151
|
+
async agentAccountStatus(userId, id, signal) {
|
|
152
|
+
if (this.stopped)
|
|
153
|
+
throw new HyperliquidError("Hyperliquid service is closed", 503);
|
|
154
|
+
const address = await this.dashboardAccount(userId, id);
|
|
155
|
+
const cached = this.started && address ? this.live.snapshot(address) : undefined;
|
|
156
|
+
return loadAccountStatus(this.transport, address, cached, signal);
|
|
157
|
+
}
|
|
158
|
+
async agentDashboard(userId, id) {
|
|
159
|
+
const address = await this.dashboardAccount(userId, id);
|
|
160
|
+
const [snapshot, balanceHistory] = await Promise.all([
|
|
161
|
+
this.started && address ? this.live.snapshot(address) : loadAgentDashboard(this.transport, address),
|
|
162
|
+
address && this.journal ? this.journal.balanceHistory(address) : null,
|
|
163
|
+
]);
|
|
164
|
+
return { ...snapshot, balanceHistory };
|
|
165
|
+
}
|
|
166
|
+
async watchDashboard(userId, id, signal, publish) {
|
|
167
|
+
const address = await this.dashboardAccount(userId, id);
|
|
168
|
+
if (signal.aborted || this.stopped)
|
|
169
|
+
return;
|
|
170
|
+
if (!this.started || !address) {
|
|
171
|
+
const snapshot = await loadAgentDashboard(this.transport, null);
|
|
172
|
+
if (!signal.aborted && !this.stopped)
|
|
173
|
+
publish(snapshot);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
let balanceHistory = null;
|
|
177
|
+
let latest = this.live.snapshot(address);
|
|
178
|
+
let active = true;
|
|
179
|
+
let initialized = false;
|
|
180
|
+
let ready = () => { };
|
|
181
|
+
const initial = new Promise((resolve) => { ready = resolve; });
|
|
182
|
+
const unsubscribe = this.journal?.watchBalanceHistory(address, (history) => {
|
|
183
|
+
balanceHistory = history;
|
|
184
|
+
ready();
|
|
185
|
+
if (initialized && active && !signal.aborted && !this.stopped)
|
|
186
|
+
publish({ ...latest, balanceHistory });
|
|
187
|
+
});
|
|
188
|
+
// Also settle initialization if journal shutdown suppresses the first callback.
|
|
189
|
+
if (unsubscribe)
|
|
190
|
+
void unsubscribe.ready.then(ready, ready);
|
|
191
|
+
else
|
|
192
|
+
ready();
|
|
193
|
+
// Detach immediately on abort, rather than waiting for the live feed to settle.
|
|
194
|
+
const detach = () => { active = false; ready(); void unsubscribe?.(); };
|
|
195
|
+
signal.addEventListener("abort", detach, { once: true });
|
|
196
|
+
try {
|
|
197
|
+
await initial;
|
|
198
|
+
if (!active || signal.aborted || this.stopped)
|
|
199
|
+
return;
|
|
200
|
+
initialized = true;
|
|
201
|
+
await this.live.watch(address, signal, (snapshot) => {
|
|
202
|
+
latest = snapshot;
|
|
203
|
+
if (active && !signal.aborted && !this.stopped)
|
|
204
|
+
publish({ ...latest, balanceHistory });
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
active = false;
|
|
209
|
+
signal.removeEventListener("abort", detach);
|
|
210
|
+
await unsubscribe?.();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async agentJournal(userId, id, view, page) {
|
|
214
|
+
const address = await this.dashboardAccount(userId, id);
|
|
215
|
+
if (!this.journal)
|
|
216
|
+
throw new HyperliquidError("Journal is unavailable", 503);
|
|
217
|
+
return this.journal.page(address, view, page);
|
|
218
|
+
}
|
|
219
|
+
async chartFills(userId, id, coin, range) {
|
|
220
|
+
const address = await this.dashboardAccount(userId, id);
|
|
221
|
+
if (!this.journal)
|
|
222
|
+
throw new HyperliquidError("Journal is unavailable", 503);
|
|
223
|
+
return this.journal.chartFills(address, coin, range);
|
|
224
|
+
}
|
|
225
|
+
async journalNotes(userId, id, target) {
|
|
226
|
+
const address = await this.dashboardAccount(userId, id);
|
|
227
|
+
if (!address || !this.journal)
|
|
228
|
+
throw new HyperliquidError("Journal entry not found", 404);
|
|
229
|
+
return this.journal.notes(address, target);
|
|
230
|
+
}
|
|
231
|
+
async addJournalNote(userId, id, target, body) {
|
|
232
|
+
const address = await this.dashboardAccount(userId, id);
|
|
233
|
+
if (!address || !this.journal)
|
|
234
|
+
throw new HyperliquidError("Journal entry not found", 404);
|
|
235
|
+
await this.journal.addNote(address, target, userId, body);
|
|
236
|
+
}
|
|
237
|
+
async settings(userId) {
|
|
238
|
+
return this.db.select().from(hvHyperliquidSettings).where(eq(hvHyperliquidSettings.userId, userId)).get();
|
|
239
|
+
}
|
|
240
|
+
async ensureSettings(userId) {
|
|
241
|
+
const now = Date.now();
|
|
242
|
+
await this.db.insert(hvHyperliquidSettings).values({ userId, createdAt: now, updatedAt: now })
|
|
243
|
+
.onConflictDoNothing({ target: hvHyperliquidSettings.userId }).run();
|
|
244
|
+
const row = await this.settings(userId);
|
|
245
|
+
if (!row)
|
|
246
|
+
throw new HyperliquidError("Unable to store Hyperliquid settings", 503);
|
|
247
|
+
return row;
|
|
248
|
+
}
|
|
249
|
+
/** True when the trading signer exists, is approved and not known-expired. */
|
|
250
|
+
apiWalletIsReady(row) {
|
|
251
|
+
if (!row?.apiWalletAddress || !row.privateKeyCiphertext || !row.privateKeyKeyId || !row.apiWalletApprovedAt)
|
|
252
|
+
return false;
|
|
253
|
+
const now = Date.now();
|
|
254
|
+
if (row.apiWalletExpiresAt && row.apiWalletExpiresAt <= now)
|
|
255
|
+
return false;
|
|
256
|
+
if (row.apiWalletExpiresAt && !row.apiWalletExpiryCheckedAt)
|
|
257
|
+
return false;
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
apiWalletView(row) {
|
|
261
|
+
if (!row?.apiWalletAddress)
|
|
262
|
+
return null;
|
|
263
|
+
const expired = row.apiWalletExpiresAt !== null && row.apiWalletExpiresAt <= Date.now();
|
|
264
|
+
const state = !row.apiWalletApprovedAt
|
|
265
|
+
? "approval-required"
|
|
266
|
+
: expired || !this.apiWalletIsReady(row) ? "expired-or-unavailable" : "approved";
|
|
267
|
+
return {
|
|
268
|
+
address: row.apiWalletAddress,
|
|
269
|
+
state,
|
|
270
|
+
expiresAt: row.apiWalletExpiresAt,
|
|
271
|
+
showExpired: Boolean(row.apiWalletApprovedAt) && expired,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/** Generates or imports a trading signer and stores it encrypted. */
|
|
275
|
+
async storeApiWallet(userId, source, importedKey) {
|
|
276
|
+
if (!this.encryptionKey)
|
|
277
|
+
throw new HyperliquidError("Trading signer storage is not configured on the server", 503);
|
|
278
|
+
let privateKey;
|
|
279
|
+
if (source === "generate")
|
|
280
|
+
privateKey = generateWalletPrivateKey();
|
|
281
|
+
else if (source === "import" && importedKey.trim())
|
|
282
|
+
privateKey = importedKey.trim();
|
|
283
|
+
else if (source === "import")
|
|
284
|
+
throw new HyperliquidError("Private key is required.");
|
|
285
|
+
else
|
|
286
|
+
throw new HyperliquidError("Select an API wallet option.");
|
|
287
|
+
let address;
|
|
288
|
+
try {
|
|
289
|
+
address = privateKeyToAccount(privateKey).address.toLowerCase();
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
throw new HyperliquidError("Private key is invalid.");
|
|
293
|
+
}
|
|
294
|
+
const row = await this.ensureSettings(userId);
|
|
295
|
+
if (row.apiWalletAddress === address)
|
|
296
|
+
throw new HyperliquidError("Use a fresh trading signer address.");
|
|
297
|
+
const ciphertext = encryptSecret(this.encryptionKey, privateKey);
|
|
298
|
+
await this.db.update(hvHyperliquidSettings).set({
|
|
299
|
+
apiWalletAddress: address,
|
|
300
|
+
privateKeyCiphertext: Buffer.from(ciphertext),
|
|
301
|
+
privateKeyKeyId: this.encryptionKey.id,
|
|
302
|
+
apiWalletApprovedAt: null,
|
|
303
|
+
apiWalletExpiresAt: null,
|
|
304
|
+
apiWalletExpiryCheckedAt: null,
|
|
305
|
+
updatedAt: Date.now(),
|
|
306
|
+
}).where(eq(hvHyperliquidSettings.userId, userId)).run();
|
|
307
|
+
return address;
|
|
308
|
+
}
|
|
309
|
+
/** Relays a main-wallet-signed ApproveAgent action and records the approval. */
|
|
310
|
+
async approveApiWallet(user, request) {
|
|
311
|
+
const signed = parseSignedAction(request);
|
|
312
|
+
const row = await this.settings(user.id);
|
|
313
|
+
if (!row?.apiWalletAddress)
|
|
314
|
+
throw new HyperliquidError("Set up a trading signer first.");
|
|
315
|
+
if (!validApproveAgentAction(signed.action, row.apiWalletAddress)
|
|
316
|
+
|| !(await signatureMatches(signed.action, signed.signature, user.walletAddress, "approveAgent"))) {
|
|
317
|
+
throw new HyperliquidError("Invalid trading signer approval.");
|
|
318
|
+
}
|
|
319
|
+
const exchange = await this.relay(signed);
|
|
320
|
+
if (!exchangeSuccess(exchange)) {
|
|
321
|
+
throw new HyperliquidError(exchangeErrorMessage(exchange), 502);
|
|
322
|
+
}
|
|
323
|
+
const now = Date.now();
|
|
324
|
+
await this.db.update(hvHyperliquidSettings).set({
|
|
325
|
+
apiWalletApprovedAt: now,
|
|
326
|
+
apiWalletExpiresAt: now + API_WALLET_APPROVAL_MS,
|
|
327
|
+
apiWalletExpiryCheckedAt: now,
|
|
328
|
+
updatedAt: now,
|
|
329
|
+
}).where(and(eq(hvHyperliquidSettings.userId, user.id), isNotNull(hvHyperliquidSettings.apiWalletAddress), isNotNull(hvHyperliquidSettings.privateKeyCiphertext), isNotNull(hvHyperliquidSettings.privateKeyKeyId))).run();
|
|
330
|
+
const approved = await this.settings(user.id);
|
|
331
|
+
if (!approved?.apiWalletApprovedAt)
|
|
332
|
+
throw new HyperliquidError("Trading signer approval conflict", 409);
|
|
333
|
+
}
|
|
334
|
+
/** Relays a main-wallet-signed ApproveBuilderFee action and stores the fee. */
|
|
335
|
+
async approveBuilderFee(user, request, feeBps) {
|
|
336
|
+
const maxFeeRate = `${(feeBps / 100).toFixed(2)}%`;
|
|
337
|
+
if (!Number.isSafeInteger(feeBps) || feeBps < MIN_BUILDER_FEE_BPS || feeBps > MAX_BUILDER_FEE_BPS) {
|
|
338
|
+
throw new HyperliquidError("Builder fee must be between 1 and 10 basis points.");
|
|
339
|
+
}
|
|
340
|
+
await this.relayBuilderFee(user, request, maxFeeRate);
|
|
341
|
+
await this.ensureSettings(user.id);
|
|
342
|
+
await this.db.update(hvHyperliquidSettings).set({
|
|
343
|
+
builderFeeTenthsOfBp: feeBps * BUILDER_FEE_BPS_TO_TENTHS,
|
|
344
|
+
builderFeeApprovedAt: Date.now(),
|
|
345
|
+
updatedAt: Date.now(),
|
|
346
|
+
}).where(eq(hvHyperliquidSettings.userId, user.id)).run();
|
|
347
|
+
}
|
|
348
|
+
/** Relays a main-wallet-signed builder-fee revocation. */
|
|
349
|
+
async cancelBuilderFee(user, request) {
|
|
350
|
+
await this.relayBuilderFee(user, request, REVOKED_BUILDER_FEE_RATE);
|
|
351
|
+
await this.db.update(hvHyperliquidSettings).set({ builderFeeApprovedAt: null, updatedAt: Date.now() })
|
|
352
|
+
.where(eq(hvHyperliquidSettings.userId, user.id)).run();
|
|
353
|
+
}
|
|
354
|
+
async relayBuilderFee(user, request, maxFeeRate) {
|
|
355
|
+
const signed = parseSignedAction(request);
|
|
356
|
+
if (!validApproveBuilderFeeAction(signed.action, maxFeeRate)
|
|
357
|
+
|| !(await signatureMatches(signed.action, signed.signature, user.walletAddress, "approveBuilderFee"))) {
|
|
358
|
+
throw new HyperliquidError("Invalid builder fee approval.");
|
|
359
|
+
}
|
|
360
|
+
const exchange = await this.relay(signed);
|
|
361
|
+
if (!exchangeSuccess(exchange)) {
|
|
362
|
+
throw new HyperliquidError(exchangeErrorMessage(exchange), 502);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
async relay(signed) {
|
|
366
|
+
try {
|
|
367
|
+
return await relaySignedAction(this.transport, signed.action, signed.signature);
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
throw new HyperliquidError("Hyperliquid exchange unavailable", 502);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/** Reconciles the stored builder fee with Hyperliquid's current maximum. */
|
|
374
|
+
async reconcileBuilderFee(user) {
|
|
375
|
+
const row = await this.settings(user.id);
|
|
376
|
+
const saved = row?.builderFeeTenthsOfBp ?? 0;
|
|
377
|
+
try {
|
|
378
|
+
const remote = await maxBuilderFee(this.transport, user.walletAddress, BUILDER_RECIPIENT);
|
|
379
|
+
const capped = Math.min(remote, MAX_BUILDER_FEE_TENTHS_OF_BP);
|
|
380
|
+
const tenths = capped > 0 && capped !== saved ? capped : saved;
|
|
381
|
+
// Coverage must be judged against the value synced from Hyperliquid,
|
|
382
|
+
// never against a stale pre-sync value: a first visit by a user who
|
|
383
|
+
// already approved the fee on Hyperliquid must show as approved.
|
|
384
|
+
const covered = tenths > 0 && remote >= tenths;
|
|
385
|
+
if (tenths !== saved) {
|
|
386
|
+
await this.ensureSettings(user.id);
|
|
387
|
+
await this.db.update(hvHyperliquidSettings).set({ builderFeeTenthsOfBp: tenths, updatedAt: Date.now() })
|
|
388
|
+
.where(eq(hvHyperliquidSettings.userId, user.id)).run();
|
|
389
|
+
}
|
|
390
|
+
if (covered && !row?.builderFeeApprovedAt) {
|
|
391
|
+
await this.ensureSettings(user.id);
|
|
392
|
+
await this.db.update(hvHyperliquidSettings).set({ builderFeeApprovedAt: Date.now(), updatedAt: Date.now() })
|
|
393
|
+
.where(and(eq(hvHyperliquidSettings.userId, user.id), isNull(hvHyperliquidSettings.builderFeeApprovedAt))).run();
|
|
394
|
+
}
|
|
395
|
+
else if (!covered && row?.builderFeeApprovedAt) {
|
|
396
|
+
await this.db.update(hvHyperliquidSettings).set({ builderFeeApprovedAt: null, updatedAt: Date.now() })
|
|
397
|
+
.where(and(eq(hvHyperliquidSettings.userId, user.id), isNotNull(hvHyperliquidSettings.builderFeeApprovedAt))).run();
|
|
398
|
+
}
|
|
399
|
+
const feeBps = tenths > 0 && tenths % BUILDER_FEE_BPS_TO_TENTHS === 0
|
|
400
|
+
? Math.min(Math.max(tenths / BUILDER_FEE_BPS_TO_TENTHS, MIN_BUILDER_FEE_BPS), MAX_BUILDER_FEE_BPS)
|
|
401
|
+
: DEFAULT_BUILDER_FEE_BPS;
|
|
402
|
+
return { feeBps, approved: covered };
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
// Keep the last known approval when the lookup fails.
|
|
406
|
+
return { feeBps: this.displayFeeBps(saved), approved: Boolean(row?.builderFeeApprovedAt) };
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
displayFeeBps(tenths) {
|
|
410
|
+
return tenths > 0 && tenths % BUILDER_FEE_BPS_TO_TENTHS === 0
|
|
411
|
+
? Math.min(Math.max(tenths / BUILDER_FEE_BPS_TO_TENTHS, MIN_BUILDER_FEE_BPS), MAX_BUILDER_FEE_BPS)
|
|
412
|
+
: DEFAULT_BUILDER_FEE_BPS;
|
|
413
|
+
}
|
|
414
|
+
/** Assembles the full Account page view for a signed-in wallet. */
|
|
415
|
+
async accountView(user) {
|
|
416
|
+
const [row, accounts, builderFee] = await Promise.all([
|
|
417
|
+
this.settings(user.id),
|
|
418
|
+
loadOwnedTradingAccounts(this.transport, user.walletAddress, { includeCapacity: false }),
|
|
419
|
+
this.reconcileBuilderFee(user),
|
|
420
|
+
]);
|
|
421
|
+
const assignments = await this.db.select({
|
|
422
|
+
address: hvAgents.tradingAccountAddress,
|
|
423
|
+
agentId: hvAgents.id,
|
|
424
|
+
agentName: hvAgents.name,
|
|
425
|
+
}).from(hvAgents).where(isNotNull(hvAgents.tradingAccountAddress)).all();
|
|
426
|
+
const assignmentFor = (address) => assignments
|
|
427
|
+
.find((assignment) => assignment.address?.toLowerCase() === address.toLowerCase());
|
|
428
|
+
const rows = [this.accountRow("Main Account", accounts.mainAddress, accounts.mainBalance, assignmentFor)];
|
|
429
|
+
for (const subaccount of accounts.subaccounts) {
|
|
430
|
+
rows.push(this.accountRow(subaccount.name ?? "Unnamed subaccount", subaccount.address, subaccount.balance, assignmentFor));
|
|
431
|
+
}
|
|
432
|
+
const total = rows.length > 1 ? this.totalBalance(accounts) : null;
|
|
433
|
+
return {
|
|
434
|
+
walletAddress: user.walletAddress,
|
|
435
|
+
accountMode: accounts.accountMode,
|
|
436
|
+
accountModeMessage: accountModeError(accounts.accountMode),
|
|
437
|
+
encryptionConfigured: Boolean(this.encryptionKey),
|
|
438
|
+
apiWallet: this.apiWalletView(row),
|
|
439
|
+
builderFee,
|
|
440
|
+
accounts: rows,
|
|
441
|
+
totalBalance: total,
|
|
442
|
+
lookupError: accounts.lookupError,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
accountRow(name, address, balance, assignmentFor) {
|
|
446
|
+
const assignment = assignmentFor(address);
|
|
447
|
+
return {
|
|
448
|
+
name,
|
|
449
|
+
address,
|
|
450
|
+
agentId: assignment?.agentId ?? null,
|
|
451
|
+
agentName: assignment?.agentName ?? null,
|
|
452
|
+
balance,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
totalBalance(accounts) {
|
|
456
|
+
let total = "0";
|
|
457
|
+
for (const balance of [accounts.mainBalance, ...accounts.subaccounts.map((subaccount) => subaccount.balance)]) {
|
|
458
|
+
if (!balance)
|
|
459
|
+
return null;
|
|
460
|
+
total = addDecimals(total, balance);
|
|
461
|
+
if (!total)
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
return total;
|
|
465
|
+
}
|
|
466
|
+
/** Account choices for the Create Agent page. */
|
|
467
|
+
async tradingAccountChoices(user) {
|
|
468
|
+
const [row, accounts] = await Promise.all([
|
|
469
|
+
this.settings(user.id),
|
|
470
|
+
loadOwnedTradingAccounts(this.transport, user.walletAddress, { includeCapacity: true }),
|
|
471
|
+
]);
|
|
472
|
+
const assignments = await this.db.select({
|
|
473
|
+
address: hvAgents.tradingAccountAddress,
|
|
474
|
+
agentId: hvAgents.id,
|
|
475
|
+
agentName: hvAgents.name,
|
|
476
|
+
}).from(hvAgents).where(isNotNull(hvAgents.tradingAccountAddress)).all();
|
|
477
|
+
const assignmentFor = (address) => assignments
|
|
478
|
+
.find((assignment) => assignment.address?.toLowerCase() === address.toLowerCase());
|
|
479
|
+
const mainAssignment = assignmentFor(accounts.mainAddress);
|
|
480
|
+
return {
|
|
481
|
+
mainAddress: accounts.mainAddress,
|
|
482
|
+
mainBalance: accounts.mainBalance,
|
|
483
|
+
mainAssignedTo: mainAssignment?.agentName ?? null,
|
|
484
|
+
subaccounts: accounts.subaccounts.map((subaccount) => ({
|
|
485
|
+
name: subaccount.name,
|
|
486
|
+
address: subaccount.address,
|
|
487
|
+
balance: subaccount.balance,
|
|
488
|
+
assignedTo: assignmentFor(subaccount.address) ?? null,
|
|
489
|
+
})),
|
|
490
|
+
subaccountCapacity: accounts.subaccountCapacity,
|
|
491
|
+
lookupError: accounts.lookupError ?? accountModeError(accounts.accountMode),
|
|
492
|
+
accountModeSupported: accounts.accountMode === "unified",
|
|
493
|
+
apiWalletReady: this.apiWalletIsReady(row),
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
/** Creates a Hyperliquid sub-account for an agent using the trading signer. */
|
|
497
|
+
async createAgentSubaccount(user, displayName) {
|
|
498
|
+
const name = agentSubaccountName(displayName);
|
|
499
|
+
if (!name || name === "vt-")
|
|
500
|
+
throw new HyperliquidError("Enter an agent name first.");
|
|
501
|
+
const privateKey = await this.decryptSigner(user.id);
|
|
502
|
+
const result = await this.creator.create(user.walletAddress.toLowerCase(), name, privateKey);
|
|
503
|
+
if (!result.ok) {
|
|
504
|
+
throw new HyperliquidError(result.failure.kind === "rejected" ? result.failure.message
|
|
505
|
+
: result.failure.kind === "lookup-unavailable" ? "Hyperliquid account data is temporarily unavailable."
|
|
506
|
+
: "Sub-Account creation could not be confirmed. Refresh the account list before retrying.", result.failure.kind === "rejected" ? 422 : 502);
|
|
507
|
+
}
|
|
508
|
+
return result.subaccount;
|
|
509
|
+
}
|
|
510
|
+
/** Loads the decrypted trading signer for exchange signing only. */
|
|
511
|
+
async decryptSigner(userId) {
|
|
512
|
+
if (!this.encryptionKey)
|
|
513
|
+
throw new HyperliquidError("Trading signer storage is not configured on the server", 503);
|
|
514
|
+
const row = await this.settings(userId);
|
|
515
|
+
if (!row || !this.apiWalletIsReady(row))
|
|
516
|
+
throw new HyperliquidError("Set up and approve a trading signer first.", 409);
|
|
517
|
+
if (!row.privateKeyCiphertext)
|
|
518
|
+
throw new HyperliquidError("The stored trading signer is incomplete.", 409);
|
|
519
|
+
if (row.privateKeyKeyId !== this.encryptionKey.id) {
|
|
520
|
+
throw new HyperliquidError("The stored trading signer uses an unknown encryption key.", 409);
|
|
521
|
+
}
|
|
522
|
+
try {
|
|
523
|
+
return decryptSecret(this.encryptionKey, new Uint8Array(row.privateKeyCiphertext));
|
|
524
|
+
}
|
|
525
|
+
catch {
|
|
526
|
+
throw new HyperliquidError("The stored trading signer could not be decrypted.", 409);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { SubscriptionClient, WebSocketTransport } from "@nktkas/hyperliquid";
|
|
2
|
+
import { openAccountFeed } from "./live-dashboard.js";
|
|
3
|
+
import { openJournalFeed } from "./journal-service.js";
|
|
4
|
+
/** A single lazy, app-owned exchange socket shared by account and candle feeds. */
|
|
5
|
+
export class SharedHyperliquidFeeds {
|
|
6
|
+
transport;
|
|
7
|
+
stopped = false;
|
|
8
|
+
socket() {
|
|
9
|
+
if (this.stopped)
|
|
10
|
+
throw new Error("Exchange feeds are closed");
|
|
11
|
+
return this.transport ??= new WebSocketTransport();
|
|
12
|
+
}
|
|
13
|
+
account = (address, events) => openAccountFeed(address, events, this.socket());
|
|
14
|
+
journal = (address, events) => openJournalFeed(address, events, this.socket());
|
|
15
|
+
candle = (selection, events) => {
|
|
16
|
+
const transport = this.socket();
|
|
17
|
+
const abort = new AbortController();
|
|
18
|
+
const connected = () => events.connected();
|
|
19
|
+
const disconnected = () => events.disconnected();
|
|
20
|
+
transport.socket.addEventListener("open", connected);
|
|
21
|
+
transport.socket.addEventListener("close", disconnected);
|
|
22
|
+
if (transport.socket.readyState === 1)
|
|
23
|
+
queueMicrotask(() => { if (!abort.signal.aborted)
|
|
24
|
+
connected(); });
|
|
25
|
+
const task = new SubscriptionClient({ transport }).candle(selection, (candle) => { if (!abort.signal.aborted)
|
|
26
|
+
events.update(candle); }, { signal: abort.signal, onError: () => { if (!abort.signal.aborted)
|
|
27
|
+
events.failed(); } });
|
|
28
|
+
void task.catch(() => { if (!abort.signal.aborted)
|
|
29
|
+
events.failed(); });
|
|
30
|
+
return { async close() {
|
|
31
|
+
transport.socket.removeEventListener("open", connected);
|
|
32
|
+
transport.socket.removeEventListener("close", disconnected);
|
|
33
|
+
abort.abort();
|
|
34
|
+
const [result] = await Promise.allSettled([task]);
|
|
35
|
+
if (result?.status === "fulfilled")
|
|
36
|
+
await result.value.unsubscribe().catch(() => { });
|
|
37
|
+
} };
|
|
38
|
+
};
|
|
39
|
+
close() { this.stopped = true; this.transport?.close(); }
|
|
40
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { isAddress, recoverTypedDataAddress } from "viem";
|
|
2
|
+
import { BUILDER_RECIPIENT, SIGNATURE_CHAIN_ID, USER_API_WALLET_NAME, } from "./constants.js";
|
|
3
|
+
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
4
|
+
const FIELD_SETS = {
|
|
5
|
+
approveAgent: ["type", "hyperliquidChain", "signatureChainId", "agentAddress", "agentName", "nonce"],
|
|
6
|
+
approveBuilderFee: ["type", "hyperliquidChain", "signatureChainId", "maxFeeRate", "builder", "nonce"],
|
|
7
|
+
};
|
|
8
|
+
/** Narrow, validate and sanitize a client request body into a SignedAction. */
|
|
9
|
+
export function parseSignedAction(value) {
|
|
10
|
+
if (typeof value !== "object" || value === null)
|
|
11
|
+
throw new Error("Invalid request");
|
|
12
|
+
const action = Reflect.get(value, "action");
|
|
13
|
+
const signature = Reflect.get(value, "signature");
|
|
14
|
+
if (typeof action !== "object" || action === null || Array.isArray(action))
|
|
15
|
+
throw new Error("Invalid request");
|
|
16
|
+
if (typeof signature !== "string" || !/^0x[0-9a-fA-F]{130}$/.test(signature))
|
|
17
|
+
throw new Error("Invalid request");
|
|
18
|
+
return { action: { ...action }, signature };
|
|
19
|
+
}
|
|
20
|
+
function hasExactFields(action, kind) {
|
|
21
|
+
const expected = FIELD_SETS[kind];
|
|
22
|
+
const keys = Object.keys(action);
|
|
23
|
+
return keys.length === expected.length && expected.every((field) => field in action);
|
|
24
|
+
}
|
|
25
|
+
function validCommon(action) {
|
|
26
|
+
const nonce = action.nonce;
|
|
27
|
+
return action.hyperliquidChain === "Mainnet"
|
|
28
|
+
&& action.signatureChainId === "0xa4b1"
|
|
29
|
+
&& typeof nonce === "number" && Number.isSafeInteger(nonce) && nonce > 0;
|
|
30
|
+
}
|
|
31
|
+
function isLowercaseAddress(value) {
|
|
32
|
+
return typeof value === "string" && isAddress(value) && value === value.toLowerCase();
|
|
33
|
+
}
|
|
34
|
+
/** The action must approve exactly this server-generated trading signer. */
|
|
35
|
+
export function validApproveAgentAction(action, expectedAddress) {
|
|
36
|
+
return hasExactFields(action, "approveAgent")
|
|
37
|
+
&& validCommon(action)
|
|
38
|
+
&& action.type === "approveAgent"
|
|
39
|
+
&& action.agentName === USER_API_WALLET_NAME
|
|
40
|
+
&& isLowercaseAddress(action.agentAddress)
|
|
41
|
+
&& action.agentAddress === expectedAddress;
|
|
42
|
+
}
|
|
43
|
+
/** The action must approve or revoke exactly this server's builder at the expected rate. */
|
|
44
|
+
export function validApproveBuilderFeeAction(action, maxFeeRate) {
|
|
45
|
+
return hasExactFields(action, "approveBuilderFee")
|
|
46
|
+
&& validCommon(action)
|
|
47
|
+
&& action.type === "approveBuilderFee"
|
|
48
|
+
&& action.builder === BUILDER_RECIPIENT
|
|
49
|
+
&& action.maxFeeRate === maxFeeRate;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Recovers the signing address from an EIP-712 Hyperliquid action signature and
|
|
53
|
+
* compares it with the expected address. Invalid structures never match.
|
|
54
|
+
*/
|
|
55
|
+
export async function signatureMatches(action, signature, expectedAddress, kind) {
|
|
56
|
+
const primaryType = kind === "approveAgent"
|
|
57
|
+
? "HyperliquidTransaction:ApproveAgent"
|
|
58
|
+
: "HyperliquidTransaction:ApproveBuilderFee";
|
|
59
|
+
try {
|
|
60
|
+
const recovered = await recoverTypedDataAddress({
|
|
61
|
+
domain: {
|
|
62
|
+
name: "HyperliquidSignTransaction",
|
|
63
|
+
version: "1",
|
|
64
|
+
chainId: SIGNATURE_CHAIN_ID,
|
|
65
|
+
verifyingContract: ZERO_ADDRESS,
|
|
66
|
+
},
|
|
67
|
+
types: kind === "approveAgent"
|
|
68
|
+
? { [primaryType]: [
|
|
69
|
+
{ name: "hyperliquidChain", type: "string" },
|
|
70
|
+
{ name: "agentAddress", type: "address" },
|
|
71
|
+
{ name: "agentName", type: "string" },
|
|
72
|
+
{ name: "nonce", type: "uint64" },
|
|
73
|
+
] }
|
|
74
|
+
: { [primaryType]: [
|
|
75
|
+
{ name: "hyperliquidChain", type: "string" },
|
|
76
|
+
{ name: "maxFeeRate", type: "string" },
|
|
77
|
+
{ name: "builder", type: "address" },
|
|
78
|
+
{ name: "nonce", type: "uint64" },
|
|
79
|
+
] },
|
|
80
|
+
primaryType,
|
|
81
|
+
message: action,
|
|
82
|
+
signature: signature,
|
|
83
|
+
});
|
|
84
|
+
return recovered.toLowerCase() === expectedAddress.toLowerCase();
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|