chain-insights 0.35.1 → 0.37.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 +2 -3
- package/bin/install.cjs +1 -1
- package/dist/{capabilities-B-M8vAYp.cjs → capabilities-BbAQ4Tfa.cjs} +36 -0
- package/dist/{capabilities-ByKP5l0G.mjs → capabilities-C8DQSfyW.mjs} +37 -1
- package/dist/capabilities-C8DQSfyW.mjs.map +1 -0
- package/dist/cli.cjs +5 -5
- package/dist/cli.mjs +5 -5
- package/dist/mcp-proxy.cjs +10 -8
- package/dist/mcp-proxy.d.cts.map +1 -1
- package/dist/mcp-proxy.d.mts.map +1 -1
- package/dist/mcp-proxy.mjs +10 -8
- package/dist/mcp-proxy.mjs.map +1 -1
- package/dist/{public-tools-CZTDEjqJ.cjs → public-tools-C6QY_gam.cjs} +1 -1
- package/dist/{public-tools-CsBmOyeQ.mjs → public-tools-rKIzmn6f.mjs} +2 -2
- package/dist/public-tools-rKIzmn6f.mjs.map +1 -0
- package/docs/graph-query-compatibility.md +117 -51
- package/docs/graph-tools.md +28 -7
- package/docs/mcp-proxy.md +3 -4
- package/package.json +1 -1
- package/skills/chain-insights-address-risk/SKILL.md +2 -2
- package/skills/chain-insights-cypher/SKILL.md +15 -4
- package/skills/chain-insights-schema-evm/SKILL.md +216 -61
- package/dist/capabilities-ByKP5l0G.mjs.map +0 -1
- package/dist/public-tools-CsBmOyeQ.mjs.map +0 -1
- package/skills/chain-insights-schema-bittensor/SKILL.md +0 -99
package/README.md
CHANGED
|
@@ -163,9 +163,8 @@ database and `Address` carries no `network` property at all. See
|
|
|
163
163
|
[Graph query compatibility](docs/graph-query-compatibility.md).
|
|
164
164
|
|
|
165
165
|
Agent installs include `chain-insights-address-risk` for one-address
|
|
166
|
-
screens, `chain-insights-cypher` for graph-query dialect rules,
|
|
167
|
-
`chain-insights-schema-evm` for the EVM / Robinhood graph map
|
|
168
|
-
`chain-insights-schema-bittensor` for the Bittensor graph map.
|
|
166
|
+
screens, `chain-insights-cypher` for graph-query dialect rules, and
|
|
167
|
+
`chain-insights-schema-evm` for the EVM / Robinhood graph map.
|
|
169
168
|
|
|
170
169
|
## Billing: Billable Units
|
|
171
170
|
|
package/bin/install.cjs
CHANGED
|
@@ -48,7 +48,6 @@ const srcSkillsDir = path.join(__dirname, '..', 'skills')
|
|
|
48
48
|
const PUBLIC_SKILL_NAMES = Object.freeze([
|
|
49
49
|
'chain-insights-address-risk',
|
|
50
50
|
'chain-insights-cypher',
|
|
51
|
-
'chain-insights-schema-bittensor',
|
|
52
51
|
'chain-insights-schema-evm',
|
|
53
52
|
])
|
|
54
53
|
const RETIRED_SKILL_NAMES = Object.freeze([
|
|
@@ -56,6 +55,7 @@ const RETIRED_SKILL_NAMES = Object.freeze([
|
|
|
56
55
|
'chain-insights-developer-experience',
|
|
57
56
|
'chain-insights-investigation',
|
|
58
57
|
'chain-insights-monitoring',
|
|
58
|
+
'chain-insights-schema-bittensor',
|
|
59
59
|
'ci-status',
|
|
60
60
|
'test-chain-insights-graph',
|
|
61
61
|
])
|
|
@@ -19,6 +19,38 @@ function advertisedTools(raw) {
|
|
|
19
19
|
for (const [name, status] of Object.entries(raw)) if (name.trim() !== "" && typeof status === "string") tools[name] = status;
|
|
20
20
|
return tools;
|
|
21
21
|
}
|
|
22
|
+
function advertisedLaneProgress(raw) {
|
|
23
|
+
if (!Array.isArray(raw)) return void 0;
|
|
24
|
+
const lanes = [];
|
|
25
|
+
for (const entry of raw) {
|
|
26
|
+
if (!isRecord(entry) || typeof entry.lane !== "string" || entry.lane.trim() === "") continue;
|
|
27
|
+
if (typeof entry.height === "number") lanes.push({
|
|
28
|
+
lane: entry.lane,
|
|
29
|
+
height: entry.height
|
|
30
|
+
});
|
|
31
|
+
else if (entry.height === null) lanes.push({
|
|
32
|
+
lane: entry.lane,
|
|
33
|
+
height: null
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return lanes;
|
|
37
|
+
}
|
|
38
|
+
function advertisedGraphProgress(raw) {
|
|
39
|
+
if (!isRecord(raw) || typeof raw.complete_through_block !== "number") return void 0;
|
|
40
|
+
if (!Array.isArray(raw.layers)) return void 0;
|
|
41
|
+
const layers = [];
|
|
42
|
+
for (const entry of raw.layers) {
|
|
43
|
+
if (!isRecord(entry) || typeof entry.layer !== "string" || typeof entry.position !== "number") continue;
|
|
44
|
+
layers.push({
|
|
45
|
+
layer: entry.layer,
|
|
46
|
+
position: entry.position
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
complete_through_block: raw.complete_through_block,
|
|
51
|
+
layers
|
|
52
|
+
};
|
|
53
|
+
}
|
|
22
54
|
function advertisedNetwork(raw) {
|
|
23
55
|
if (!isRecord(raw) || typeof raw.network !== "string" || raw.network.trim() === "") return null;
|
|
24
56
|
const network = raw.network.trim();
|
|
@@ -33,6 +65,10 @@ function advertisedNetwork(raw) {
|
|
|
33
65
|
if (raw.default === false) capability.default = false;
|
|
34
66
|
if (isRecord(raw.coverage)) capability.coverage = raw.coverage;
|
|
35
67
|
if (isRecord(raw.freshness)) capability.freshness = raw.freshness;
|
|
68
|
+
const laneProgress = advertisedLaneProgress(raw.lane_progress);
|
|
69
|
+
if (laneProgress) capability.lane_progress = laneProgress;
|
|
70
|
+
const graphProgress = advertisedGraphProgress(raw.graph_progress);
|
|
71
|
+
if (graphProgress) capability.graph_progress = graphProgress;
|
|
36
72
|
return capability;
|
|
37
73
|
}
|
|
38
74
|
/** Repeat GraphRAG's network list. CIA does not add, drop, or invent names. */
|
|
@@ -19,6 +19,38 @@ function advertisedTools(raw) {
|
|
|
19
19
|
for (const [name, status] of Object.entries(raw)) if (name.trim() !== "" && typeof status === "string") tools[name] = status;
|
|
20
20
|
return tools;
|
|
21
21
|
}
|
|
22
|
+
function advertisedLaneProgress(raw) {
|
|
23
|
+
if (!Array.isArray(raw)) return void 0;
|
|
24
|
+
const lanes = [];
|
|
25
|
+
for (const entry of raw) {
|
|
26
|
+
if (!isRecord(entry) || typeof entry.lane !== "string" || entry.lane.trim() === "") continue;
|
|
27
|
+
if (typeof entry.height === "number") lanes.push({
|
|
28
|
+
lane: entry.lane,
|
|
29
|
+
height: entry.height
|
|
30
|
+
});
|
|
31
|
+
else if (entry.height === null) lanes.push({
|
|
32
|
+
lane: entry.lane,
|
|
33
|
+
height: null
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return lanes;
|
|
37
|
+
}
|
|
38
|
+
function advertisedGraphProgress(raw) {
|
|
39
|
+
if (!isRecord(raw) || typeof raw.complete_through_block !== "number") return void 0;
|
|
40
|
+
if (!Array.isArray(raw.layers)) return void 0;
|
|
41
|
+
const layers = [];
|
|
42
|
+
for (const entry of raw.layers) {
|
|
43
|
+
if (!isRecord(entry) || typeof entry.layer !== "string" || typeof entry.position !== "number") continue;
|
|
44
|
+
layers.push({
|
|
45
|
+
layer: entry.layer,
|
|
46
|
+
position: entry.position
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
complete_through_block: raw.complete_through_block,
|
|
51
|
+
layers
|
|
52
|
+
};
|
|
53
|
+
}
|
|
22
54
|
function advertisedNetwork(raw) {
|
|
23
55
|
if (!isRecord(raw) || typeof raw.network !== "string" || raw.network.trim() === "") return null;
|
|
24
56
|
const network = raw.network.trim();
|
|
@@ -33,6 +65,10 @@ function advertisedNetwork(raw) {
|
|
|
33
65
|
if (raw.default === false) capability.default = false;
|
|
34
66
|
if (isRecord(raw.coverage)) capability.coverage = raw.coverage;
|
|
35
67
|
if (isRecord(raw.freshness)) capability.freshness = raw.freshness;
|
|
68
|
+
const laneProgress = advertisedLaneProgress(raw.lane_progress);
|
|
69
|
+
if (laneProgress) capability.lane_progress = laneProgress;
|
|
70
|
+
const graphProgress = advertisedGraphProgress(raw.graph_progress);
|
|
71
|
+
if (graphProgress) capability.graph_progress = graphProgress;
|
|
36
72
|
return capability;
|
|
37
73
|
}
|
|
38
74
|
/** Repeat GraphRAG's network list. CIA does not add, drop, or invent names. */
|
|
@@ -160,4 +196,4 @@ function formatNetworkCapability(network) {
|
|
|
160
196
|
//#endregion
|
|
161
197
|
export { mirrorGraphNetworkCapabilities as n, capabilities_exports as t };
|
|
162
198
|
|
|
163
|
-
//# sourceMappingURL=capabilities-
|
|
199
|
+
//# sourceMappingURL=capabilities-C8DQSfyW.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities-C8DQSfyW.mjs","names":[],"sources":["../src/mcp/capabilities.ts"],"sourcesContent":["import type { InvestigatorConfig } from '../config/schema.js'\nimport { applyMcpAuthHeaders, resolveGraphMcpEndpoint } from './client.js'\n\nexport interface NetworkTopologyLayer {\n enabled: boolean\n coverage?: {\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n chain_tip_block?: number\n blocks_behind_tip?: number\n /** Lowest height among the raw-data lanes that have started. Above it an empty answer may mean \"not indexed yet\". */\n complete_through_block?: number\n }\n}\n\nexport interface NetworkLayerCapability {\n enabled: boolean\n live?: NetworkTopologyLayer\n archive?: NetworkTopologyLayer\n}\n\nexport interface NetworkCapability {\n network: string\n display_name?: string\n status: string\n default?: boolean\n layers: Record<string, NetworkLayerCapability>\n tools: Record<string, string>\n coverage?: {\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n chain_tip_block?: number\n blocks_behind_tip?: number\n /** Lowest height among the raw-data lanes that have started. Above it an empty answer may mean \"not indexed yet\". */\n complete_through_block?: number\n }\n freshness?: {\n last_processed_at?: string\n last_successful_sync_at?: string\n max_data_age_seconds?: number\n last_processing_duration_seconds?: number\n }\n lane_progress?: NetworkLaneProgress[]\n graph_progress?: NetworkGraphProgress\n}\n\n/** One writer lane's committed height. null: the lane has not started. */\nexport interface NetworkLaneProgress {\n lane: string\n height: number | null\n}\n\n/** How far each block layer of the topology graph reaches, and their lowest. */\nexport interface NetworkGraphProgress {\n complete_through_block: number\n layers: { layer: string; position: number }[]\n}\n\nexport interface NetworkCapabilitiesDocument {\n schema: 'chain-insights.network-capabilities.v1'\n networks: NetworkCapability[]\n}\n\nconst AVAILABLE_TOOLS_PER_LINE = 3\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction advertisedTools(raw: unknown): Record<string, string> {\n if (!isRecord(raw)) return {}\n const tools: Record<string, string> = {}\n for (const [name, status] of Object.entries(raw)) {\n if (name.trim() !== '' && typeof status === 'string') tools[name] = status\n }\n return tools\n}\n\nfunction advertisedLaneProgress(raw: unknown): NetworkLaneProgress[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const lanes: NetworkLaneProgress[] = []\n for (const entry of raw) {\n if (!isRecord(entry) || typeof entry.lane !== 'string' || entry.lane.trim() === '') continue\n if (typeof entry.height === 'number') lanes.push({ lane: entry.lane, height: entry.height })\n else if (entry.height === null) lanes.push({ lane: entry.lane, height: null })\n }\n return lanes\n}\n\nfunction advertisedGraphProgress(raw: unknown): NetworkGraphProgress | undefined {\n if (!isRecord(raw) || typeof raw.complete_through_block !== 'number') return undefined\n if (!Array.isArray(raw.layers)) return undefined\n const layers: NetworkGraphProgress['layers'] = []\n for (const entry of raw.layers) {\n if (!isRecord(entry) || typeof entry.layer !== 'string' || typeof entry.position !== 'number')\n continue\n layers.push({ layer: entry.layer, position: entry.position })\n }\n return { complete_through_block: raw.complete_through_block, layers }\n}\n\nfunction advertisedNetwork(raw: unknown): NetworkCapability | null {\n if (!isRecord(raw) || typeof raw.network !== 'string' || raw.network.trim() === '') {\n return null\n }\n const network = raw.network.trim()\n const capability: NetworkCapability = {\n network,\n display_name:\n typeof raw.display_name === 'string' && raw.display_name.trim() !== ''\n ? raw.display_name\n : network,\n status: typeof raw.status === 'string' && raw.status.trim() !== '' ? raw.status : 'live',\n layers: {},\n tools: advertisedTools(raw.tools),\n }\n if (raw.default === true) capability.default = true\n if (raw.default === false) capability.default = false\n if (isRecord(raw.coverage)) capability.coverage = raw.coverage as NetworkCapability['coverage']\n if (isRecord(raw.freshness))\n capability.freshness = raw.freshness as NetworkCapability['freshness']\n const laneProgress = advertisedLaneProgress(raw.lane_progress)\n if (laneProgress) capability.lane_progress = laneProgress\n const graphProgress = advertisedGraphProgress(raw.graph_progress)\n if (graphProgress) capability.graph_progress = graphProgress\n return capability\n}\n\n/** Repeat GraphRAG's network list. CIA does not add, drop, or invent names. */\nexport function mirrorGraphNetworkCapabilities(document: {\n networks: unknown[]\n}): NetworkCapabilitiesDocument {\n return {\n schema: 'chain-insights.network-capabilities.v1',\n networks: document.networks\n .map((network) => advertisedNetwork(network))\n .filter((network): network is NetworkCapability => network !== null),\n }\n}\n\nfunction publicNetworkCapabilities(\n document: NetworkCapabilitiesDocument\n): NetworkCapabilitiesDocument {\n return mirrorGraphNetworkCapabilities(document)\n}\n\nfunction metadataNetworksUrl(endpoint: string): URL {\n const url = new URL(endpoint)\n url.pathname = '/metadata/networks'\n url.search = ''\n url.hash = ''\n return url\n}\n\nexport async function fetchNetworkCapabilities(\n config: Pick<InvestigatorConfig, 'graphMcpAuthToken' | 'graphMcpMode' | 'graphMcpEndpoint'>\n): Promise<NetworkCapabilitiesDocument> {\n const endpoint = resolveGraphMcpEndpoint(config)\n const request = metadataNetworksUrl(endpoint)\n const headers = new Headers()\n const token = config.graphMcpAuthToken?.trim()\n if (token) {\n applyMcpAuthHeaders(headers, token)\n }\n let response: Response\n try {\n response = await fetch(request, { headers })\n } catch (err) {\n throw new Error(`network capabilities unavailable at ${request}: ${(err as Error).message}`)\n }\n if (!response.ok) {\n throw new Error(`network capabilities unavailable at ${request}: HTTP ${response.status}`)\n }\n const parsed = (await response.json()) as NetworkCapabilitiesDocument\n if (\n parsed.schema !== 'chain-insights.network-capabilities.v1' ||\n !Array.isArray(parsed.networks)\n ) {\n throw new Error('network capabilities response has unsupported schema')\n }\n return publicNetworkCapabilities(parsed)\n}\n\nfunction availableTools(network: NetworkCapability): string[] {\n const tools = Object.entries(network.tools ?? {})\n .filter(([, status]) => status === 'available')\n .map(([name]) => name)\n return tools.sort()\n}\n\nfunction availableToolLines(network: NetworkCapability): string[] {\n const tools = availableTools(network)\n if (tools.length === 0) return ['none']\n const lines: string[] = []\n for (let index = 0; index < tools.length; index += AVAILABLE_TOOLS_PER_LINE) {\n lines.push(tools.slice(index, index + AVAILABLE_TOOLS_PER_LINE).join(', '))\n }\n return lines\n}\n\nfunction shortDate(value?: string): string {\n if (!value) return ''\n return value.slice(0, 10)\n}\n\nfunction datasetLabel(network: NetworkCapability): string {\n const coverage = network.coverage\n if (!coverage) return 'unknown'\n const blockRange =\n coverage.from_block !== undefined && coverage.to_block !== undefined\n ? `${coverage.from_block}..${coverage.to_block}`\n : 'blocks unknown'\n const dateRange =\n coverage.from_timestamp && coverage.to_timestamp\n ? `${shortDate(coverage.from_timestamp)}..${shortDate(coverage.to_timestamp)}`\n : ''\n if (blockRange === 'blocks unknown' && dateRange === '') return 'unknown'\n if (blockRange === 'blocks unknown') return dateRange\n if (dateRange === '') return blockRange\n return `${blockRange} / ${dateRange}`\n}\n\nfunction displayName(network: NetworkCapability): string {\n return network.display_name || network.network\n}\n\nfunction statusLabel(network: NetworkCapability): string {\n return network.default ? `${network.status} (default)` : network.status\n}\n\nfunction formatTable(headers: string[], rows: string[][], minimumWidths: number[]): string {\n const widths = headers.map((header, index) =>\n Math.max(\n minimumWidths[index] ?? 0,\n header.length,\n ...rows.map((row) => row[index]?.length ?? 0)\n )\n )\n const row = (values: string[]) =>\n values.map((value, index) => value.padEnd(widths[index]!)).join(' ')\n return [row(headers), widths.map((width) => '-'.repeat(width)).join(' '), ...rows.map(row)].join(\n '\\n'\n )\n}\n\nexport function formatNetworkOverview(document: NetworkCapabilitiesDocument): string {\n if (document.networks.length === 0) return 'No supported networks advertised.'\n const rows = document.networks.map((network) => [\n displayName(network),\n statusLabel(network),\n datasetLabel(network),\n ])\n return formatTable(['Network', 'Status', 'Dataset'], rows, [14, 10, 38])\n}\n\nexport function formatNetworkCapabilities(document: NetworkCapabilitiesDocument): string {\n if (document.networks.length === 0) return 'No supported networks advertised.'\n const networkRows = document.networks.flatMap((network) => {\n const toolLines = availableToolLines(network)\n return toolLines.map((toolLine, index) => [\n index === 0 ? displayName(network) : '',\n index === 0 ? datasetLabel(network) : '',\n toolLine,\n ])\n })\n return formatTable(['Network', 'Dataset', 'Chain Insights tools'], networkRows, [14, 38, 64])\n}\n\nexport function findNetworkCapability(\n document: NetworkCapabilitiesDocument,\n name: string\n): NetworkCapability | undefined {\n const normalizedName = name.trim().toLowerCase()\n if (!normalizedName) return undefined\n return document.networks.find((network) =>\n [network.network, network.display_name].some(\n (candidate) => candidate?.trim().toLowerCase() === normalizedName\n )\n )\n}\n\nexport function formatNetworkCapability(network: NetworkCapability): string {\n const rows = [\n ['Network', network.display_name || network.network],\n ['Identifier', network.network],\n ['Status', network.default ? `${network.status} (default)` : network.status],\n ['Dataset', datasetLabel(network)],\n ['Available tools', availableTools(network).join(', ') || 'none'],\n ]\n const labelWidth = Math.max(...rows.map(([label]) => label.length))\n return rows.map(([label, value]) => `${label.padEnd(labelWidth)} ${value}`).join('\\n')\n}\n"],"mappings":"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"}
|
package/dist/cli.cjs
CHANGED
|
@@ -213,21 +213,21 @@ async function withGraphMcpClient(name, fn) {
|
|
|
213
213
|
}
|
|
214
214
|
async function printNetworkCapabilities(opts) {
|
|
215
215
|
const { loadConfig } = await Promise.resolve().then(() => require("./config-B542AM4w.cjs")).then((n) => n.config_exports);
|
|
216
|
-
const { fetchNetworkCapabilities, formatNetworkCapabilities } = await Promise.resolve().then(() => require("./capabilities-
|
|
216
|
+
const { fetchNetworkCapabilities, formatNetworkCapabilities } = await Promise.resolve().then(() => require("./capabilities-BbAQ4Tfa.cjs")).then((n) => n.capabilities_exports);
|
|
217
217
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
218
218
|
if (opts.json) console.log(JSON.stringify(document, null, 2));
|
|
219
219
|
else console.log(formatNetworkCapabilities(document));
|
|
220
220
|
}
|
|
221
221
|
async function printNetworkOverview(opts) {
|
|
222
222
|
const { loadConfig } = await Promise.resolve().then(() => require("./config-B542AM4w.cjs")).then((n) => n.config_exports);
|
|
223
|
-
const { fetchNetworkCapabilities, formatNetworkOverview } = await Promise.resolve().then(() => require("./capabilities-
|
|
223
|
+
const { fetchNetworkCapabilities, formatNetworkOverview } = await Promise.resolve().then(() => require("./capabilities-BbAQ4Tfa.cjs")).then((n) => n.capabilities_exports);
|
|
224
224
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
225
225
|
if (opts.json) console.log(JSON.stringify(document, null, 2));
|
|
226
226
|
else console.log(formatNetworkOverview(document));
|
|
227
227
|
}
|
|
228
228
|
async function printNetworkCapability(name, opts) {
|
|
229
229
|
const { loadConfig } = await Promise.resolve().then(() => require("./config-B542AM4w.cjs")).then((n) => n.config_exports);
|
|
230
|
-
const { fetchNetworkCapabilities, findNetworkCapability, formatNetworkCapability } = await Promise.resolve().then(() => require("./capabilities-
|
|
230
|
+
const { fetchNetworkCapabilities, findNetworkCapability, formatNetworkCapability } = await Promise.resolve().then(() => require("./capabilities-BbAQ4Tfa.cjs")).then((n) => n.capabilities_exports);
|
|
231
231
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
232
232
|
const network = findNetworkCapability(document, name);
|
|
233
233
|
if (!network) {
|
|
@@ -301,11 +301,11 @@ async function printSubscriptionPassGuidance(pass) {
|
|
|
301
301
|
function addAmlAddressRiskCommand(parent, networksCommand) {
|
|
302
302
|
parent.addCommand(createCliCommand("aml-address-risk").description("CIA workflow: screen an address for AML risk, exchange behavior, and optional comparison with another address").requiredOption("--address <address>", "Full blockchain address to screen").requiredOption("--network <network>", `Network to query. Run \`${networksCommand}\` for supported networks.`).option("--compare-address <address>", "Optional second address to compare against the screened address").option("--json", "Print machine-readable JSON output").option("--version <version>", "AML tool contract version. Omit to use the latest version (currently v1)").addOption(new commander.Option("--tool-version <version>", "Internal alias for the AML tool version").hideHelp()).action(async (opts) => {
|
|
303
303
|
try {
|
|
304
|
-
const { resolveAmlAddressRiskVersion } = await Promise.resolve().then(() => require("./public-tools-
|
|
304
|
+
const { resolveAmlAddressRiskVersion } = await Promise.resolve().then(() => require("./public-tools-C6QY_gam.cjs"));
|
|
305
305
|
const requestedVersion = opts.toolVersion ?? opts.version;
|
|
306
306
|
resolveAmlAddressRiskVersion(requestedVersion);
|
|
307
307
|
await withGraphMcpClient("chain-insights-cli-aml-address-risk", async (client) => {
|
|
308
|
-
const { runAmlAddressRisk } = await Promise.resolve().then(() => require("./public-tools-
|
|
308
|
+
const { runAmlAddressRisk } = await Promise.resolve().then(() => require("./public-tools-C6QY_gam.cjs"));
|
|
309
309
|
const result = await runAmlAddressRisk(client, {
|
|
310
310
|
address: opts.address,
|
|
311
311
|
network: opts.network,
|
package/dist/cli.mjs
CHANGED
|
@@ -211,21 +211,21 @@ async function withGraphMcpClient(name, fn) {
|
|
|
211
211
|
}
|
|
212
212
|
async function printNetworkCapabilities(opts) {
|
|
213
213
|
const { loadConfig } = await import("./config-DbbPiGk0.mjs").then((n) => n.t);
|
|
214
|
-
const { fetchNetworkCapabilities, formatNetworkCapabilities } = await import("./capabilities-
|
|
214
|
+
const { fetchNetworkCapabilities, formatNetworkCapabilities } = await import("./capabilities-C8DQSfyW.mjs").then((n) => n.t);
|
|
215
215
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
216
216
|
if (opts.json) console.log(JSON.stringify(document, null, 2));
|
|
217
217
|
else console.log(formatNetworkCapabilities(document));
|
|
218
218
|
}
|
|
219
219
|
async function printNetworkOverview(opts) {
|
|
220
220
|
const { loadConfig } = await import("./config-DbbPiGk0.mjs").then((n) => n.t);
|
|
221
|
-
const { fetchNetworkCapabilities, formatNetworkOverview } = await import("./capabilities-
|
|
221
|
+
const { fetchNetworkCapabilities, formatNetworkOverview } = await import("./capabilities-C8DQSfyW.mjs").then((n) => n.t);
|
|
222
222
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
223
223
|
if (opts.json) console.log(JSON.stringify(document, null, 2));
|
|
224
224
|
else console.log(formatNetworkOverview(document));
|
|
225
225
|
}
|
|
226
226
|
async function printNetworkCapability(name, opts) {
|
|
227
227
|
const { loadConfig } = await import("./config-DbbPiGk0.mjs").then((n) => n.t);
|
|
228
|
-
const { fetchNetworkCapabilities, findNetworkCapability, formatNetworkCapability } = await import("./capabilities-
|
|
228
|
+
const { fetchNetworkCapabilities, findNetworkCapability, formatNetworkCapability } = await import("./capabilities-C8DQSfyW.mjs").then((n) => n.t);
|
|
229
229
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
230
230
|
const network = findNetworkCapability(document, name);
|
|
231
231
|
if (!network) {
|
|
@@ -299,11 +299,11 @@ async function printSubscriptionPassGuidance(pass) {
|
|
|
299
299
|
function addAmlAddressRiskCommand(parent, networksCommand) {
|
|
300
300
|
parent.addCommand(createCliCommand("aml-address-risk").description("CIA workflow: screen an address for AML risk, exchange behavior, and optional comparison with another address").requiredOption("--address <address>", "Full blockchain address to screen").requiredOption("--network <network>", `Network to query. Run \`${networksCommand}\` for supported networks.`).option("--compare-address <address>", "Optional second address to compare against the screened address").option("--json", "Print machine-readable JSON output").option("--version <version>", "AML tool contract version. Omit to use the latest version (currently v1)").addOption(new Option("--tool-version <version>", "Internal alias for the AML tool version").hideHelp()).action(async (opts) => {
|
|
301
301
|
try {
|
|
302
|
-
const { resolveAmlAddressRiskVersion } = await import("./public-tools-
|
|
302
|
+
const { resolveAmlAddressRiskVersion } = await import("./public-tools-rKIzmn6f.mjs");
|
|
303
303
|
const requestedVersion = opts.toolVersion ?? opts.version;
|
|
304
304
|
resolveAmlAddressRiskVersion(requestedVersion);
|
|
305
305
|
await withGraphMcpClient("chain-insights-cli-aml-address-risk", async (client) => {
|
|
306
|
-
const { runAmlAddressRisk } = await import("./public-tools-
|
|
306
|
+
const { runAmlAddressRisk } = await import("./public-tools-rKIzmn6f.mjs");
|
|
307
307
|
const result = await runAmlAddressRisk(client, {
|
|
308
308
|
address: opts.address,
|
|
309
309
|
network: opts.network,
|
package/dist/mcp-proxy.cjs
CHANGED
|
@@ -5,7 +5,7 @@ const require_client = require("./client-Ypw9BNbn.cjs");
|
|
|
5
5
|
const require_tool_visibility = require("./tool-visibility-CyZqYnQA.cjs");
|
|
6
6
|
const require_usage_status = require("./usage-status-BdwwgYsu.cjs");
|
|
7
7
|
const require_subscription_status = require("./subscription-status-Cm7LqY_h.cjs");
|
|
8
|
-
const require_capabilities = require("./capabilities-
|
|
8
|
+
const require_capabilities = require("./capabilities-BbAQ4Tfa.cjs");
|
|
9
9
|
const require_action_log = require("./action-log-HniMMF7f.cjs");
|
|
10
10
|
let node_path = require("node:path");
|
|
11
11
|
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
@@ -61,22 +61,24 @@ const CHAIN_INSIGHTS_WORKFLOW = [
|
|
|
61
61
|
const GRAPH_SCHEMA_HINTS = [
|
|
62
62
|
"Graph query hints:",
|
|
63
63
|
"- Call meta_network_capabilities first. Pass network= exactly as GraphRAG advertised it. CIA does not pick a default network.",
|
|
64
|
+
"- Coverage is per kind of data. coverage.complete_through_block is the floor of USE facts: below it every raw relationship is indexed. graph_progress.complete_through_block is the floor of USE topology, and graph_progress.layers gives each link kind its own height. An empty answer above the floor may mean not indexed yet, not absent on chain. An absent chain_tip_block means the indexers are catching up: read freshness.max_data_age_seconds for the lag.",
|
|
64
65
|
"- The graph is address-grain. The only topology money node label is Address, keyed by the raw chain-native H160 address on EVM networks, for example 0x1874a43d7c6d888f9eda3d22a3a49704e3cadb24. The network value on Address nodes matches the tool argument. There is no separate identity key.",
|
|
65
|
-
"- Address nodes carry address, network, labels, and is_exchange. (:Address)-[:LINKED]-(:Address) is an undirected ownership-overlay edge (basis derived/associated, plus confidence, source_event, declared_owner) asserting the two addresses are controlled by the same actor. LINKED is served on the topology graph only. Enumerate LINKED neighbors with MATCH (a:Address {address: $addr})-[l:LINKED]-(b:Address) RETURN b.address, b.network, l.basis, l.confidence.",
|
|
66
|
+
"- Address nodes carry address, network, labels, and the role flags is_exchange, is_scam, is_victim and is_sanctioned. (:Address)-[:LINKED]-(:Address) is an undirected ownership-overlay edge (basis derived/associated, plus confidence, source_event, declared_owner) asserting the two addresses are controlled by the same actor. LINKED is served on the topology graph only. Enumerate LINKED neighbors with MATCH (a:Address {address: $addr})-[l:LINKED]-(b:Address) RETURN b.address, b.network, l.basis, l.confidence.",
|
|
67
|
+
"- Labels hold role words, never detector names: Exchange (plus the exchange name), Scam, Victim and Sanctioned (plus the entity name). Each role is also a node label and a flag on the Address: :Exchange and is_exchange follow the exchange label, :Scam and is_scam the risk label, :Victim and is_victim the protection label, :Sanctioned and is_sanctioned the sanctioned label. Each flag is present only when true and absent otherwise, never false: test IS NOT NULL or IS NULL, not = false. A withdrawn label removes its node label and its flag.",
|
|
66
68
|
"- Address nodes also carry a risk verdict (risk_score float, risk_level string) plus base activity rollups: degree_in/degree_out/degree_total (distinct counterparty addresses), tx_in_count/tx_out_count/tx_total_count, total_in_usd/total_out_usd/total_volume_usd, net_flow_usd (in minus out; positive = net receiver) — all computed from external flows only — and first_activity_timestamp/last_activity_timestamp/activity_span_days, which include all flows (self-loops included). FLOWS_TO edges carry exactly tx_count, amount_usd_sum (total money flow, token and native value merged), first_seen_timestamp, last_seen_timestamp. Lifetime aggregates are the only serving window. Averages are computed inline (amount_usd_sum / toFloat(tx_count)); transaction anchors resolve through USE facts: MATCH (a:Address {address: $from})-[t:TRANSFER]->(b:Address {address: $to}) RETURN t.tx_id ORDER BY t.block_timestamp ASC LIMIT 1.",
|
|
67
69
|
"- For actor-level exposure (AC11), UNION FLOWS_TO and SWAPPED reachability over one visible LINKED hop instead of expanding through the LINKED edge itself: MATCH (a:Address {address: $addr})-[:LINKED]-(owned:Address)-[r:FLOWS_TO|SWAPPED]-(b:Address) WHERE NOT a:Pool AND NOT owned:Pool AND owned.address <> b.address AND a.address <> b.address RETURN owned.address, b.address, type(r), coalesce(r.amount_usd_sum, r.bought_usd).",
|
|
68
|
-
"- The risk verdict lives on topology nodes (risk_score float, risk_level string). Labels and per-label risk also live on the address node
|
|
70
|
+
"- The risk verdict lives on topology nodes (risk_score float, risk_level string). An absent risk_score means UNSCORED: the model gave the address no verdict, which is no signal and never low risk. Labels and per-label risk also live on the address node: the labels array plus three parallel lists, label_risk_labels, label_risk_levels and label_risk_updated_timestamps, where entry i of each list is one label row. USE facts serves bounded single-event rows (TRANSFER, SWAP, LIQUIDITY_ADD, LIQUIDITY_REMOVE and BRIDGE_CROSSING edges) only; lifetime address metrics (degrees, totals, activity window) are node properties on USE topology.",
|
|
69
71
|
"- (from:Address)-[t:TRANSFER]->(to:Address) on USE facts returns individual transfer rows, not aggregates, with properties amount, amount_usd, asset_symbol, asset_contract, tx_id, block_height, block_timestamp, event_index, edge_index, price_usd, and price_missing. Every TRANSFER query — row-select or count()/sum() aggregate — requires an indexed predicate: address equality on either endpoint (for example {address: \"...\"} on from or to), a WHERE t.tx_id = \"...\" equality (on EVM networks tx_id is the 0x transaction hash), or a bare WHERE t.block_date = \"YYYY-MM-DD\" bound, which t.block_timestamp bounds in epoch milliseconds may narrow to a time window; a bare LIMIT with no indexed predicate is rejected, since facts_transfers_view is a full transfer-history table, not a small per-address dimension view.",
|
|
70
72
|
"- Facts graph labels include Address; the TRANSFER, SWAP, LIQUIDITY_ADD, LIQUIDITY_REMOVE and BRIDGE_CROSSING relationships each connect two Address nodes. Facts address keys match topology address values exactly.",
|
|
71
73
|
"- Topology relationships include FLOWS_TO, SWAPPED, OPERATED_BY, LINKED, and RISK_PROXIMITY between Address nodes, ADDED_LIQUIDITY and REMOVED_LIQUIDITY to and from Pool nodes, and BRIDGED to and from Chain nodes.",
|
|
72
|
-
"- (:Address)-[:OPERATED_BY]->(:Address) is the directed owner-to-operator edge: the
|
|
73
|
-
"- FLOWS_TO properties are tx_count, amount_usd_sum, first_seen_timestamp, last_seen_timestamp.
|
|
74
|
-
"- Swaps, liquidity and bridges on USE topology are lifetime totals per pair; single events are USE facts rows. (:Address)-[:SWAPPED]->(:Address) joins a swap payer to a different recipient, one edge per payer, recipient, sold_asset and bought_asset, with swap_count, sold_amount_raw, bought_amount_raw, sold_usd, bought_usd, pools, families, strength, first_seen_height and last_seen_height. strength is swap (the whole route is proven: payer, recipient, assets, exact raw amounts and conservation) or swap_like (the shape is a swap but the pool matches no reviewed family). A self swap makes no edge, and a missing SWAPPED edge is not proof that no swap happened.",
|
|
74
|
+
"- (:Address)-[:OPERATED_BY]->(:Address) is the directed owner-to-operator edge: the transaction sender that moved the owner's tokens (ERC-20/721), or the event operator (ERC-1155); not the approved spender. Aggregate properties: tx_count, amount_usd_sum, first_seen_timestamp, last_seen_timestamp, and optional token_standard (ERC20/ERC721/ERC1155; absent when the pair is mixed-standard). One edge per owner/operator pair; direct transfers with no operator create none; an owner never operates for itself. Topology-only, and a topology fact — not a risk label. Probe: MATCH (owner:Address)-[operation:OPERATED_BY]->(operator:Address {address: $addr}) RETURN owner.address AS owner, operation.tx_count AS tx_count ORDER BY operation.tx_count DESC LIMIT 10.",
|
|
75
|
+
"- FLOWS_TO properties are tx_count, amount_usd_sum, first_seen_timestamp, last_seen_timestamp. pair_key and synced_through_height are internal sync bookkeeping, not to be queried. Tx ids, coverage, and averages come from USE facts or inline arithmetic. FLOWS_TO into and out of pools is kept; the pool trace rule below governs a trace that reaches one.",
|
|
76
|
+
"- Swaps, liquidity and bridges on USE topology are lifetime totals per pair; single events are USE facts rows. (:Address)-[:SWAPPED]->(:Address) joins a swap payer to a different recipient, one edge per payer, recipient, sold_asset and bought_asset, with swap_count, sold_amount_raw, bought_amount_raw, sold_usd, bought_usd, pools, families, strength, first_seen_height and last_seen_height. strength is swap (the whole route is proven: payer, recipient, assets, exact raw amounts and conservation) or swap_like (the shape is a swap but the pool matches no reviewed family). A self swap makes no edge, and a missing SWAPPED edge is not proof that no swap happened. Swap attribution is read from SWAPPED, the aggregate (strength, pools, families), or from the USE facts SWAP row, one route. FLOWS_TO carries value only.",
|
|
75
77
|
"- Pool is a second label on an Address: the pool of a swap route or liquidity event, with liquidity_added_usd, liquidity_removed_usd, liquidity_net_usd, provider_count and receiver_count. (:Address)-[:ADDED_LIQUIDITY]->(:Pool) and (:Pool)-[:REMOVED_LIQUIDITY]->(:Address) join named providers and receivers to the pool, with event_count, totals_raw, usd, unpriced_count, first_seen_height and last_seen_height; REMOVED_LIQUIDITY adds fees_raw, receiver_added_usd (USD the receiver itself added to the same pool) and receiver_provided. A receiver's profit from a pool is usd minus receiver_added_usd: receiver_provided false is a drain by a stranger, true with a large profit is the creator's rug pull.",
|
|
76
78
|
"- (:Address)-[:BRIDGED]->(:Chain) is outbound bridge use and (:Chain)-[:BRIDGED]->(:Address) inbound, with kinds, events, totals_raw, first_height, last_height and last_bridge_event_id. A Chain node (network, address) is the remote bridge endpoint, never an Address, so no FLOWS_TO walk passes through it.",
|
|
77
79
|
"- Pool trace rule, for every trace: 1. Enter a `:Pool` on any edge. 2. Leave a `:Pool` only on `REMOVED_LIQUIDITY`, to the address the liquidity was paid to. 3. Never leave a `:Pool` on `FLOWS_TO`. 4. Across a swap, follow `SWAPPED` from payer to recipient, between two different addresses. Do not walk through the pool. Rug-pull probe: MATCH (victim:Address {address: $addr})-[paid:FLOWS_TO]->(pool:Pool)-[removal:REMOVED_LIQUIDITY]->(receiver:Address) WHERE NOT victim:Pool AND receiver.address <> victim.address RETURN pool.address AS pool_address, receiver.address AS receiver_address, removal.usd AS removed_usd, removal.receiver_added_usd AS receiver_added_usd, removal.receiver_provided AS receiver_provided LIMIT 25.",
|
|
78
80
|
"- USE facts serves the single events. (payer:Address)-[s:SWAP]->(recipient:Address) is one row per swap route, self swaps included, with strength, reason, route_id, pools, families and the sold_ and bought_ asset, amount and usd columns. (provider:Address)-[:LIQUIDITY_ADD]->(pool:Address) and (pool:Address)-[:LIQUIDITY_REMOVE]->(receiver:Address) are one row per liquidity event, with party_state and evidence_state. SWAP and LIQUIDITY_* need an address equality on either endpoint or a tx_id equality. (sender:Address)-[c:BRIDGE_CROSSING]->(recipient:Address) is one row per bridge event and needs a bare block_date bound or a tx_id equality. USD comes from the daily price services, never from a swap: with no price, USD is empty and the matching price_missing column is true (price_missing on TRANSFER, sold_price_missing and bought_price_missing on SWAP, amount0_price_missing and amount1_price_missing on LIQUIDITY_*). block_timestamp on TRANSFER, SWAP and LIQUIDITY_* rows is epoch milliseconds, in filters and in results; BRIDGE_CROSSING has none.",
|
|
79
|
-
"- Traversal rule: for BFS, fixed-hop fallback, shortest-path, or manual FLOWS_TO traversal, exchange hot wallets are terminal endpoints only. Do not expand from, through, or classify exchange nodes as deposit, suspect, or intermediate candidates; filter every non-terminal node with is_exchange IS NULL. At a Pool, follow the pool trace rule above.",
|
|
81
|
+
"- Traversal rule: for BFS, fixed-hop fallback, shortest-path, or manual FLOWS_TO traversal, exchange hot wallets are terminal endpoints only. Do not expand from, through, or classify exchange nodes as deposit, suspect, or intermediate candidates; filter every non-terminal node with is_exchange IS NULL. is_exchange is absent unless true, so a labelled node with no is_exchange is walked through, and is_scam, is_victim and is_sanctioned do not end a walk. At a Pool, follow the pool trace rule above.",
|
|
80
82
|
"- Pool guard: a trace walks FLOWS_TO and SWAPPED, so it crosses a swap from payer to recipient without passing through the pool. A walk may end at a Pool, but never starts at one or passes through one: its start and every address in its middle stay off a Pool. A fixed-hop walk adds WHERE NOT src:Pool AND NOT mid:Pool, each its own AND term, never inside an OR. A quantified or shortest-path walk puts the guards inside the path pattern, on the start and on up to 4 guarded hops before one last hop: MATCH p = SHORTEST 1 (a:Address {address: $from} WHERE NOT a:Pool) (()-[:FLOWS_TO|SWAPPED]-(via:Address) WHERE NOT via:Pool){0,4} ()-[:FLOWS_TO|SWAPPED]-(b:Address {address: $to}) RETURN [n IN nodes(p) | n.address] AS route. ANY SHORTEST and ALL SHORTEST take the same pattern. An open target from one address: MATCH SHORTEST 1 (a:Address {address: $addr} WHERE NOT a:Pool) (()-[:FLOWS_TO|SWAPPED]-(via:Address) WHERE NOT via:Pool){0,4} ()-[:FLOWS_TO|SWAPPED]-(b:Address) RETURN b.address LIMIT 50. Use these shapes as written, changing only the addresses and the RETURN. A WHERE placed after a SHORTEST pattern runs after the shortest route is chosen, so it drops a route that crosses a pool instead of finding the route that avoids it.",
|
|
81
83
|
"- Start schema discovery with endpoint-safe property reads: MATCH (n:Address) WHERE n.address IS NOT NULL RETURN n.address AS address, n.network AS network, n.labels AS labels, n.risk_score AS risk_score, n.risk_level AS risk_level LIMIT 20",
|
|
82
84
|
"- Relationship discovery: MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN r.amount_usd_sum AS amount_usd_sum, r.tx_count AS tx_count LIMIT 20",
|
|
@@ -767,7 +769,7 @@ async function createProxy() {
|
|
|
767
769
|
}],
|
|
768
770
|
isError: true
|
|
769
771
|
};
|
|
770
|
-
const { runAmlAddressRisk } = await Promise.resolve().then(() => require("./public-tools-
|
|
772
|
+
const { runAmlAddressRisk } = await Promise.resolve().then(() => require("./public-tools-C6QY_gam.cjs"));
|
|
771
773
|
const result = await runAmlAddressRisk(remoteClient, {
|
|
772
774
|
address,
|
|
773
775
|
network,
|
package/dist/mcp-proxy.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-proxy.d.cts","names":[],"sources":["../src/mcp/proxy.ts"],"mappings":";;YA+BY;wBAEI,oBAAoB,MAAK,OAAO,aAA2B;KA2BtE,iBAAiB,eAAe,EAAE;
|
|
1
|
+
{"version":3,"file":"mcp-proxy.d.cts","names":[],"sources":["../src/mcp/proxy.ts"],"mappings":";;YA+BY;wBAEI,oBAAoB,MAAK,OAAO,aAA2B;KA2BtE,iBAAiB,eAAe,EAAE;wBAsEvB,2BAA2B,mBAAmB;;;;;;;;wBAwlBxC,eAAe"}
|
package/dist/mcp-proxy.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-proxy.d.mts","names":[],"sources":["../src/mcp/proxy.ts"],"mappings":";;YA+BY;wBAEI,oBAAoB,MAAK,OAAO,aAA2B;KA2BtE,iBAAiB,eAAe,EAAE;
|
|
1
|
+
{"version":3,"file":"mcp-proxy.d.mts","names":[],"sources":["../src/mcp/proxy.ts"],"mappings":";;YA+BY;wBAEI,oBAAoB,MAAK,OAAO,aAA2B;KA2BtE,iBAAiB,eAAe,EAAE;wBAsEvB,2BAA2B,mBAAmB;;;;;;;;wBAwlBxC,eAAe"}
|
package/dist/mcp-proxy.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { t as PaymentRequiredError } from "./client-CHYskPnG.mjs";
|
|
|
3
3
|
import { n as PUBLIC_MCP_TOOL_ALLOWED_ARGS, r as PUBLIC_MCP_TOOL_REQUIRED_ARGS, t as HIDDEN_REMOTE_TOOL_NAMES } from "./tool-visibility-CJV9HezW.mjs";
|
|
4
4
|
import { t as primitiveBackendUsageStatus } from "./usage-status-CHiOPA-2.mjs";
|
|
5
5
|
import { n as unavailableSubscriptionStatus } from "./subscription-status-MyeqOmbA.mjs";
|
|
6
|
-
import { n as mirrorGraphNetworkCapabilities } from "./capabilities-
|
|
6
|
+
import { n as mirrorGraphNetworkCapabilities } from "./capabilities-C8DQSfyW.mjs";
|
|
7
7
|
import { r as appendActionLog, t as actionLogSignalsFromResult } from "./action-log-diTHyMbV.mjs";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { appendFile, mkdir } from "node:fs/promises";
|
|
@@ -57,22 +57,24 @@ const CHAIN_INSIGHTS_WORKFLOW = [
|
|
|
57
57
|
const GRAPH_SCHEMA_HINTS = [
|
|
58
58
|
"Graph query hints:",
|
|
59
59
|
"- Call meta_network_capabilities first. Pass network= exactly as GraphRAG advertised it. CIA does not pick a default network.",
|
|
60
|
+
"- Coverage is per kind of data. coverage.complete_through_block is the floor of USE facts: below it every raw relationship is indexed. graph_progress.complete_through_block is the floor of USE topology, and graph_progress.layers gives each link kind its own height. An empty answer above the floor may mean not indexed yet, not absent on chain. An absent chain_tip_block means the indexers are catching up: read freshness.max_data_age_seconds for the lag.",
|
|
60
61
|
"- The graph is address-grain. The only topology money node label is Address, keyed by the raw chain-native H160 address on EVM networks, for example 0x1874a43d7c6d888f9eda3d22a3a49704e3cadb24. The network value on Address nodes matches the tool argument. There is no separate identity key.",
|
|
61
|
-
"- Address nodes carry address, network, labels, and is_exchange. (:Address)-[:LINKED]-(:Address) is an undirected ownership-overlay edge (basis derived/associated, plus confidence, source_event, declared_owner) asserting the two addresses are controlled by the same actor. LINKED is served on the topology graph only. Enumerate LINKED neighbors with MATCH (a:Address {address: $addr})-[l:LINKED]-(b:Address) RETURN b.address, b.network, l.basis, l.confidence.",
|
|
62
|
+
"- Address nodes carry address, network, labels, and the role flags is_exchange, is_scam, is_victim and is_sanctioned. (:Address)-[:LINKED]-(:Address) is an undirected ownership-overlay edge (basis derived/associated, plus confidence, source_event, declared_owner) asserting the two addresses are controlled by the same actor. LINKED is served on the topology graph only. Enumerate LINKED neighbors with MATCH (a:Address {address: $addr})-[l:LINKED]-(b:Address) RETURN b.address, b.network, l.basis, l.confidence.",
|
|
63
|
+
"- Labels hold role words, never detector names: Exchange (plus the exchange name), Scam, Victim and Sanctioned (plus the entity name). Each role is also a node label and a flag on the Address: :Exchange and is_exchange follow the exchange label, :Scam and is_scam the risk label, :Victim and is_victim the protection label, :Sanctioned and is_sanctioned the sanctioned label. Each flag is present only when true and absent otherwise, never false: test IS NOT NULL or IS NULL, not = false. A withdrawn label removes its node label and its flag.",
|
|
62
64
|
"- Address nodes also carry a risk verdict (risk_score float, risk_level string) plus base activity rollups: degree_in/degree_out/degree_total (distinct counterparty addresses), tx_in_count/tx_out_count/tx_total_count, total_in_usd/total_out_usd/total_volume_usd, net_flow_usd (in minus out; positive = net receiver) — all computed from external flows only — and first_activity_timestamp/last_activity_timestamp/activity_span_days, which include all flows (self-loops included). FLOWS_TO edges carry exactly tx_count, amount_usd_sum (total money flow, token and native value merged), first_seen_timestamp, last_seen_timestamp. Lifetime aggregates are the only serving window. Averages are computed inline (amount_usd_sum / toFloat(tx_count)); transaction anchors resolve through USE facts: MATCH (a:Address {address: $from})-[t:TRANSFER]->(b:Address {address: $to}) RETURN t.tx_id ORDER BY t.block_timestamp ASC LIMIT 1.",
|
|
63
65
|
"- For actor-level exposure (AC11), UNION FLOWS_TO and SWAPPED reachability over one visible LINKED hop instead of expanding through the LINKED edge itself: MATCH (a:Address {address: $addr})-[:LINKED]-(owned:Address)-[r:FLOWS_TO|SWAPPED]-(b:Address) WHERE NOT a:Pool AND NOT owned:Pool AND owned.address <> b.address AND a.address <> b.address RETURN owned.address, b.address, type(r), coalesce(r.amount_usd_sum, r.bought_usd).",
|
|
64
|
-
"- The risk verdict lives on topology nodes (risk_score float, risk_level string). Labels and per-label risk also live on the address node
|
|
66
|
+
"- The risk verdict lives on topology nodes (risk_score float, risk_level string). An absent risk_score means UNSCORED: the model gave the address no verdict, which is no signal and never low risk. Labels and per-label risk also live on the address node: the labels array plus three parallel lists, label_risk_labels, label_risk_levels and label_risk_updated_timestamps, where entry i of each list is one label row. USE facts serves bounded single-event rows (TRANSFER, SWAP, LIQUIDITY_ADD, LIQUIDITY_REMOVE and BRIDGE_CROSSING edges) only; lifetime address metrics (degrees, totals, activity window) are node properties on USE topology.",
|
|
65
67
|
"- (from:Address)-[t:TRANSFER]->(to:Address) on USE facts returns individual transfer rows, not aggregates, with properties amount, amount_usd, asset_symbol, asset_contract, tx_id, block_height, block_timestamp, event_index, edge_index, price_usd, and price_missing. Every TRANSFER query — row-select or count()/sum() aggregate — requires an indexed predicate: address equality on either endpoint (for example {address: \"...\"} on from or to), a WHERE t.tx_id = \"...\" equality (on EVM networks tx_id is the 0x transaction hash), or a bare WHERE t.block_date = \"YYYY-MM-DD\" bound, which t.block_timestamp bounds in epoch milliseconds may narrow to a time window; a bare LIMIT with no indexed predicate is rejected, since facts_transfers_view is a full transfer-history table, not a small per-address dimension view.",
|
|
66
68
|
"- Facts graph labels include Address; the TRANSFER, SWAP, LIQUIDITY_ADD, LIQUIDITY_REMOVE and BRIDGE_CROSSING relationships each connect two Address nodes. Facts address keys match topology address values exactly.",
|
|
67
69
|
"- Topology relationships include FLOWS_TO, SWAPPED, OPERATED_BY, LINKED, and RISK_PROXIMITY between Address nodes, ADDED_LIQUIDITY and REMOVED_LIQUIDITY to and from Pool nodes, and BRIDGED to and from Chain nodes.",
|
|
68
|
-
"- (:Address)-[:OPERATED_BY]->(:Address) is the directed owner-to-operator edge: the
|
|
69
|
-
"- FLOWS_TO properties are tx_count, amount_usd_sum, first_seen_timestamp, last_seen_timestamp.
|
|
70
|
-
"- Swaps, liquidity and bridges on USE topology are lifetime totals per pair; single events are USE facts rows. (:Address)-[:SWAPPED]->(:Address) joins a swap payer to a different recipient, one edge per payer, recipient, sold_asset and bought_asset, with swap_count, sold_amount_raw, bought_amount_raw, sold_usd, bought_usd, pools, families, strength, first_seen_height and last_seen_height. strength is swap (the whole route is proven: payer, recipient, assets, exact raw amounts and conservation) or swap_like (the shape is a swap but the pool matches no reviewed family). A self swap makes no edge, and a missing SWAPPED edge is not proof that no swap happened.",
|
|
70
|
+
"- (:Address)-[:OPERATED_BY]->(:Address) is the directed owner-to-operator edge: the transaction sender that moved the owner's tokens (ERC-20/721), or the event operator (ERC-1155); not the approved spender. Aggregate properties: tx_count, amount_usd_sum, first_seen_timestamp, last_seen_timestamp, and optional token_standard (ERC20/ERC721/ERC1155; absent when the pair is mixed-standard). One edge per owner/operator pair; direct transfers with no operator create none; an owner never operates for itself. Topology-only, and a topology fact — not a risk label. Probe: MATCH (owner:Address)-[operation:OPERATED_BY]->(operator:Address {address: $addr}) RETURN owner.address AS owner, operation.tx_count AS tx_count ORDER BY operation.tx_count DESC LIMIT 10.",
|
|
71
|
+
"- FLOWS_TO properties are tx_count, amount_usd_sum, first_seen_timestamp, last_seen_timestamp. pair_key and synced_through_height are internal sync bookkeeping, not to be queried. Tx ids, coverage, and averages come from USE facts or inline arithmetic. FLOWS_TO into and out of pools is kept; the pool trace rule below governs a trace that reaches one.",
|
|
72
|
+
"- Swaps, liquidity and bridges on USE topology are lifetime totals per pair; single events are USE facts rows. (:Address)-[:SWAPPED]->(:Address) joins a swap payer to a different recipient, one edge per payer, recipient, sold_asset and bought_asset, with swap_count, sold_amount_raw, bought_amount_raw, sold_usd, bought_usd, pools, families, strength, first_seen_height and last_seen_height. strength is swap (the whole route is proven: payer, recipient, assets, exact raw amounts and conservation) or swap_like (the shape is a swap but the pool matches no reviewed family). A self swap makes no edge, and a missing SWAPPED edge is not proof that no swap happened. Swap attribution is read from SWAPPED, the aggregate (strength, pools, families), or from the USE facts SWAP row, one route. FLOWS_TO carries value only.",
|
|
71
73
|
"- Pool is a second label on an Address: the pool of a swap route or liquidity event, with liquidity_added_usd, liquidity_removed_usd, liquidity_net_usd, provider_count and receiver_count. (:Address)-[:ADDED_LIQUIDITY]->(:Pool) and (:Pool)-[:REMOVED_LIQUIDITY]->(:Address) join named providers and receivers to the pool, with event_count, totals_raw, usd, unpriced_count, first_seen_height and last_seen_height; REMOVED_LIQUIDITY adds fees_raw, receiver_added_usd (USD the receiver itself added to the same pool) and receiver_provided. A receiver's profit from a pool is usd minus receiver_added_usd: receiver_provided false is a drain by a stranger, true with a large profit is the creator's rug pull.",
|
|
72
74
|
"- (:Address)-[:BRIDGED]->(:Chain) is outbound bridge use and (:Chain)-[:BRIDGED]->(:Address) inbound, with kinds, events, totals_raw, first_height, last_height and last_bridge_event_id. A Chain node (network, address) is the remote bridge endpoint, never an Address, so no FLOWS_TO walk passes through it.",
|
|
73
75
|
"- Pool trace rule, for every trace: 1. Enter a `:Pool` on any edge. 2. Leave a `:Pool` only on `REMOVED_LIQUIDITY`, to the address the liquidity was paid to. 3. Never leave a `:Pool` on `FLOWS_TO`. 4. Across a swap, follow `SWAPPED` from payer to recipient, between two different addresses. Do not walk through the pool. Rug-pull probe: MATCH (victim:Address {address: $addr})-[paid:FLOWS_TO]->(pool:Pool)-[removal:REMOVED_LIQUIDITY]->(receiver:Address) WHERE NOT victim:Pool AND receiver.address <> victim.address RETURN pool.address AS pool_address, receiver.address AS receiver_address, removal.usd AS removed_usd, removal.receiver_added_usd AS receiver_added_usd, removal.receiver_provided AS receiver_provided LIMIT 25.",
|
|
74
76
|
"- USE facts serves the single events. (payer:Address)-[s:SWAP]->(recipient:Address) is one row per swap route, self swaps included, with strength, reason, route_id, pools, families and the sold_ and bought_ asset, amount and usd columns. (provider:Address)-[:LIQUIDITY_ADD]->(pool:Address) and (pool:Address)-[:LIQUIDITY_REMOVE]->(receiver:Address) are one row per liquidity event, with party_state and evidence_state. SWAP and LIQUIDITY_* need an address equality on either endpoint or a tx_id equality. (sender:Address)-[c:BRIDGE_CROSSING]->(recipient:Address) is one row per bridge event and needs a bare block_date bound or a tx_id equality. USD comes from the daily price services, never from a swap: with no price, USD is empty and the matching price_missing column is true (price_missing on TRANSFER, sold_price_missing and bought_price_missing on SWAP, amount0_price_missing and amount1_price_missing on LIQUIDITY_*). block_timestamp on TRANSFER, SWAP and LIQUIDITY_* rows is epoch milliseconds, in filters and in results; BRIDGE_CROSSING has none.",
|
|
75
|
-
"- Traversal rule: for BFS, fixed-hop fallback, shortest-path, or manual FLOWS_TO traversal, exchange hot wallets are terminal endpoints only. Do not expand from, through, or classify exchange nodes as deposit, suspect, or intermediate candidates; filter every non-terminal node with is_exchange IS NULL. At a Pool, follow the pool trace rule above.",
|
|
77
|
+
"- Traversal rule: for BFS, fixed-hop fallback, shortest-path, or manual FLOWS_TO traversal, exchange hot wallets are terminal endpoints only. Do not expand from, through, or classify exchange nodes as deposit, suspect, or intermediate candidates; filter every non-terminal node with is_exchange IS NULL. is_exchange is absent unless true, so a labelled node with no is_exchange is walked through, and is_scam, is_victim and is_sanctioned do not end a walk. At a Pool, follow the pool trace rule above.",
|
|
76
78
|
"- Pool guard: a trace walks FLOWS_TO and SWAPPED, so it crosses a swap from payer to recipient without passing through the pool. A walk may end at a Pool, but never starts at one or passes through one: its start and every address in its middle stay off a Pool. A fixed-hop walk adds WHERE NOT src:Pool AND NOT mid:Pool, each its own AND term, never inside an OR. A quantified or shortest-path walk puts the guards inside the path pattern, on the start and on up to 4 guarded hops before one last hop: MATCH p = SHORTEST 1 (a:Address {address: $from} WHERE NOT a:Pool) (()-[:FLOWS_TO|SWAPPED]-(via:Address) WHERE NOT via:Pool){0,4} ()-[:FLOWS_TO|SWAPPED]-(b:Address {address: $to}) RETURN [n IN nodes(p) | n.address] AS route. ANY SHORTEST and ALL SHORTEST take the same pattern. An open target from one address: MATCH SHORTEST 1 (a:Address {address: $addr} WHERE NOT a:Pool) (()-[:FLOWS_TO|SWAPPED]-(via:Address) WHERE NOT via:Pool){0,4} ()-[:FLOWS_TO|SWAPPED]-(b:Address) RETURN b.address LIMIT 50. Use these shapes as written, changing only the addresses and the RETURN. A WHERE placed after a SHORTEST pattern runs after the shortest route is chosen, so it drops a route that crosses a pool instead of finding the route that avoids it.",
|
|
77
79
|
"- Start schema discovery with endpoint-safe property reads: MATCH (n:Address) WHERE n.address IS NOT NULL RETURN n.address AS address, n.network AS network, n.labels AS labels, n.risk_score AS risk_score, n.risk_level AS risk_level LIMIT 20",
|
|
78
80
|
"- Relationship discovery: MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN r.amount_usd_sum AS amount_usd_sum, r.tx_count AS tx_count LIMIT 20",
|
|
@@ -763,7 +765,7 @@ async function createProxy() {
|
|
|
763
765
|
}],
|
|
764
766
|
isError: true
|
|
765
767
|
};
|
|
766
|
-
const { runAmlAddressRisk } = await import("./public-tools-
|
|
768
|
+
const { runAmlAddressRisk } = await import("./public-tools-rKIzmn6f.mjs");
|
|
767
769
|
const result = await runAmlAddressRisk(remoteClient, {
|
|
768
770
|
address,
|
|
769
771
|
network,
|