chain-insights 0.56.11 → 0.56.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chain-insights",
3
- "version": "0.56.11",
3
+ "version": "0.56.13",
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": {
@@ -24,8 +24,12 @@ The one case without the tool is at the end, Shell without the tool.
24
24
  time. A repeat mention is written in full too: `0xd4a9…` as a
25
25
  back-reference to an address named earlier is wrong, in a long answer most
26
26
  of all. When the user asks for shortened addresses "for readability",
27
- still write them in full and say the full form is the rule, because a
28
- shortened address cannot be copied or checked. When a table column or a
27
+ give them names instead: list each name with its full address once at the
28
+ top (Wallet A: 0x…, the vault: 0x…), then use the names in the sentences
29
+ and the table. The addresses stay in full, because a shortened address
30
+ cannot be copied or checked, and the user gets the readable answer they
31
+ asked for. Codex once shortened every address for the rest of a
32
+ conversation after such a request: that is wrong. When a table column or a
29
33
  row needs a short label, name the thing instead (Pair A, Wallet Y, the
30
34
  vault) and list each name with its full address just above the table:
31
35
  `To 0x00862568… pair` as a column header is wrong. When the user types a shortened address, take it as the one full
@@ -111,6 +115,7 @@ Find the question in this table. Send its first query, once, with the addresses
111
115
  | Biggest senders or receivers of one address | `topology` | Newest links of one address, `ORDER BY f.amount_usd_sum DESC LIMIT 10` |
112
116
  | How many, how much, first or last active, for one address | `topology` | Profile of one address |
113
117
  | The transfers of a pair on one day | `facts` | `TRANSFER` of the pair and the day, see Facts |
118
+ | What one address did in a month, since a date, in the last N days | topology, then facts | Links bounded by first and last seen, day reads only for the links that straddle an edge, see A window of days for one address |
114
119
  | What happened in this transaction hash? | `chain` | `Transaction {tx_id}`, see Chain |
115
120
  | The latest block, block N, how far behind the graph is | `chain` | `Head`, or `Block {block_height}` |
116
121
  | What fields does an Address have? | none | Call `meta_schema {network}`. Send no query. |
@@ -269,6 +274,19 @@ MATCH (a:Address {address: "0x0168d9f5cac7d63eb75095cef316d68b0dc42bc6"})-[s:SWA
269
274
  RETURN b.address AS recipient, s.sold_usd AS sold_usd, s.bought_usd AS bought_usd, s.pools AS pools ORDER BY s.swap_count DESC LIMIT 25
270
275
  ```
271
276
 
277
+ What a pool is, and who trades through it. A pool is the `:Pool` node label,
278
+ read with `labels(p)`; the `labels` property is a different thing (named
279
+ labels such as a vault), and a pool usually has none. The traders are the
280
+ senders into the pool on `FLOWS_TO`, ranked by `tx_count`: one read, no swap
281
+ search. A swap check is `SWAPPED` anchored on one trader, never on a list of
282
+ traders and never on the pool, because both time out.
283
+
284
+ ```cypher
285
+ USE topology
286
+ MATCH (b:Address)-[f:FLOWS_TO]->(p:Address {address: "0xd42a491087a15e5afd51feb3606066cc152d2b09"}) WHERE NOT b:Pool
287
+ RETURN b.address AS from_address, p.address AS to_address, labels(p) AS to_node_labels, f.tx_count AS tx_count, f.amount_usd_sum AS amount_usd_sum, f.first_seen_timestamp AS first_seen_timestamp, f.last_seen_timestamp AS last_seen_timestamp, b.labels AS from_labels, p.labels AS to_labels, b.is_exchange AS from_is_exchange, p.is_exchange AS to_is_exchange ORDER BY f.tx_count DESC LIMIT 25
288
+ ```
289
+
272
290
  Pool trace: who took liquidity out of the pools an address paid into.
273
291
 
274
292
  ```cypher
@@ -330,7 +348,7 @@ The server serves five read-only catalog calls: `CALL db.labels()`, `CALL db.rel
330
348
  - A facts read aggregates with `count()` and `sum()` only, over the whole page: no grouping key, no `min()`, `max()` or `avg()` (`unsupported facts query shape`). Read the rows and compute the rest yourself.
331
349
  - Every row and both endpoints carry `network`, the query's `network`. A filter on it is no anchor: it never replaces the pair and the day.
332
350
  - Facts serves no kind. A read that names `:Account` or `:Contract` is refused with `facts_no_anchor`: ask `USE topology` for the kind, or `USE chain` for `is_contract` and `nonce`.
333
- - A facts read covers one day. For more days, send one read for each day.
351
+ - A facts read covers one day. For more days, send one read for each day, and for a window of days around one address pick the pairs first (A window of days for one address, below): never every pair on every day.
334
352
  - Amounts and USD values come back as text, such as `"5.23661492"`. Convert them before you add them.
335
353
  - `TRANSFER` columns: `tx_id`, `block_date`, `block_height`, `block_timestamp`, `event_index`, `edge_index`, `kind`, `asset_contract`, `asset_symbol`, `amount`, `amount_usd`, `price_usd`, `price_missing`, `token_id`, `token_standard`, `operator_address`, `raw_amount` and `decimals`. `kind` is `token`, `native` or `internal`: an `internal` row is ETH a contract sends while it runs a call.
336
354
 
@@ -350,6 +368,40 @@ pair moved on one day, read its `TRANSFER` rows for that day, then:
350
368
  3. If a row's `price_missing` is true, show its amount and say no price exists for that asset. Never guess a USD value.
351
369
  4. Say "tokens worth about X USD on DAY", because the row moved tokens.
352
370
 
371
+ ### A window of days for one address
372
+
373
+ "What did this address do in September", "everything it received since
374
+ August 1", "only the last 7 days": never read every pair on every day. One
375
+ answer once took 72 reads and 189 seconds. Three steps, in this order:
376
+
377
+ 1. One topology read, both directions, bounded by the window: a link is in
378
+ the window when `f.first_seen_timestamp <= <window end ms>` and
379
+ `f.last_seen_timestamp >= <window start ms>`. Return `from_address`,
380
+ `to_address`, `tx_count`, `amount_usd_sum`, `first_seen_timestamp` and
381
+ `last_seen_timestamp`. This is the list of counterparties active in the
382
+ window.
383
+ 2. A link whose first and last seen both fall inside the window needs no
384
+ facts read: its lifetime `tx_count` and `amount_usd_sum` are the window
385
+ totals. Only a link that straddles a window edge needs the per-day
386
+ totals: for that pair, one `graph_query_batch` of one `count()` and
387
+ `sum()` query per day, up to 20 days per call, over the days between the
388
+ later of its first seen and the window start, and the earlier of its last
389
+ seen and the window end.
390
+ 3. Answer with the totals per counterparty, and say which are exact and
391
+ which came from the day reads. Read rows only for the days the user asks
392
+ about. When more than ten links straddle an edge, give the window picture
393
+ from the link totals, name the straddling pairs as estimates, and offer
394
+ the exact read for one of them.
395
+
396
+ The outgoing side, for September 2026; the incoming side is the same read
397
+ with the arrow reversed, and both go in one `graph_query_batch`:
398
+
399
+ ```cypher
400
+ USE topology
401
+ MATCH (a:Address {address: "0x9f73873740c799d74741ad2d15cee7fb33572e4a"})-[f:FLOWS_TO]->(b:Address) WHERE NOT a:Pool AND f.first_seen_timestamp <= 1790812799000 AND f.last_seen_timestamp >= 1788220800000
402
+ RETURN a.address AS from_address, b.address AS to_address, f.amount_usd_sum AS amount_usd_sum, f.tx_count AS tx_count, f.first_seen_timestamp AS first_seen_timestamp, f.last_seen_timestamp AS last_seen_timestamp, a.labels AS from_labels, b.labels AS to_labels, a.is_exchange AS from_is_exchange, b.is_exchange AS to_is_exchange ORDER BY f.last_seen_timestamp DESC LIMIT 50
403
+ ```
404
+
353
405
  ## Chain: one key, one node
354
406
 
355
407
  `USE chain` asks the chain node for one known thing: `Transaction` by `tx_id`, `Block` by `block_height` or `block_hash` (exactly one), `Address` by `address` (with an optional `at_block`), or `Head`. One node, literal keys in braces, a `RETURN` of `var.property` items, no `WHERE`, no range. `RETURN t` of the whole node is refused (`chain_not_a_lookup`), and a property outside the node's list is refused (`chain_not_served`): a `Transaction` has `tx_id`, `status`, `block_height`, `block_hash`, `tx_index`, `block_timestamp`, `block_date`, `from_address`, `to_address`, `created_contract`, `value`, `nonce`, `gas_used`, `effective_gas_price`, `log_count` and `tx_type`, and no `type`. Read its limits in `chain_admission` (`cia network robinhood --json`).