dexterm-cli 0.0.0-stage → 0.1.1
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/LICENSE +661 -0
- package/dist/packages/agents/src/audit.js +122 -0
- package/dist/packages/agents/src/bus.js +379 -0
- package/dist/packages/agents/src/executor.js +177 -0
- package/dist/packages/agents/src/gate.js +176 -0
- package/dist/packages/agents/src/identity.js +179 -0
- package/dist/packages/agents/src/index.js +15 -0
- package/dist/packages/agents/src/proposals.js +153 -0
- package/dist/packages/agents/src/schema.js +87 -0
- package/dist/packages/agents/src/sdk.js +140 -0
- package/dist/packages/agents/src/tiers.js +114 -0
- package/dist/packages/agents/src/tools.js +69 -0
- package/dist/packages/agents/src/ws.js +158 -0
- package/dist/packages/cli/assets/claude/analyst-system.md +76 -0
- package/dist/packages/cli/assets/claude/plugin/.claude-plugin/plugin.json +5 -0
- package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +16 -0
- package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +67 -0
- package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +64 -0
- package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +63 -0
- package/dist/packages/cli/assets/claude/plugin/skills/trench-scan/SKILL.md +44 -0
- package/dist/packages/cli/assets/claude/schemas/larp.schema.json +117 -0
- package/dist/packages/cli/assets/claude/schemas/trench-scan.schema.json +71 -0
- package/dist/packages/cli/assets/claude/schemas/verdict.schema.json +115 -0
- package/dist/packages/cli/bin/dexterm.mjs +11 -0
- package/dist/packages/cli/src/agent-cmd.js +303 -0
- package/dist/packages/cli/src/agent-roster.js +26 -0
- package/dist/packages/cli/src/analyst/backend.js +526 -0
- package/dist/packages/cli/src/analyst/results.js +38 -0
- package/dist/packages/cli/src/analyst/session.js +319 -0
- package/dist/packages/cli/src/audit.js +51 -0
- package/dist/packages/cli/src/backtest-cmd.js +149 -0
- package/dist/packages/cli/src/boot-state.js +62 -0
- package/dist/packages/cli/src/custom.js +261 -0
- package/dist/packages/cli/src/display.js +191 -0
- package/dist/packages/cli/src/doctor.js +361 -0
- package/dist/packages/cli/src/feed.js +634 -0
- package/dist/packages/cli/src/fixtures.js +275 -0
- package/dist/packages/cli/src/follow-cmd.js +221 -0
- package/dist/packages/cli/src/images-config.js +59 -0
- package/dist/packages/cli/src/index.js +31 -0
- package/dist/packages/cli/src/kb-skill-cmd.js +240 -0
- package/dist/packages/cli/src/larp-record.js +21 -0
- package/dist/packages/cli/src/live/copy.js +80 -0
- package/dist/packages/cli/src/live/flow.js +233 -0
- package/dist/packages/cli/src/live/index.js +509 -0
- package/dist/packages/cli/src/live/intel.js +227 -0
- package/dist/packages/cli/src/live/newpairs.js +334 -0
- package/dist/packages/cli/src/live/screens/newpairs-live.js +219 -0
- package/dist/packages/cli/src/live/screens/token-live.js +459 -0
- package/dist/packages/cli/src/live/screens/watchlist-live.js +149 -0
- package/dist/packages/cli/src/live/token.js +585 -0
- package/dist/packages/cli/src/live/trade.js +929 -0
- package/dist/packages/cli/src/live/watchlist.js +178 -0
- package/dist/packages/cli/src/main.js +5583 -0
- package/dist/packages/cli/src/narrative-cmd.js +76 -0
- package/dist/packages/cli/src/pack-cmd.js +323 -0
- package/dist/packages/cli/src/plan-cmd.js +278 -0
- package/dist/packages/cli/src/popup.js +810 -0
- package/dist/packages/cli/src/rules-cmd.js +131 -0
- package/dist/packages/cli/src/rules.js +339 -0
- package/dist/packages/cli/src/scan/command.js +57 -0
- package/dist/packages/cli/src/scan/engine.js +419 -0
- package/dist/packages/cli/src/scan/observe.js +64 -0
- package/dist/packages/cli/src/scan/ui.js +98 -0
- package/dist/packages/cli/src/screener/filter.js +241 -0
- package/dist/packages/cli/src/screener/presets.js +80 -0
- package/dist/packages/cli/src/screener/rows.js +146 -0
- package/dist/packages/cli/src/screener/rug.js +50 -0
- package/dist/packages/cli/src/screener/sort.js +52 -0
- package/dist/packages/cli/src/screener/view.js +183 -0
- package/dist/packages/cli/src/screens/analyst-session.js +343 -0
- package/dist/packages/cli/src/screens/analyst.js +326 -0
- package/dist/packages/cli/src/screens/boot.js +74 -0
- package/dist/packages/cli/src/screens/bubbles.js +255 -0
- package/dist/packages/cli/src/screens/coin.js +33 -0
- package/dist/packages/cli/src/screens/follows.js +201 -0
- package/dist/packages/cli/src/screens/kol.js +90 -0
- package/dist/packages/cli/src/screens/narrative.js +90 -0
- package/dist/packages/cli/src/screens/newpairs.js +150 -0
- package/dist/packages/cli/src/screens/overlay.js +257 -0
- package/dist/packages/cli/src/screens/plans.js +126 -0
- package/dist/packages/cli/src/screens/positions.js +176 -0
- package/dist/packages/cli/src/screens/rules.js +170 -0
- package/dist/packages/cli/src/screens/screener.js +459 -0
- package/dist/packages/cli/src/screens/social-panel.js +107 -0
- package/dist/packages/cli/src/screens/ticket.js +342 -0
- package/dist/packages/cli/src/screens/token.js +312 -0
- package/dist/packages/cli/src/screens/trending.js +181 -0
- package/dist/packages/cli/src/screens/watchlist.js +104 -0
- package/dist/packages/cli/src/social/demo.js +96 -0
- package/dist/packages/cli/src/social/view.js +250 -0
- package/dist/packages/cli/src/state.js +463 -0
- package/dist/packages/cli/src/theme-cmd.js +126 -0
- package/dist/packages/cli/src/trade.js +319 -0
- package/dist/packages/cli/src/voice-hud.js +42 -0
- package/dist/packages/cli/src/wallet-cmd.js +440 -0
- package/dist/packages/cli/src/wallet-command.js +31 -0
- package/dist/packages/cli/src/widgets.js +208 -0
- package/dist/packages/cli/src/workspace-cmd.js +68 -0
- package/dist/packages/cli/src/x-cmd.js +154 -0
- package/dist/packages/custom/src/config.js +213 -0
- package/dist/packages/custom/src/index.js +10 -0
- package/dist/packages/custom/src/keymap.js +107 -0
- package/dist/packages/custom/src/layout.js +235 -0
- package/dist/packages/custom/src/packs.js +633 -0
- package/dist/packages/custom/src/rules.js +704 -0
- package/dist/packages/custom/src/schema.js +515 -0
- package/dist/packages/custom/src/themes.js +176 -0
- package/dist/packages/custom/src/widget-html.js +222 -0
- package/dist/packages/custom/src/workspace.js +141 -0
- package/dist/packages/data/src/analyst-actions.js +133 -0
- package/dist/packages/data/src/budget.js +88 -0
- package/dist/packages/data/src/cache.js +142 -0
- package/dist/packages/data/src/candles.js +168 -0
- package/dist/packages/data/src/clock.js +64 -0
- package/dist/packages/data/src/credits.js +137 -0
- package/dist/packages/data/src/detail.js +97 -0
- package/dist/packages/data/src/fast-lane.js +321 -0
- package/dist/packages/data/src/fixtures.js +135 -0
- package/dist/packages/data/src/health.js +245 -0
- package/dist/packages/data/src/http.js +277 -0
- package/dist/packages/data/src/images/cache.js +141 -0
- package/dist/packages/data/src/images/decode-worker.js +28 -0
- package/dist/packages/data/src/images/decode.js +81 -0
- package/dist/packages/data/src/images/fetch.js +162 -0
- package/dist/packages/data/src/images/gif.js +214 -0
- package/dist/packages/data/src/images/index.js +10 -0
- package/dist/packages/data/src/images/jpeg.js +544 -0
- package/dist/packages/data/src/images/png.js +310 -0
- package/dist/packages/data/src/images/service.js +334 -0
- package/dist/packages/data/src/images/types.js +27 -0
- package/dist/packages/data/src/index.js +208 -0
- package/dist/packages/data/src/intel/helius-history.js +59 -0
- package/dist/packages/data/src/intel/holder-class.js +125 -0
- package/dist/packages/data/src/intel/rules.js +421 -0
- package/dist/packages/data/src/intel/scan.js +170 -0
- package/dist/packages/data/src/larp/evidence.js +288 -0
- package/dist/packages/data/src/larp/links.js +137 -0
- package/dist/packages/data/src/larp/robots.js +90 -0
- package/dist/packages/data/src/larp/safe-fetch.js +389 -0
- package/dist/packages/data/src/limiter.js +341 -0
- package/dist/packages/data/src/model.js +12 -0
- package/dist/packages/data/src/paper.js +331 -0
- package/dist/packages/data/src/rpc-config.js +126 -0
- package/dist/packages/data/src/rpc-pool.js +166 -0
- package/dist/packages/data/src/social/chain.js +83 -0
- package/dist/packages/data/src/social/dedupe.js +205 -0
- package/dist/packages/data/src/social/feed.js +462 -0
- package/dist/packages/data/src/social/firewall.js +110 -0
- package/dist/packages/data/src/social/index.js +11 -0
- package/dist/packages/data/src/social/kol.js +269 -0
- package/dist/packages/data/src/social/model.js +11 -0
- package/dist/packages/data/src/social/rank.js +184 -0
- package/dist/packages/data/src/social/rss.js +113 -0
- package/dist/packages/data/src/social/summary.js +104 -0
- package/dist/packages/data/src/social/venues.js +186 -0
- package/dist/packages/data/src/sources/dexscreener.js +170 -0
- package/dist/packages/data/src/sources/geckoterminal.js +133 -0
- package/dist/packages/data/src/sources/goplus.js +161 -0
- package/dist/packages/data/src/sources/jupiter-swap.js +236 -0
- package/dist/packages/data/src/sources/jupiter.js +198 -0
- package/dist/packages/data/src/sources/pda.js +113 -0
- package/dist/packages/data/src/sources/pump-curve.js +58 -0
- package/dist/packages/data/src/sources/pumpportal.js +247 -0
- package/dist/packages/data/src/sources/rugcheck.js +71 -0
- package/dist/packages/data/src/sources/solana-rpc.js +299 -0
- package/dist/packages/data/src/sources/solana-ws.js +287 -0
- package/dist/packages/data/src/store.js +289 -0
- package/dist/packages/data/src/trade/bridge.js +464 -0
- package/dist/packages/data/src/trade/caps.js +323 -0
- package/dist/packages/data/src/trade/copy/auto.js +385 -0
- package/dist/packages/data/src/trade/copy/follows.js +103 -0
- package/dist/packages/data/src/trade/copy/index.js +9 -0
- package/dist/packages/data/src/trade/copy/mirror.js +91 -0
- package/dist/packages/data/src/trade/copy/stats.js +120 -0
- package/dist/packages/data/src/trade/copy/tracker.js +158 -0
- package/dist/packages/data/src/trade/copy/trades.js +87 -0
- package/dist/packages/data/src/trade/copy/types.js +6 -0
- package/dist/packages/data/src/trade/executor.js +933 -0
- package/dist/packages/data/src/trade/kill-switch.js +47 -0
- package/dist/packages/data/src/trade/link-store.js +52 -0
- package/dist/packages/data/src/trade/plans/confirm.js +157 -0
- package/dist/packages/data/src/trade/plans/engine.js +784 -0
- package/dist/packages/data/src/trade/plans/executor-live.js +220 -0
- package/dist/packages/data/src/trade/plans/executor-paper.js +44 -0
- package/dist/packages/data/src/trade/plans/executor.js +11 -0
- package/dist/packages/data/src/trade/plans/index.js +12 -0
- package/dist/packages/data/src/trade/plans/journal.js +77 -0
- package/dist/packages/data/src/trade/plans/mandate.js +129 -0
- package/dist/packages/data/src/trade/plans/plan.js +179 -0
- package/dist/packages/data/src/trade/plans/service.js +168 -0
- package/dist/packages/data/src/trade/plans/types.js +6 -0
- package/dist/packages/data/src/trade/rpc-exec.js +229 -0
- package/dist/packages/data/src/trade/signer/client.js +197 -0
- package/dist/packages/data/src/trade/signer/fast-wallet.js +154 -0
- package/dist/packages/data/src/trade/signer/host.js +97 -0
- package/dist/packages/data/src/trade/signer/main.js +121 -0
- package/dist/packages/data/src/trade/signer/probe-builder.js +85 -0
- package/dist/packages/data/src/trade/signer/protocol.js +92 -0
- package/dist/packages/data/src/trade/signer/service.js +400 -0
- package/dist/packages/data/src/trade/signer/sol-usd-feed.js +98 -0
- package/dist/packages/data/src/trade/sol-usd.js +86 -0
- package/dist/packages/data/src/trade/swap-build.js +108 -0
- package/dist/packages/data/src/trade/token-delta.js +201 -0
- package/dist/packages/data/src/trade/tx-decode.js +632 -0
- package/dist/packages/data/src/util.js +24 -0
- package/dist/packages/data/src/watchlist.js +176 -0
- package/dist/packages/data/src/x/api.js +256 -0
- package/dist/packages/data/src/x/budget.js +66 -0
- package/dist/packages/data/src/x/config.js +49 -0
- package/dist/packages/data/src/x/index.js +10 -0
- package/dist/packages/data/src/x/mock-server.js +217 -0
- package/dist/packages/data/src/x/oauth.js +242 -0
- package/dist/packages/data/src/x/pkce.js +21 -0
- package/dist/packages/data/src/x/source.js +285 -0
- package/dist/packages/data/src/x/token-store.js +134 -0
- package/dist/packages/data/src/x/transport.js +71 -0
- package/dist/packages/data/vendor/solana-web3/LICENSE +20 -0
- package/dist/packages/data/vendor/solana-web3/NOTICE.md +13 -0
- package/dist/packages/data/vendor/solana-web3/index.iife.min.js +20 -0
- package/dist/packages/mcp/assets/cases.json +1194 -0
- package/dist/packages/mcp/assets/kb/entries/cabal-and-coordinated-launches.md +28 -0
- package/dist/packages/mcp/assets/kb/entries/common-rug-patterns.md +40 -0
- package/dist/packages/mcp/assets/kb/entries/dbc-mechanics.md +30 -0
- package/dist/packages/mcp/assets/kb/entries/dev-wallet-behaviour.md +33 -0
- package/dist/packages/mcp/assets/kb/entries/glossary.md +35 -0
- package/dist/packages/mcp/assets/kb/entries/holder-concentration.md +30 -0
- package/dist/packages/mcp/assets/kb/entries/jito-bundles.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/launchlab-curve.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/lp-burn-vs-lock.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/metadata-and-copycats.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/mint-freeze-authority.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/narrative-lifecycle.md +32 -0
- package/dist/packages/mcp/assets/kb/entries/pump-bonding-curve.md +33 -0
- package/dist/packages/mcp/assets/kb/entries/pumpswap-migration.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/sniping-and-snipers.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/token2022-extensions-risk.md +32 -0
- package/dist/packages/mcp/assets/kb/entries/wash-trading-fake-volume.md +33 -0
- package/dist/packages/mcp/assets/skills/rug-check.md +52 -0
- package/dist/packages/mcp/assets/skills/token-dossier.md +57 -0
- package/dist/packages/mcp/assets/skills/trench-scan.md +40 -0
- package/dist/packages/mcp/bin/dexterm-mcp.mjs +16 -0
- package/dist/packages/mcp/src/dossier.js +78 -0
- package/dist/packages/mcp/src/firewall.js +21 -0
- package/dist/packages/mcp/src/index.js +22 -0
- package/dist/packages/mcp/src/server.js +849 -0
- package/dist/packages/mcp/src/user-content.js +434 -0
- package/dist/packages/packs/src/backtest.js +183 -0
- package/dist/packages/packs/src/digest.js +95 -0
- package/dist/packages/packs/src/firewall.js +57 -0
- package/dist/packages/packs/src/index.js +19 -0
- package/dist/packages/packs/src/larp-eval.js +100 -0
- package/dist/packages/packs/src/narrative.js +309 -0
- package/dist/packages/packs/src/reason.js +135 -0
- package/dist/packages/packs/src/rug-model.js +137 -0
- package/dist/packages/packs/src/schema.js +54 -0
- package/dist/packages/packs/src/token-dossier.js +174 -0
- package/dist/packages/packs/src/verdicts.js +17 -0
- package/dist/packages/packs/src/wallet-dossier.js +105 -0
- package/dist/packages/popup/src/config.js +71 -0
- package/dist/packages/popup/src/dock.js +271 -0
- package/dist/packages/popup/src/host.js +210 -0
- package/dist/packages/popup/src/index.js +12 -0
- package/dist/packages/popup/src/loopback.js +53 -0
- package/dist/packages/popup/src/open.js +183 -0
- package/dist/packages/popup/src/pages/bubbles-lib.js +160 -0
- package/dist/packages/popup/src/pages/bubbles.css +23 -0
- package/dist/packages/popup/src/pages/bubbles.html +36 -0
- package/dist/packages/popup/src/pages/bubbles.js +214 -0
- package/dist/packages/popup/src/pages/chart-lib.js +262 -0
- package/dist/packages/popup/src/pages/chart.css +50 -0
- package/dist/packages/popup/src/pages/chart.html +56 -0
- package/dist/packages/popup/src/pages/chart.js +554 -0
- package/dist/packages/popup/src/pages/common.css +34 -0
- package/dist/packages/popup/src/pages/common.js +187 -0
- package/dist/packages/popup/src/pages/feeds.html +20 -0
- package/dist/packages/popup/src/pages/feeds.js +77 -0
- package/dist/packages/popup/src/pages/narrative.html +20 -0
- package/dist/packages/popup/src/pages/narrative.js +61 -0
- package/dist/packages/popup/src/pages/plans.css +30 -0
- package/dist/packages/popup/src/pages/plans.html +26 -0
- package/dist/packages/popup/src/pages/plans.js +82 -0
- package/dist/packages/popup/src/pages/roster.css +41 -0
- package/dist/packages/popup/src/pages/roster.html +25 -0
- package/dist/packages/popup/src/pages/roster.js +75 -0
- package/dist/packages/popup/src/pages/token.html +20 -0
- package/dist/packages/popup/src/pages/token.js +112 -0
- package/dist/packages/popup/src/pages/widget-sdk.js +224 -0
- package/dist/packages/popup/src/pages/widget.css +5 -0
- package/dist/packages/popup/src/pages/widget.html +21 -0
- package/dist/packages/popup/src/pages/widget.js +85 -0
- package/dist/packages/popup/src/protocol.js +31 -0
- package/dist/packages/popup/src/selftest.js +156 -0
- package/dist/packages/popup/src/server.js +582 -0
- package/dist/packages/popup/src/sinkproxy.js +167 -0
- package/dist/packages/popup/src/widget-html.js +231 -0
- package/dist/packages/popup/vendor/lightweight-charts/LICENSE +201 -0
- package/dist/packages/popup/vendor/lightweight-charts/NOTICE +2 -0
- package/dist/packages/popup/vendor/lightweight-charts/README.md +30 -0
- package/dist/packages/popup/vendor/lightweight-charts/VERSION +1 -0
- package/dist/packages/popup/vendor/lightweight-charts/lightweight-charts.standalone.production.js +7 -0
- package/dist/packages/recorder/bin/dexterm-recorder.mjs +11 -0
- package/dist/packages/recorder/src/cli.js +113 -0
- package/dist/packages/recorder/src/export.js +71 -0
- package/dist/packages/recorder/src/index.js +20 -0
- package/dist/packages/recorder/src/labels.js +151 -0
- package/dist/packages/recorder/src/open.js +29 -0
- package/dist/packages/recorder/src/recorder.js +553 -0
- package/dist/packages/recorder/src/schedule.js +43 -0
- package/dist/packages/recorder/src/schema.js +135 -0
- package/dist/packages/recorder/src/snapshot.js +191 -0
- package/dist/packages/recorder/src/sweep.js +52 -0
- package/dist/packages/recorder/src/verdicts.js +52 -0
- package/dist/packages/tui/src/bubbles.js +415 -0
- package/dist/packages/tui/src/canvas.js +370 -0
- package/dist/packages/tui/src/chrome.js +85 -0
- package/dist/packages/tui/src/color.js +133 -0
- package/dist/packages/tui/src/flush.js +62 -0
- package/dist/packages/tui/src/fmt.js +188 -0
- package/dist/packages/tui/src/fx-trade.js +278 -0
- package/dist/packages/tui/src/fx.js +527 -0
- package/dist/packages/tui/src/images/blocks.js +141 -0
- package/dist/packages/tui/src/images/coin.js +22 -0
- package/dist/packages/tui/src/images/detect.js +140 -0
- package/dist/packages/tui/src/images/index.js +7 -0
- package/dist/packages/tui/src/images/layer.js +146 -0
- package/dist/packages/tui/src/images/pixels.js +157 -0
- package/dist/packages/tui/src/images/protocols.js +87 -0
- package/dist/packages/tui/src/index.js +15 -0
- package/dist/packages/tui/src/keymap.js +355 -0
- package/dist/packages/tui/src/layout.js +11 -0
- package/dist/packages/tui/src/sound.js +177 -0
- package/dist/packages/tui/src/theme.js +182 -0
- package/dist/packages/tui/src/widgets.js +598 -0
- package/dist/packages/voice/src/controller.js +395 -0
- package/dist/packages/voice/src/doctor.js +63 -0
- package/dist/packages/voice/src/engines.js +44 -0
- package/dist/packages/voice/src/index.js +16 -0
- package/dist/packages/voice/src/router.js +502 -0
- package/dist/packages/voice/src/stt/apple-helper.swift.js +103 -0
- package/dist/packages/voice/src/stt/apple.js +179 -0
- package/dist/packages/voice/src/stt/fake.js +34 -0
- package/dist/packages/voice/src/stt/whisper.js +102 -0
- package/dist/packages/voice/src/summary.js +44 -0
- package/dist/packages/voice/src/tts/fake.js +30 -0
- package/dist/packages/voice/src/tts/say.js +96 -0
- package/dist/packages/voice/src/types.js +3 -0
- package/package.json +42 -4
- package/README.md +0 -3
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// task-1360 Tier 1 — PnL, win rate and early-entry score for a followed wallet (research/wallet-intel design
|
|
2
|
+
// §4.3), computed from decoded trades only. Every number is sample-bounded and says so: realised PnL is FIFO by
|
|
3
|
+
// token amount over the trades we have, in SOL (exact) with a USD view at the rate given; win rate is hidden under
|
|
4
|
+
// n < 10 closed positions; the early-entry score needs launch-window buyer ranks (a token's bubble-map scan) and is
|
|
5
|
+
// `—` with the reason otherwise. Airdrops / transfers-in are cost 0 and flagged; transfers-out remove tokens and
|
|
6
|
+
// their cost without touching realised PnL (a wallet move is not a loss). Nothing here is a recommendation.
|
|
7
|
+
export const WIN_RATE_MIN_N = 10;
|
|
8
|
+
function closedNow(p) {
|
|
9
|
+
return p.peakRaw > 0n && p.tokensRaw * 100n <= p.peakRaw;
|
|
10
|
+
}
|
|
11
|
+
/** FIFO positions per mint over trades sorted oldest first. */
|
|
12
|
+
export function positionsOf(trades) {
|
|
13
|
+
const lots = new Map();
|
|
14
|
+
const pos = new Map();
|
|
15
|
+
const sorted = [...trades].sort((a, b) => a.slot - b.slot || a.sig.localeCompare(b.sig));
|
|
16
|
+
for (const tr of sorted) {
|
|
17
|
+
const delta = BigInt(tr.tokenDelta);
|
|
18
|
+
let p = pos.get(tr.mint);
|
|
19
|
+
if (!p) {
|
|
20
|
+
p = { mint: tr.mint, tokensRaw: 0n, peakRaw: 0n, costSol: 0, realisedSol: 0, buys: 0, sells: 0, transfers: 0, flagged: false, closed: false, firstT: tr.t, lastT: tr.t };
|
|
21
|
+
pos.set(tr.mint, p);
|
|
22
|
+
}
|
|
23
|
+
const L = lots.get(tr.mint) ?? [];
|
|
24
|
+
lots.set(tr.mint, L);
|
|
25
|
+
p.lastT = tr.t;
|
|
26
|
+
if (delta > 0n) {
|
|
27
|
+
// a buy with a SOL leg carries cost; a token-swap leg or an airdrop is cost 0 and flagged
|
|
28
|
+
const cost = tr.side === 'buy' && tr.solDelta !== null ? Math.abs(tr.solDelta) : 0;
|
|
29
|
+
const flagged = cost === 0;
|
|
30
|
+
L.push({ tokensRaw: delta, costSol: cost, flagged });
|
|
31
|
+
p.tokensRaw += delta;
|
|
32
|
+
p.costSol += cost;
|
|
33
|
+
if (flagged)
|
|
34
|
+
p.flagged = true;
|
|
35
|
+
if (tr.side === 'buy')
|
|
36
|
+
p.buys += 1;
|
|
37
|
+
else
|
|
38
|
+
p.transfers += 1;
|
|
39
|
+
if (p.tokensRaw > p.peakRaw)
|
|
40
|
+
p.peakRaw = p.tokensRaw;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
let need = -delta;
|
|
44
|
+
let costOut = 0;
|
|
45
|
+
while (need > 0n && L.length) {
|
|
46
|
+
const lot = L[0];
|
|
47
|
+
if (lot.tokensRaw <= need) {
|
|
48
|
+
need -= lot.tokensRaw;
|
|
49
|
+
costOut += lot.costSol;
|
|
50
|
+
L.shift();
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const frac = Number(need) / Number(lot.tokensRaw);
|
|
54
|
+
const c = lot.costSol * frac;
|
|
55
|
+
costOut += c;
|
|
56
|
+
lot.costSol -= c;
|
|
57
|
+
lot.tokensRaw -= need;
|
|
58
|
+
need = 0n;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const removed = -delta - need; // tokens we actually had lots for (history may start mid-position)
|
|
62
|
+
p.tokensRaw -= removed;
|
|
63
|
+
if (p.tokensRaw < 0n)
|
|
64
|
+
p.tokensRaw = 0n;
|
|
65
|
+
p.costSol = Math.max(0, p.costSol - costOut);
|
|
66
|
+
if (tr.side === 'sell' && tr.solDelta !== null) {
|
|
67
|
+
p.realisedSol += tr.solDelta - costOut;
|
|
68
|
+
p.sells += 1;
|
|
69
|
+
}
|
|
70
|
+
else if (tr.side === 'sell') {
|
|
71
|
+
p.sells += 1;
|
|
72
|
+
p.flagged = true;
|
|
73
|
+
} // token-swap leg: proceeds unknown, cost released
|
|
74
|
+
else
|
|
75
|
+
p.transfers += 1; // transfer-out: cost released, no PnL
|
|
76
|
+
}
|
|
77
|
+
p.closed = closedNow(p);
|
|
78
|
+
}
|
|
79
|
+
return [...pos.values()].sort((a, b) => b.lastT - a.lastT);
|
|
80
|
+
}
|
|
81
|
+
/** Early-entry score = median over tokens of (1 − rank / buyers); null without ranks (design §4.3). */
|
|
82
|
+
export function earlyEntryScore(inputs) {
|
|
83
|
+
const xs = inputs.filter((e) => e.buyers > 0 && e.rank >= 0 && e.rank < e.buyers).map((e) => 1 - e.rank / e.buyers).sort((a, b) => a - b);
|
|
84
|
+
if (!xs.length)
|
|
85
|
+
return { score: null, n: 0, note: 'needs launch-window buyer ranks (scan the token: M) · no 3× filter yet' };
|
|
86
|
+
const mid = Math.floor(xs.length / 2);
|
|
87
|
+
const score = xs.length % 2 ? xs[mid] : (xs[mid - 1] + xs[mid]) / 2;
|
|
88
|
+
return { score: Math.round(score * 1000) / 1000, n: xs.length, note: `median over ${xs.length} scanned launch window${xs.length === 1 ? '' : 's'} · no 3× filter yet` };
|
|
89
|
+
}
|
|
90
|
+
export function walletStats(wallet, trades, opts = {}) {
|
|
91
|
+
const positions = positionsOf(trades);
|
|
92
|
+
const closed = positions.filter((p) => p.closed);
|
|
93
|
+
const wins = closed.filter((p) => p.realisedSol > 0).length;
|
|
94
|
+
const realisedSol = positions.reduce((s, p) => s + p.realisedSol, 0);
|
|
95
|
+
const solUsd = typeof opts.solUsd === 'number' && opts.solUsd > 0 ? opts.solUsd : null;
|
|
96
|
+
const ts = trades.map((t) => t.t).filter((t) => t > 0);
|
|
97
|
+
const winRate = closed.length >= WIN_RATE_MIN_N ? wins / closed.length : null;
|
|
98
|
+
const txs = opts.txs ?? new Set(trades.map((t) => t.sig)).size;
|
|
99
|
+
return {
|
|
100
|
+
wallet,
|
|
101
|
+
trades: trades.length,
|
|
102
|
+
buys: trades.filter((t) => t.side === 'buy').length,
|
|
103
|
+
sells: trades.filter((t) => t.side === 'sell').length,
|
|
104
|
+
transfers: trades.filter((t) => t.side === 'transfer').length,
|
|
105
|
+
mints: positions.length,
|
|
106
|
+
realisedSol: Math.round(realisedSol * 1e6) / 1e6,
|
|
107
|
+
realisedUsd: solUsd === null ? null : Math.round(realisedSol * solUsd * 100) / 100,
|
|
108
|
+
solUsdAt: solUsd,
|
|
109
|
+
closed: closed.length,
|
|
110
|
+
wins,
|
|
111
|
+
winRate: winRate === null ? null : Math.round(winRate * 1000) / 1000,
|
|
112
|
+
winRateNote: closed.length >= WIN_RATE_MIN_N ? `${wins}/${closed.length} closed` : `hidden under n=${WIN_RATE_MIN_N} (${wins}/${closed.length} closed so far)`,
|
|
113
|
+
open: positions.length - closed.length,
|
|
114
|
+
earlyEntry: earlyEntryScore(opts.earlyEntry ?? []),
|
|
115
|
+
positions,
|
|
116
|
+
firstT: ts.length ? Math.min(...ts) : null,
|
|
117
|
+
lastT: ts.length ? Math.max(...ts) : null,
|
|
118
|
+
sample: { txs, complete: !!opts.complete, note: opts.complete ? `all ${txs} txs` : `last ${txs} txs only · older history not fetched` },
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// task-1360 Tier 1 — the WALLET TRACKER: polls each followed wallet's recent history (Helius
|
|
2
|
+
// `getTransactionsForAddress`, newest first, 10 credits a call; the limiter and credit meter own the budget),
|
|
3
|
+
// decodes new transactions into trades, keeps per-wallet trades + stats, and raises an ALERT for every new buy or
|
|
4
|
+
// sell. The first page of a wallet is a BACKFILL (history is not news: no alerts); only transactions that appear
|
|
5
|
+
// after that are alerted. Keyless the tracker is honestly unavailable with the free-key fix; a failed poll is a
|
|
6
|
+
// visible degraded state, never silence. Read-only: it never trades, never signs, never proposes by itself —
|
|
7
|
+
// stages 2 and 3 consume its alerts through their own gates.
|
|
8
|
+
import { systemClock } from "../../clock.js";
|
|
9
|
+
import { KEY_FIX } from "../../intel/scan.js";
|
|
10
|
+
import { decodeWalletTrades } from "./trades.js";
|
|
11
|
+
import { walletStats } from "./stats.js";
|
|
12
|
+
export const TRACKER_DEFAULTS = { pollMs: 15_000, pageLimit: 25, backfillLimit: 100, keep: 500 };
|
|
13
|
+
export class WalletTracker {
|
|
14
|
+
follows;
|
|
15
|
+
history;
|
|
16
|
+
clock;
|
|
17
|
+
solUsd;
|
|
18
|
+
pollMs;
|
|
19
|
+
pageLimit;
|
|
20
|
+
backfillLimit;
|
|
21
|
+
keep;
|
|
22
|
+
onAlert;
|
|
23
|
+
onChange;
|
|
24
|
+
available;
|
|
25
|
+
earlyEntry;
|
|
26
|
+
wallets = new Map();
|
|
27
|
+
alertLog = [];
|
|
28
|
+
polls = 0;
|
|
29
|
+
lastPollAt = null;
|
|
30
|
+
lastError = null;
|
|
31
|
+
running = null;
|
|
32
|
+
inflight = null;
|
|
33
|
+
n = 0;
|
|
34
|
+
constructor(opts) {
|
|
35
|
+
this.follows = opts.follows;
|
|
36
|
+
this.history = opts.history;
|
|
37
|
+
this.clock = opts.clock ?? systemClock;
|
|
38
|
+
this.solUsd = opts.solUsd ?? (() => null);
|
|
39
|
+
this.pollMs = opts.pollMs ?? TRACKER_DEFAULTS.pollMs;
|
|
40
|
+
this.pageLimit = opts.pageLimit ?? TRACKER_DEFAULTS.pageLimit;
|
|
41
|
+
this.backfillLimit = opts.backfillLimit ?? TRACKER_DEFAULTS.backfillLimit;
|
|
42
|
+
this.keep = opts.keep ?? TRACKER_DEFAULTS.keep;
|
|
43
|
+
this.onAlert = opts.onAlert ?? (() => { });
|
|
44
|
+
this.onChange = opts.onChange ?? (() => { });
|
|
45
|
+
this.available = opts.available ?? (() => ({ ok: true }));
|
|
46
|
+
this.earlyEntry = opts.earlyEntry ?? (() => undefined);
|
|
47
|
+
}
|
|
48
|
+
status() {
|
|
49
|
+
const wallets = this.follows.list().length;
|
|
50
|
+
const base = { polls: this.polls, lastPollAt: this.lastPollAt, wallets, alerts: this.alertLog.length };
|
|
51
|
+
if (!this.history)
|
|
52
|
+
return { state: 'unavailable', why: `following wallets needs a ${KEY_FIX}`, ...base };
|
|
53
|
+
const av = this.available();
|
|
54
|
+
if (!av.ok)
|
|
55
|
+
return { state: 'paused', why: av.why, ...base };
|
|
56
|
+
if (this.lastError)
|
|
57
|
+
return { state: 'degraded', why: this.lastError, ...base };
|
|
58
|
+
if (this.polls === 0)
|
|
59
|
+
return { state: 'idle', why: wallets ? 'first poll pending' : 'no wallets followed', ...base };
|
|
60
|
+
return { state: 'ok', why: null, ...base };
|
|
61
|
+
}
|
|
62
|
+
trades(addr) { return [...(this.wallets.get(addr)?.trades ?? [])].reverse(); }
|
|
63
|
+
alerts(limit = 50) { return this.alertLog.slice(-limit).reverse(); }
|
|
64
|
+
stats(addr) {
|
|
65
|
+
const w = this.wallets.get(addr);
|
|
66
|
+
return walletStats(addr, w?.trades ?? [], { solUsd: this.solUsd(), txs: w?.txs ?? 0, complete: w?.complete ?? false, earlyEntry: this.earlyEntry(addr) });
|
|
67
|
+
}
|
|
68
|
+
/** Per-wallet poll error (shown on the row), null when the last poll was clean. */
|
|
69
|
+
walletError(addr) { return this.wallets.get(addr)?.error ?? null; }
|
|
70
|
+
/** One pass over the follow list. Serialised: a second call while one runs returns the same promise. */
|
|
71
|
+
pollOnce() {
|
|
72
|
+
if (this.inflight)
|
|
73
|
+
return this.inflight;
|
|
74
|
+
this.inflight = this.run().finally(() => { this.inflight = null; });
|
|
75
|
+
return this.inflight;
|
|
76
|
+
}
|
|
77
|
+
async run() {
|
|
78
|
+
const out = [];
|
|
79
|
+
if (!this.history)
|
|
80
|
+
return out;
|
|
81
|
+
const av = this.available();
|
|
82
|
+
if (!av.ok)
|
|
83
|
+
return out;
|
|
84
|
+
const live = new Set(this.follows.list().map((f) => f.addr));
|
|
85
|
+
for (const addr of [...this.wallets.keys()])
|
|
86
|
+
if (!live.has(addr))
|
|
87
|
+
this.wallets.delete(addr);
|
|
88
|
+
let anyError = null;
|
|
89
|
+
for (const f of this.follows.list()) {
|
|
90
|
+
let w = this.wallets.get(f.addr);
|
|
91
|
+
if (!w) {
|
|
92
|
+
w = { primed: false, seen: new Set(), trades: [], txs: 0, complete: false, lastSlot: 0, error: null };
|
|
93
|
+
this.wallets.set(f.addr, w);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const page = await this.history.transactions(f.addr, { limit: w.primed ? this.pageLimit : this.backfillLimit, sortOrder: 'desc' });
|
|
97
|
+
const fresh = page.data.filter((tx) => { const s = tx.transaction?.signatures?.[0]; return !!s && !w.seen.has(s); });
|
|
98
|
+
for (const tx of fresh)
|
|
99
|
+
w.seen.add(tx.transaction.signatures[0]);
|
|
100
|
+
w.txs += fresh.length;
|
|
101
|
+
const trades = decodeWalletTrades(fresh, f.addr, { solUsd: this.solUsd(), fetchedAt: page.fetchedAt });
|
|
102
|
+
if (!w.primed) {
|
|
103
|
+
w.primed = true;
|
|
104
|
+
w.complete = page.paginationToken === null;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const now = this.clock.now();
|
|
108
|
+
for (const tr of trades) {
|
|
109
|
+
if (tr.side === 'transfer')
|
|
110
|
+
continue;
|
|
111
|
+
const a = { id: `copy_${++this.n}`, t: now, wallet: f.addr, name: f.name, trade: tr, lagMs: tr.tKnown ? Math.max(0, now - tr.t) : null };
|
|
112
|
+
this.alertLog.push(a);
|
|
113
|
+
out.push(a);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (trades.length) {
|
|
117
|
+
w.trades.push(...trades);
|
|
118
|
+
w.trades.sort((a, b) => a.slot - b.slot || a.sig.localeCompare(b.sig));
|
|
119
|
+
if (w.trades.length > this.keep)
|
|
120
|
+
w.trades.splice(0, w.trades.length - this.keep);
|
|
121
|
+
w.lastSlot = w.trades[w.trades.length - 1].slot;
|
|
122
|
+
}
|
|
123
|
+
w.error = null;
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
w.error = e instanceof Error ? e.message : String(e);
|
|
127
|
+
anyError = `${f.addr.slice(0, 4)}…: ${w.error}`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (this.alertLog.length > 500)
|
|
131
|
+
this.alertLog.splice(0, this.alertLog.length - 500);
|
|
132
|
+
this.polls += 1;
|
|
133
|
+
this.lastPollAt = this.clock.now();
|
|
134
|
+
this.lastError = anyError;
|
|
135
|
+
for (const a of out)
|
|
136
|
+
this.onAlert(a);
|
|
137
|
+
this.onChange();
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
/** Poll on an interval until stop(). Never throws out of the loop; errors show in status(). */
|
|
141
|
+
start() {
|
|
142
|
+
if (this.running || !this.history)
|
|
143
|
+
return;
|
|
144
|
+
const ctl = new AbortController();
|
|
145
|
+
this.running = ctl;
|
|
146
|
+
void (async () => {
|
|
147
|
+
while (!ctl.signal.aborted) {
|
|
148
|
+
try {
|
|
149
|
+
await this.pollOnce();
|
|
150
|
+
}
|
|
151
|
+
catch { /* reported via status() */ }
|
|
152
|
+
await this.clock.sleep(this.pollMs, ctl.signal);
|
|
153
|
+
}
|
|
154
|
+
})();
|
|
155
|
+
}
|
|
156
|
+
stop() { this.running?.abort(); this.running = null; }
|
|
157
|
+
get isRunning() { return this.running !== null; }
|
|
158
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// task-1360 Tier 1 — decode a followed wallet's swaps from its own transaction history (RawTx = the slim
|
|
2
|
+
// jsonParsed shape the wallet-intel rules already use; raw transactions are never stored).
|
|
3
|
+
//
|
|
4
|
+
// Per transaction the wallet's net token delta per mint (pre/post token balances for accounts it owns) and its
|
|
5
|
+
// net lamports delta (pre/post balances; fees and tips included) are read. One mint moved: tokens up and SOL
|
|
6
|
+
// down = BUY, tokens down and SOL up = SELL, tokens moved with no SOL leg = TRANSFER (airdrop / wallet move,
|
|
7
|
+
// flagged). Two or more mints moved (token→token) = both legs listed as a 'token-swap' with no SOL split, so
|
|
8
|
+
// nothing is priced that cannot be priced. Wrapped SOL is the swap's own plumbing and is never a "mint" here.
|
|
9
|
+
import { accountKeys } from "../../intel/rules.js";
|
|
10
|
+
import { WSOL_MINT } from "../tx-decode.js";
|
|
11
|
+
/** A SOL move below this is a fee/tip only, not a trade leg (0.0002 SOL). */
|
|
12
|
+
export const SOL_LEG_MIN_LAMPORTS = 200_000;
|
|
13
|
+
/** The wallet's net token delta per mint in one tx (raw base units), WSOL excluded. */
|
|
14
|
+
export function tokenDeltasForOwner(tx, owner) {
|
|
15
|
+
const pre = new Map();
|
|
16
|
+
const post = new Map();
|
|
17
|
+
for (const b of tx.meta?.preTokenBalances ?? [])
|
|
18
|
+
if (b.owner === owner && b.mint !== WSOL_MINT)
|
|
19
|
+
pre.set(b.accountIndex, { mint: b.mint, amt: BigInt(b.uiTokenAmount.amount) });
|
|
20
|
+
for (const b of tx.meta?.postTokenBalances ?? [])
|
|
21
|
+
if (b.owner === owner && b.mint !== WSOL_MINT)
|
|
22
|
+
post.set(b.accountIndex, { mint: b.mint, amt: BigInt(b.uiTokenAmount.amount) });
|
|
23
|
+
const out = new Map();
|
|
24
|
+
for (const i of new Set([...pre.keys(), ...post.keys()])) {
|
|
25
|
+
const mint = post.get(i)?.mint ?? pre.get(i).mint;
|
|
26
|
+
const d = (post.get(i)?.amt ?? 0n) - (pre.get(i)?.amt ?? 0n);
|
|
27
|
+
if (d !== 0n)
|
|
28
|
+
out.set(mint, (out.get(mint) ?? 0n) + d);
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
/** The wallet's net lamports delta in one tx (fee included); 0 when the wallet is not an account key. */
|
|
33
|
+
export function lamportsDeltaFor(tx, owner) {
|
|
34
|
+
const keys = accountKeys(tx);
|
|
35
|
+
const i = keys.findIndex((k) => k.pubkey === owner);
|
|
36
|
+
if (i < 0)
|
|
37
|
+
return 0;
|
|
38
|
+
const pre = tx.meta?.preBalances ?? [], post = tx.meta?.postBalances ?? [];
|
|
39
|
+
return (post[i] ?? 0) - (pre[i] ?? 0);
|
|
40
|
+
}
|
|
41
|
+
/** Decode one transaction into the wallet's trades (0, 1 or several legs). Pure; never throws on odd input. */
|
|
42
|
+
export function decodeTx(tx, wallet, opts = {}) {
|
|
43
|
+
const sig = tx.transaction?.signatures?.[0] ?? '';
|
|
44
|
+
const deltas = tokenDeltasForOwner(tx, wallet);
|
|
45
|
+
if (deltas.size === 0)
|
|
46
|
+
return [];
|
|
47
|
+
const lamports = lamportsDeltaFor(tx, wallet);
|
|
48
|
+
const tKnown = typeof tx.blockTime === 'number' && tx.blockTime > 0;
|
|
49
|
+
const t = tKnown ? tx.blockTime * 1000 : (opts.fetchedAt ?? 0);
|
|
50
|
+
const solUsd = typeof opts.solUsd === 'number' && opts.solUsd > 0 ? opts.solUsd : null;
|
|
51
|
+
const out = [];
|
|
52
|
+
const multi = deltas.size > 1;
|
|
53
|
+
for (const [mint, delta] of deltas) {
|
|
54
|
+
const solLeg = !multi && Math.abs(lamports) >= SOL_LEG_MIN_LAMPORTS;
|
|
55
|
+
let side;
|
|
56
|
+
let note = null;
|
|
57
|
+
if (multi) {
|
|
58
|
+
side = delta > 0n ? 'buy' : 'sell';
|
|
59
|
+
note = 'token-swap';
|
|
60
|
+
}
|
|
61
|
+
else if (delta > 0n && solLeg && lamports < 0)
|
|
62
|
+
side = 'buy';
|
|
63
|
+
else if (delta < 0n && solLeg && lamports > 0)
|
|
64
|
+
side = 'sell';
|
|
65
|
+
else if (solLeg) {
|
|
66
|
+
side = 'transfer';
|
|
67
|
+
note = 'lp-like';
|
|
68
|
+
} // tokens AND SOL moved the same way: LP add/remove or a payout, not a swap
|
|
69
|
+
else {
|
|
70
|
+
side = 'transfer';
|
|
71
|
+
if (delta > 0n)
|
|
72
|
+
note = 'airdrop';
|
|
73
|
+
}
|
|
74
|
+
const solDelta = !multi && side !== 'transfer' ? lamports / 1e9 : null;
|
|
75
|
+
const usd = solDelta !== null && solUsd !== null ? Math.abs(solDelta) * solUsd : null;
|
|
76
|
+
out.push({ wallet, sig, slot: tx.slot, t, tKnown, mint, side, tokenDelta: delta.toString(), lamportsDelta: lamports, solDelta, usd, solUsdAt: solDelta !== null ? solUsd : null, note });
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
/** Decode a history page (any order) into trades sorted oldest first. */
|
|
81
|
+
export function decodeWalletTrades(txs, wallet, opts = {}) {
|
|
82
|
+
const out = [];
|
|
83
|
+
for (const tx of txs)
|
|
84
|
+
out.push(...decodeTx(tx, wallet, opts));
|
|
85
|
+
out.sort((a, b) => a.slot - b.slot || a.sig.localeCompare(b.sig));
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// task-1360 — COPY TRADING (staged) shared types. Personal tool for the user's OWN wallet only: no custody, no
|
|
2
|
+
// third parties, no fees, no hosted/pooled copy, no curated leader lists (product decision 2026-10-06; ROADMAP legal
|
|
3
|
+
// flags; research/wallet-intel design §6). Stage 1 = follow a wallet + alerts, stage 2 = one-key mirror PROPOSAL,
|
|
4
|
+
// stage 3 = capped auto-mirror inside an armed copy mandate. Nothing in this module signs, holds a key, or reaches
|
|
5
|
+
// mainnet: it decodes public history, keeps local notes, and hands PROPOSALS to the plan engine.
|
|
6
|
+
export {};
|