chain-insights 0.68.2 → 0.69.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/README.md +1 -0
- package/dist/cli.cjs +30 -4
- package/dist/cli.mjs +30 -4
- package/dist/cli.mjs.map +1 -1
- package/dist/dex-registry-05wAhSa0.mjs +383 -0
- package/dist/dex-registry-05wAhSa0.mjs.map +1 -0
- package/dist/dex-registry-KOTJhAQf.cjs +411 -0
- package/dist/dex-tool-DIKN4dXB.cjs +536 -0
- package/dist/dex-tool-DpQKS7Te.mjs +497 -0
- package/dist/dex-tool-DpQKS7Te.mjs.map +1 -0
- package/dist/{format-DB2IH2U5.mjs → format-CSesEBvS.mjs} +4 -4
- package/dist/format-CSesEBvS.mjs.map +1 -0
- package/dist/{format-BjZuYDr8.cjs → format-DfhRQq4L.cjs} +3 -3
- package/dist/mcp-proxy.cjs +53 -59
- package/dist/mcp-proxy.d.cts +2 -0
- package/dist/mcp-proxy.d.cts.map +1 -1
- package/dist/mcp-proxy.d.mts +2 -0
- package/dist/mcp-proxy.d.mts.map +1 -1
- package/dist/mcp-proxy.mjs +51 -59
- package/dist/mcp-proxy.mjs.map +1 -1
- package/dist/{tool-visibility-B4RAFMaB.cjs → tool-visibility-BopFnGsT.cjs} +5 -0
- package/dist/{tool-visibility-kclgGKL5.mjs → tool-visibility-Dq-Ruj-8.mjs} +6 -1
- package/dist/tool-visibility-Dq-Ruj-8.mjs.map +1 -0
- package/docs/mcp-proxy.md +71 -0
- package/docs/stability.md +3 -2
- package/package.json +3 -2
- package/registry/README.md +133 -0
- package/registry/robinhood.json +769 -0
- package/registry/robinhood.md +102 -0
- package/skills/chain-insights-dex/SKILL.md +104 -96
- package/skills/chain-insights-dex/agents/openai.yaml +1 -1
- package/skills/chain-insights-dex/reference.md +8 -27
- package/skills/chain-insights-dex-gauge/SKILL.md +6 -4
- package/skills/chain-insights-dex-launchpad/SKILL.md +6 -3
- package/skills/chain-insights-dex-vault/SKILL.md +4 -2
- package/skills/chain-insights-virtuals/SKILL.md +8 -4
- package/dist/format-DB2IH2U5.mjs.map +0 -1
- package/dist/tool-visibility-kclgGKL5.mjs.map +0 -1
|
@@ -47,6 +47,11 @@ const PUBLIC_MCP_TOOL_ALLOWED_ARGS = {
|
|
|
47
47
|
"queries",
|
|
48
48
|
"per_query_timeout_seconds",
|
|
49
49
|
"time_scope"
|
|
50
|
+
],
|
|
51
|
+
meta_dex_lookup: [
|
|
52
|
+
"network",
|
|
53
|
+
"name",
|
|
54
|
+
"pools"
|
|
50
55
|
]
|
|
51
56
|
};
|
|
52
57
|
function isHiddenRemoteToolName(name) {
|
|
@@ -86,4 +91,4 @@ function validatePublicMcpToolArguments(name, args) {
|
|
|
86
91
|
//#endregion
|
|
87
92
|
export { tool_visibility_exports as a, isAppOnlyTool as i, PUBLIC_MCP_TOOL_ALLOWED_ARGS as n, PUBLIC_MCP_TOOL_REQUIRED_ARGS as r, HIDDEN_REMOTE_TOOL_NAMES as t };
|
|
88
93
|
|
|
89
|
-
//# sourceMappingURL=tool-visibility-
|
|
94
|
+
//# sourceMappingURL=tool-visibility-Dq-Ruj-8.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-visibility-Dq-Ruj-8.mjs","names":[],"sources":["../src/mcp/tool-visibility.ts"],"sourcesContent":["import { McpToolError, formatMcpToolError } from './print-result.js'\n\nexport const HIDDEN_REMOTE_TOOL_NAMES = new Set([\n 'topup',\n 'address_risk',\n // Hidden until its verdict is fixed: never listed, never registered, never\n // called through the proxy. The remote graph endpoint does not serve it.\n 'aml_address_risk',\n 'trace_victim_funds',\n 'trace_suspect_funds',\n 'trace_deposit_sources',\n 'trace_funds',\n 'track_funds',\n // Retired tools: never exposed publicly. Built by concatenation so the\n // retired names do not appear as literals in this file.\n ['aml_trace_victim', '_funds'].join(''),\n ['aml_trace_suspect', '_funds'].join(''),\n ['aml_trace_deposit', '_sources'].join(''),\n 'network_capabilities',\n 'usage_status',\n 'subscription_status',\n 'balance',\n 'help',\n ['money', '_flows_between_exchanges'].join(''),\n 'address_connection_risk',\n])\n\nexport const PUBLIC_MCP_TOOL_REQUIRED_ARGS: Record<string, string[]> = {\n graph_query: ['query', 'network'],\n graph_query_batch: ['network', 'queries'],\n}\n\n// normalizeRemoteToolArguments FILTERS pass-through arguments to this list, so\n// an argument that is missing here is silently STRIPPED and the caller gets a\n// default-bounded result with no error and no warning. `time_scope` shipped\n// broken for exactly this reason. Every tunable search bound below\n// (per_address_limit, row_limit) must therefore be listed the moment it\n// appears on a tool schema, and is covered end-to-end through the proxy in\n// tests/mcp-proxy.test.ts.\nexport const PUBLIC_MCP_TOOL_ALLOWED_ARGS: Record<string, string[]> = {\n // `time_scope` narrows a `USE topology` query to a temporal-shard subset\n graph_query: ['query', 'network', 'time_scope'],\n graph_query_batch: ['network', 'queries', 'per_query_timeout_seconds', 'time_scope'],\n // A local tool of the proxy (src/mcp/dex-tool.ts): the exchange by name.\n meta_dex_lookup: ['network', 'name', 'pools'],\n}\n\nexport function isHiddenRemoteToolName(name: string): boolean {\n return HIDDEN_REMOTE_TOOL_NAMES.has(name)\n}\n\n/**\n * True for an MCP Apps tool only a view may call: `_meta.ui.visibility` is set\n * and does not include \"model\" (for example graph_expand, visibility [\"app\"]).\n * Such a tool never appears in a tool list built for the model or the CLI.\n */\nexport function isAppOnlyTool(tool: { _meta?: Record<string, unknown> }): boolean {\n const ui = tool._meta?.ui\n if (!ui || typeof ui !== 'object' || Array.isArray(ui)) return false\n const visibility = (ui as Record<string, unknown>).visibility\n return Array.isArray(visibility) && !visibility.includes('model')\n}\n\nexport function visibleRemoteTools<T extends { name: string; _meta?: Record<string, unknown> }>(\n tools: T[]\n): T[] {\n return tools.filter((tool) => !isHiddenRemoteToolName(tool.name) && !isAppOnlyTool(tool))\n}\n\nexport function assertPublicMcpToolName(name: string): void {\n if (!isHiddenRemoteToolName(name)) return\n const replacement =\n name === 'network_capabilities'\n ? ' Use meta_network_capabilities instead.'\n : name === 'usage_status'\n ? ' Use meta_usage_status instead.'\n : name === 'subscription_status'\n ? ' Use meta_subscription_status instead.'\n : name === 'balance'\n ? ' Use wallet_balance instead.'\n : name === 'help'\n ? ' Use meta_help instead.'\n : ''\n throw new Error(`MCP tool '${name}' is not exposed by Chain Insights.${replacement}`)\n}\n\nexport function formatMcpCallError(tool: string, err: unknown): string {\n // A tool error with an envelope is the server's own answer: its text, then the\n // line of its error_detail. It is never matched against the unknown-tool\n // pattern below. A tool error with no envelope keeps the old reading, because\n // a server can answer an unknown tool as an error reply.\n if (err instanceof McpToolError && err.errorDetail) return formatMcpToolError(err)\n const message = err instanceof Error ? err.message : String(err)\n if (/\\bunknown tool\\b|\\btool\\b.*\\bnot found\\b/i.test(message)) {\n return `Unknown MCP tool \"${tool}\". Run \\`cia mcp tools --refresh\\` to list available tools.`\n }\n return message\n}\n\nexport function validatePublicMcpToolArguments(name: string, args: Record<string, unknown>): void {\n const allowedArgs = PUBLIC_MCP_TOOL_ALLOWED_ARGS[name]\n if (!allowedArgs) return\n\n const unsupportedArgs = Object.keys(args).filter((argName) => !allowedArgs.includes(argName))\n if (unsupportedArgs.length === 0) return\n\n throw new Error(\n [\n `Unsupported argument${unsupportedArgs.length === 1 ? '' : 's'} for ${name}: ${unsupportedArgs.join(', ')}.`,\n `Allowed arguments: ${allowedArgs.join(', ')}.`,\n ].join(' ')\n )\n}\n"],"mappings":";;;;;;;;;;;;;;AAEA,MAAa,2CAA2B,IAAI,IAAI;CAC9C;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA,CAAC,oBAAoB,QAAQ,CAAC,CAAC,KAAK,EAAE;CACtC,CAAC,qBAAqB,QAAQ,CAAC,CAAC,KAAK,EAAE;CACvC,CAAC,qBAAqB,UAAU,CAAC,CAAC,KAAK,EAAE;CACzC;CACA;CACA;CACA;CACA;CACA,CAAC,SAAS,0BAA0B,CAAC,CAAC,KAAK,EAAE;CAC7C;AACF,CAAC;AAED,MAAa,gCAA0D;CACrE,aAAa,CAAC,SAAS,SAAS;CAChC,mBAAmB,CAAC,WAAW,SAAS;AAC1C;AASA,MAAa,+BAAyD;CAEpE,aAAa;EAAC;EAAS;EAAW;CAAY;CAC9C,mBAAmB;EAAC;EAAW;EAAW;EAA6B;CAAY;CAEnF,iBAAiB;EAAC;EAAW;EAAQ;CAAO;AAC9C;AAEA,SAAgB,uBAAuB,MAAuB;CAC5D,OAAO,yBAAyB,IAAI,IAAI;AAC1C;;;;;;AAOA,SAAgB,cAAc,MAAoD;CAChF,MAAM,KAAK,KAAK,OAAO;CACvB,IAAI,CAAC,MAAM,OAAO,OAAO,YAAY,MAAM,QAAQ,EAAE,GAAG,OAAO;CAC/D,MAAM,aAAc,GAA+B;CACnD,OAAO,MAAM,QAAQ,UAAU,KAAK,CAAC,WAAW,SAAS,OAAO;AAClE;AAEA,SAAgB,mBACd,OACK;CACL,OAAO,MAAM,QAAQ,SAAS,CAAC,uBAAuB,KAAK,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC;AAC1F;AAEA,SAAgB,wBAAwB,MAAoB;CAC1D,IAAI,CAAC,uBAAuB,IAAI,GAAG;CAanC,MAAM,IAAI,MAAM,aAAa,KAAK,qCAXhC,SAAS,yBACL,4CACA,SAAS,iBACP,oCACA,SAAS,wBACP,2CACA,SAAS,YACP,iCACA,SAAS,SACP,4BACA,IACsE;AACtF;AAEA,SAAgB,mBAAmB,MAAc,KAAsB;CAKrE,IAAI,eAAe,gBAAgB,IAAI,aAAa,OAAO,mBAAmB,GAAG;CACjF,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;CAC/D,IAAI,4CAA4C,KAAK,OAAO,GAC1D,OAAO,qBAAqB,KAAK;CAEnC,OAAO;AACT;AAEA,SAAgB,+BAA+B,MAAc,MAAqC;CAChG,MAAM,cAAc,6BAA6B;CACjD,IAAI,CAAC,aAAa;CAElB,MAAM,kBAAkB,OAAO,KAAK,IAAI,CAAC,CAAC,QAAQ,YAAY,CAAC,YAAY,SAAS,OAAO,CAAC;CAC5F,IAAI,gBAAgB,WAAW,GAAG;CAElC,MAAM,IAAI,MACR,CACE,uBAAuB,gBAAgB,WAAW,IAAI,KAAK,IAAI,OAAO,KAAK,IAAI,gBAAgB,KAAK,IAAI,EAAE,IAC1G,sBAAsB,YAAY,KAAK,IAAI,EAAE,EAC/C,CAAC,CAAC,KAAK,GAAG,CACZ;AACF"}
|
package/docs/mcp-proxy.md
CHANGED
|
@@ -81,6 +81,7 @@ The proxy:
|
|
|
81
81
|
| --------------------------- | ------------------------------------------------------------------------------------ |
|
|
82
82
|
| `meta_network_capabilities` | Show the current Chain Insights network/tool support matrix |
|
|
83
83
|
| `meta_schema` | Show the live graph schema of a network: labels, link types, fields, indexes |
|
|
84
|
+
| `meta_dex_lookup` | Turn an exchange name into its factories, routers or manager, pools and first reads |
|
|
84
85
|
| `meta_rules` | Read the answer rules when the `chain-insights-cypher` skill is not loaded |
|
|
85
86
|
| `meta_help` | Show Chain Insights tool and workflow guidance |
|
|
86
87
|
| `meta_usage_status` | Check the caller's daily free-tier graph query allowance |
|
|
@@ -101,6 +102,76 @@ note, and such a schema is kept for 10 minutes, not 24 hours. In the terminal,
|
|
|
101
102
|
structured form and `--refresh` to rebuild it.
|
|
102
103
|
`cia mcp call meta_schema network=robinhood` does the same.
|
|
103
104
|
|
|
105
|
+
`meta_dex_lookup` turns the name of a decentralized exchange, a launchpad or a
|
|
106
|
+
vault into its addresses. It takes `network` (required), `name` (optional:
|
|
107
|
+
with no name it lists every covered exchange) and `pools` (default `true`).
|
|
108
|
+
|
|
109
|
+
1. The name is matched against the exchange registry,
|
|
110
|
+
[`registry/robinhood.json`](../registry/robinhood.json): exact on the key,
|
|
111
|
+
the display name and the aliases, then within two letters of an alias of
|
|
112
|
+
four or more characters. One winner is `matched`; a misspelling is named in
|
|
113
|
+
`took_for`. A tie is listed in `candidates`, never guessed. No hit returns
|
|
114
|
+
`covered`, every covered exchange, and sends no graph read.
|
|
115
|
+
2. For a match, one `graph_query_batch` reads `Head` for the three family
|
|
116
|
+
lags, each factory, manager and router by its address (`confirmed` is true
|
|
117
|
+
when the graph shows a contract), and the ten biggest pools each factory
|
|
118
|
+
deployed. It is billed as any read; the match itself costs nothing.
|
|
119
|
+
`pools: false` drops the pool reads.
|
|
120
|
+
3. The answer carries `recipe`, the first reads of the exchange's trail type
|
|
121
|
+
with its addresses filled in, and the answer `hint` every graph reply
|
|
122
|
+
carries.
|
|
123
|
+
|
|
124
|
+
The registry is read from the public file on `main`,
|
|
125
|
+
`https://raw.githubusercontent.com/chainswarm/chain-insights/main/registry/<network>.json`,
|
|
126
|
+
within 3 seconds, and cached for one hour in
|
|
127
|
+
`~/.chain-insights/cache/dex-registry-<network>.json` (mode 0600). When the
|
|
128
|
+
fetch fails, the stale cache answers; with no cache, the copy packaged with
|
|
129
|
+
the installed version does. A malformed public file never replaces a good
|
|
130
|
+
cache. `registry_source` in the answer says which copy answered: `fetched`,
|
|
131
|
+
`cached` or `packaged`. `CHAIN_INSIGHTS_DEX_REGISTRY_URL` points the fetch
|
|
132
|
+
somewhere else, for tests and the lab; `{network}` in it is filled in.
|
|
133
|
+
|
|
134
|
+
## Host parity
|
|
135
|
+
|
|
136
|
+
**A rule a host never loads does not exist for that host.** Each host loads a
|
|
137
|
+
different part of what this package ships:
|
|
138
|
+
|
|
139
|
+
| What a host loads | Claude Code | Desktop Code mode | Codex | Hermes | scout agent | Desktop chat / claude.ai |
|
|
140
|
+
|---|---|---|---|---|---|---|
|
|
141
|
+
| skill files (`cia setup`) | yes | yes | yes | yes | copied, 2 of 6 | no |
|
|
142
|
+
| server instructions | yes | yes | yes | yes | yes | no (ignored) |
|
|
143
|
+
| `meta_rules` | yes | yes | yes | yes | yes | no (never called) |
|
|
144
|
+
| tool descriptions | yes | yes | yes | yes | yes | yes |
|
|
145
|
+
| result `hint` | yes | yes | yes | yes | yes | yes |
|
|
146
|
+
| protocol tool doors | yes | yes | yes | yes | yes | yes |
|
|
147
|
+
|
|
148
|
+
The scout agent pins the CLI by version in its image and copies only
|
|
149
|
+
`chain-insights-cypher` and `chain-insights-virtuals` into its project, yet asks
|
|
150
|
+
an exchange question by name every run. Until 0.69.0 that question ran with no
|
|
151
|
+
exchange skill: the same gap as Desktop chat. From 0.69.0 the door answers it.
|
|
152
|
+
|
|
153
|
+
```mermaid
|
|
154
|
+
flowchart LR
|
|
155
|
+
Q["a question that names an exchange"] --> D["meta_dex_lookup<br/>the tool door, read by every host"]
|
|
156
|
+
Q --> S["skills/chain-insights-dex*<br/>read by hosts that load skills"]
|
|
157
|
+
S -->|"step 1"| D
|
|
158
|
+
D --> R["addresses, pools, first reads, hint"]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The rule:
|
|
162
|
+
|
|
163
|
+
1. A protocol skill ships with a tool door: frontmatter `door: <tool>`.
|
|
164
|
+
2. The door's description carries the rule the skill opens with, and names
|
|
165
|
+
the skill's trigger words.
|
|
166
|
+
3. The skill names the door as its first step.
|
|
167
|
+
|
|
168
|
+
A host that loads skills gets both. A host that loads none gets the door.
|
|
169
|
+
`tests/host-parity.test.ts` checks it: every
|
|
170
|
+
`skills/chain-insights-<protocol>/SKILL.md` has a door, the door is in
|
|
171
|
+
`LOCAL_TOOL_NAMES` of `src/mcp/proxy.ts`, and its description names the
|
|
172
|
+
skill's trigger words. The core skill `chain-insights-cypher` is exempt: its
|
|
173
|
+
door is `meta_rules` plus the `graph_query` description.
|
|
174
|
+
|
|
104
175
|
## Views in Claude
|
|
105
176
|
|
|
106
177
|
Claude Desktop and Cowork sessions on your computer draw MCP Apps views next
|
package/docs/stability.md
CHANGED
|
@@ -18,12 +18,13 @@ Exit codes are contract. Scripts and agent harnesses may rely on them.
|
|
|
18
18
|
|
|
19
19
|
### MCP tool names
|
|
20
20
|
|
|
21
|
-
The public MCP surface is these
|
|
21
|
+
The public MCP surface is these eleven tools:
|
|
22
22
|
|
|
23
23
|
- `graph_query`
|
|
24
24
|
- `graph_query_batch`
|
|
25
25
|
- `meta_network_capabilities`
|
|
26
26
|
- `meta_schema`
|
|
27
|
+
- `meta_dex_lookup`
|
|
27
28
|
- `meta_rules`
|
|
28
29
|
- `meta_help`
|
|
29
30
|
- `meta_usage_status`
|
|
@@ -31,7 +32,7 @@ The public MCP surface is these ten tools:
|
|
|
31
32
|
- `wallet_balance`
|
|
32
33
|
- `wallet_topup`
|
|
33
34
|
|
|
34
|
-
`graph_expand` is the
|
|
35
|
+
`graph_expand` is the twelfth tool the proxy registers. The view calls it
|
|
35
36
|
when you click in a picture; hosts keep it out of the model's tool list. It
|
|
36
37
|
is not part of the public surface.
|
|
37
38
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chain-insights",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.69.0",
|
|
4
4
|
"description": "AML investigation CLI and MCP proxy for blockchain risk screening and Chain Insights Graph access",
|
|
5
5
|
"homepage": "https://chain-insights.ai",
|
|
6
6
|
"repository": {
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"dist",
|
|
51
51
|
"skills",
|
|
52
52
|
"!skills/rbmk-*",
|
|
53
|
+
"registry",
|
|
53
54
|
"docs/*.md"
|
|
54
55
|
],
|
|
55
56
|
"scripts": {
|
|
@@ -92,7 +93,7 @@
|
|
|
92
93
|
"vitest": "^5.0.3"
|
|
93
94
|
},
|
|
94
95
|
"chainInsights": {
|
|
95
|
-
"dexRegistryVersion":
|
|
96
|
+
"dexRegistryVersion": 2
|
|
96
97
|
},
|
|
97
98
|
"overrides": {
|
|
98
99
|
"ws": "8.21.0",
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# The exchange registry
|
|
2
|
+
|
|
3
|
+
**One reviewed table per network names every decentralized exchange and its
|
|
4
|
+
factory, manager and router contracts.** A user names an exchange (Aero,
|
|
5
|
+
Uniswap), not a factory address. This folder is the one home of name to
|
|
6
|
+
addresses. The proxy tool `meta_dex_lookup` serves it to every host.
|
|
7
|
+
|
|
8
|
+
```mermaid
|
|
9
|
+
flowchart LR
|
|
10
|
+
MD["registry/robinhood.md<br/>one row per exchange and role"]
|
|
11
|
+
PIN["scripts/pin-dex-registry.mjs<br/>md to json, --check in CI"]
|
|
12
|
+
JSON["registry/robinhood.json<br/>the served copy"]
|
|
13
|
+
RAW["raw.githubusercontent.com<br/>main branch"]
|
|
14
|
+
PROXY["meta_dex_lookup<br/>cached 1 hour, packaged copy offline"]
|
|
15
|
+
MEMO["scripts/render-dex-registry.mjs<br/>the memo tables of the exchange skills"]
|
|
16
|
+
MD --> PIN --> JSON
|
|
17
|
+
JSON --> RAW --> PROXY
|
|
18
|
+
JSON --> MEMO
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## The files
|
|
22
|
+
|
|
23
|
+
| File | Rows served | Why |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| [robinhood.md](robinhood.md) | 44 | registry version 2, recorded 2026-10-24; the one `dyor` row is not served |
|
|
26
|
+
| [robinhood.json](robinhood.json) | 29 exchanges | the served copy of `robinhood.md`, never edited by hand |
|
|
27
|
+
|
|
28
|
+
A change to the rows merged on `main` reaches every running proxy within one
|
|
29
|
+
hour. No release is needed for a data change.
|
|
30
|
+
|
|
31
|
+
## The review rule
|
|
32
|
+
|
|
33
|
+
1. **No source link, no row.** Every row carries the one link that confirms
|
|
34
|
+
it. Pin the link to a commit where the source has one.
|
|
35
|
+
2. **No chain check, no serve.** A row is served only when the chain's own
|
|
36
|
+
data shows the address does what its role says:
|
|
37
|
+
- `factory`: a contract that created pools;
|
|
38
|
+
- `manager`: a contract that holds pools;
|
|
39
|
+
- `router`: a contract that forwards swaps.
|
|
40
|
+
Write the check into the file's `## Verification` section.
|
|
41
|
+
3. **An unchecked row stays in the table.** Its verification cell says
|
|
42
|
+
`not served:` and the reason. It is served later, when a check passes.
|
|
43
|
+
4. **The chain wins over the source.** When the chain shows a different
|
|
44
|
+
factory or block than the source, the row follows the chain and the file
|
|
45
|
+
says so.
|
|
46
|
+
5. **An exchange needs a pool row.** An exchange with no served row of role
|
|
47
|
+
`factory` or `manager` is refused whole, router rows included.
|
|
48
|
+
6. **One key per exchange.** A second spelling is an alias, never a second
|
|
49
|
+
key. Two exchanges may share an alias only when a user's word fits both
|
|
50
|
+
(`Balancer`): the lookup then lists both and guesses neither.
|
|
51
|
+
|
|
52
|
+
## The columns
|
|
53
|
+
|
|
54
|
+
The `## Rows` table has twelve columns, in this order.
|
|
55
|
+
|
|
56
|
+
| Column | Means |
|
|
57
|
+
|---|---|
|
|
58
|
+
| dex | the key as a user says it, in lowercase: `aero`, `uniswap-v4`. Letters, digits, `.`, `_` and `-`, at most 32 |
|
|
59
|
+
| display | the exchange name as a reader sees it, at most 96 characters. Every row of an exchange carries the same display |
|
|
60
|
+
| aliases | the names a user types, comma separated, on one row of the exchange or spread over several |
|
|
61
|
+
| trail type | how money is traced through the exchange: `plain`, `vault`, `gauge` or `launchpad`. Every row of an exchange carries the same trail type |
|
|
62
|
+
| adapter | the adapter id of the DEX indexer that reads the row's pools: `cl-gauge-v1`, `uniswap-v4` |
|
|
63
|
+
| role | `factory` creates pools, `manager` holds pools, `router` forwards swaps |
|
|
64
|
+
| address | lowercase `0x` hex in backticks |
|
|
65
|
+
| valid from block | the first block the row holds from, `0` when it is not known yet |
|
|
66
|
+
| example pool | optional: one pool of the exchange, read live by the release suite. Empty or `-` when not given |
|
|
67
|
+
| pool floor | optional: the least number of pools the exchange must show on a lane. Empty or `-` when not given |
|
|
68
|
+
| source link | the one link that confirms the row, as `[text](https://…)` |
|
|
69
|
+
| verification | `served`, or `not served:` and the reason |
|
|
70
|
+
|
|
71
|
+
Each network file states `Registry version: N`. Bump it in every change to
|
|
72
|
+
the rows. Each file also states `Recorded on: YYYY-MM-DD`, the day of the
|
|
73
|
+
last row change. The pin script copies both into the served copy.
|
|
74
|
+
|
|
75
|
+
A row is refused for one of these reasons:
|
|
76
|
+
|
|
77
|
+
| Reason | When |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `malformed row` | the row has the wrong number of cells, or no display |
|
|
80
|
+
| `duplicate row` | the same key, role and address twice |
|
|
81
|
+
| `bad dex key` | the key is not lowercase letters, digits, `.`, `_` or `-` |
|
|
82
|
+
| `bad address` | the address is not lowercase `0x` and 40 hex digits |
|
|
83
|
+
| `unknown role` | the role is not `factory`, `manager` or `router` |
|
|
84
|
+
| `unknown trail type` | the trail type is not `plain`, `vault`, `gauge` or `launchpad` |
|
|
85
|
+
| `bad adapter` | the adapter is empty or not an id |
|
|
86
|
+
| `no source link` | the source link is not `[text](https://…)` |
|
|
87
|
+
| `bad block`, `bad example pool`, `bad pool floor` | the cell is not a number or a pool |
|
|
88
|
+
| `too long` | a value does not fit its column |
|
|
89
|
+
| `dex rows disagree` | two rows of one exchange give a different display, trail type, example pool or pool floor |
|
|
90
|
+
| `no factory or manager row for the dex` | the exchange has router rows only |
|
|
91
|
+
|
|
92
|
+
## The check
|
|
93
|
+
|
|
94
|
+
Regenerate the served copy after a row change:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
node scripts/pin-dex-registry.mjs registry/robinhood.md
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Check that the committed copy is what the file gives. CI runs this on every
|
|
101
|
+
pull request:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
node scripts/pin-dex-registry.mjs --check registry/robinhood.md
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
It prints `rows: N served, M refused, K not served` and
|
|
108
|
+
`registry/robinhood.json up to date, registry version V`. It exits 1 when a
|
|
109
|
+
row is refused or the committed copy differs, and names both versions.
|
|
110
|
+
|
|
111
|
+
The skill memo follows the served copy:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
node scripts/render-dex-registry.mjs # rewrite the memo tables
|
|
115
|
+
node scripts/render-dex-registry.mjs --check # exit 1 when a memo table differs
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## The served copy
|
|
119
|
+
|
|
120
|
+
`registry/<network>.json` has the schema `chain-insights.dex-registry.v1`:
|
|
121
|
+
|
|
122
|
+
- one entry per exchange, in the order the file first names it;
|
|
123
|
+
- `factories` lists its `factory` and `manager` addresses, `routers` its
|
|
124
|
+
`router` addresses; an exchange with no router row lists its manager there,
|
|
125
|
+
because a vault forwards its own swaps;
|
|
126
|
+
- `adapters`, `since_block` and `source` come from the factory and manager
|
|
127
|
+
rows: every adapter in order, the lowest block, the first source;
|
|
128
|
+
- `example_pool` and `pool_floor` are `null` when the exchange gives none.
|
|
129
|
+
|
|
130
|
+
The release suite `tests/dex-registry-live.integration.test.ts` reads every
|
|
131
|
+
row against a live graph per release candidate: every factory, manager and
|
|
132
|
+
router is a contract, every factory with a pool floor shows at least that many
|
|
133
|
+
pools, and every example pool is a `:Pool` deployed by its factory.
|