CoinMarketCap APIkeyless0 credits2026-09-18

LP removed: $21,330,275 out of UNI/WBTC. 99.8% of it was in UNI/WETH 2 min 12 s later. Same wallet.

Where the pulled liquidity went.

Two liquidity rows of UNI by the same wallet: the red bar is the removal from UNI/WBTC, the amber bar the add into UNI/WETH 2 min 12 s later; the hairpin is the wallet's trail from one to the other.
remove · Ring Exchange (Ethereum) · UNI/WBTC · −$21,330,275 · 2026-09-02 03:59:11 UTC — bar length is the row's |tu|add · Ring Exchange (Ethereum) · UNI/WETH · +$21,287,255 · 2 min 12 s later · the same maker 0x4f0aa5…8bb9a9 · the same 1,962,475.5391248302 UNI
UNI · ethereum · 2 rows from one maker= call · 5 chains followed · 11 calls · 73.0 s captured 2026-09-18 23:16:31 UTC · reproduce ↓
The product

One removal. The wallet followed. The alert rewritten.

A removal ≥ $100k fires. A same-transaction add makes it just-in-time liquidity, refused. Otherwise the maker is followed across every pool of the token and every EVM chain the asset trades on, and the verdict rewrites the alert's severity. Switch receipts below — every one is a committed keyless run, no request.

UNI · ethereum · 0x1f9840…01f984 · rule: argmax |tu| over non-JIT removals >= $100,000 across the 11-token watchlist, last 300 rows per token, at capture time · captured 2026-09-18 23:16:31 UTC · 11 calls · 0 credits · keyless

MIGRATION · severity rewritten red → amber

−$21,330,275 out of Ring Exchange (Ethereum) · UNI/WBTC by 0x4f0aa5…8bb9a9, 2026-09-02 03:59:11 UTC · share of pool unknown. +$21,287,255 into Ring Exchange (Ethereum) · UNI/WETH 2 min 12 s later · the pool now holds $102,239,395

chain rows by this wallet, ±6 h found pages follow
ethereum0x1f9840…01f984◀ found here2the add: +$21,287,255 into Ring Exchange (Ethereum) · UNI/WETH1200 · complete
bsc0xbf5140…2ce9b10nothing by this wallet1200 · complete
arbitrum0xfa7f89…f1f7f00nothing by this wallet1200 · complete
unichain0x8f187a…e9ea210nothing by this wallet1200 · complete
polygon0xb33eaa…b5180f0nothing by this wallet1200 · complete
Solanarefused · 8FU95xFJ…Ceab36 is not an EVM address — a maker there cannot be this wallet
Gnosisrefused · 0x4537e3…af9d74 holds $239 of DEX liquidity — below the $10,000 floor, not followed
HECOrefused · 0x22c54c…5c46e6 holds $0 of DEX liquidity — below the $10,000 floor, not followed
Avalancherefused · 0x8ebaf2…ba8580 holds $214 of DEX liquidity — below the $10,000 floor, not followed
▶ MIGRATION · severity rewritten red → amber · 99.8% recovered into Ring Exchange (Ethereum) · UNI/WETH, 2 min 12 s later
rule: ≥ 70 % re-added elsewhere → Migration · ≥ 70 % same pool → Rebalance · 10–70 % → Partial · nothing, every follow 200 → Exit · a throttled follow → Incomplete, never Exit · window ±6 h · JIT pairs discarded first

The two rows, verbatim — one maker= call

maker 0x4f0aa5900b8292273b2f9a178d5468f8048bb9a9 · endpoint /v1/dex/liquidity-change/list?maker= · sha256 5bf63e4f3fed28fc… · the whole receipt (opens in a new tab)
sideutcpooltu (usd)a0a1txn
remove2026-09-02 03:59:11 UTCRing Exchange (Ethereum) · UNI/WBTC-21,330,274.56-1,962,475.5391 UNI-137.9592 WBTC0x5081d9…dcd495
add2026-09-02 04:01:23 UTCRing Exchange (Ethereum) · UNI/WETH [ethereum]21,287,254.931,962,475.5391 UNI4,402.6945 WETH0x9fafe3…71a0f3
  • tu_add 21,287,254.934237 ÷ tu_remove 21,330,274.564875 = 0.9980 → 99.8% recovered
  • ts_add 1788321683000 − ts_remove 1788321551000 = 132,000 ms → 2 min 12 s later
  • a0 -1,962,475.5391248302 UNI out · +1,962,475.5391248302 UNI in → identical to ten decimals
  • same m on both rows · UNI/WBTC → UNI/WETH · both rows from one keyless call → MIGRATION
raw JSON of these rows, verbatim from the API
[
 {
  "a0": -1962475.5391248302,
  "a1": -137.95919726,
  "eid": 32396,
  "en": "Ring Exchange (Ethereum)",
  "f": "0xeb2a625b704d73e82946d8d026e1f588eed06416",
  "h": "25887070",
  "lgid": "494",
  "m": "0x4f0aa5900b8292273b2f9a178d5468f8048bb9a9",
  "t0a": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
  "t0s": "UNI",
  "t1a": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
  "t1s": "WBTC",
  "tp": "remove",
  "ts": "1788321551000",
  "tu": -21330274.564875204,
  "txId": "178",
  "txn": "0x5081d91ab6eae50971e0dec5b5f2b03561893a638768ec4773197622dddcd495"
 },
 {
  "a0": 1962475.5391248302,
  "a1": 4402.694523200528,
  "eid": 32396,
  "en": "Ring Exchange (Ethereum)",
  "f": "0xeb2a625b704d73e82946d8d026e1f588eed06416",
  "h": "25887081",
  "lgid": "527",
  "m": "0x4f0aa5900b8292273b2f9a178d5468f8048bb9a9",
  "t0a": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
  "t0s": "UNI",
  "t1a": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
  "t1s": "WETH",
  "tp": "add",
  "ts": "1788321683000",
  "tu": 21287254.934237212,
  "txId": "101",
  "txn": "0x9fafe3ab124a818f9908a4ef5b67103ce97d8aea3734bbf18d6f7938c871a0f3"
 }
]

Paste any token — the same engine, live, keyless

The largest removal in the token's newest 100 rows, followed on this chain through a keyless proxy (CoinMarketCap sends no CORS header, so the browser cannot call it directly). The proxy shares one IP across every visitor against a per-IP anonymous limit, so a 429 is shown as a 429, with the command to run instead — the CLI follows 300 rows and every sibling chain, on a quota of your own.

committed run
The census

67% of “LP pulled” alerts were the same wallet putting it back within 6 h.

Following one removal proves the mechanism. Following 335 measures how often the alert is the wrong headline — every non-JIT removal ≥ $100,000 across an 11-token, 3-chain watchlist, each wallet followed one at a time on its own chain. Every row, with its wallet, class and destination, is in the receipt.

335removals ≥ $100,000, 11 tokens
194rebalance — 57.9% back into the same pool
19migration — 5.7% into a different pool
10partial — 3.0%, 10–70 % came back
112no re-add — 33.4%, an upper bound on exits

The 112 found nothing re-added on the same chain within 6 h — an upper bound on true exits, because the census does not follow sibling chains (the product does, and says which method produced each number). Method: same-chain maker follow (liquidity-change/list?maker=, cursor walked to the window start), JIT excluded, published thresholds; cross-chain NOT followed here, so EXIT is an upper bound. 352 keyless calls, 0 credits, captured 2026-09-18 22:40:13 UTC · base_rate.json (opens in a new tab)

The receipt

Every number on this page came from a call you can replay.

Each request behind the verdict — endpoint, parameters, HTTP status, milliseconds, credit count, sha256 of the bytes received — is in the fold below, and every response is stored verbatim under that hash in the receipt. The block follows the verdict: switch a receipt or paste a token and it updates.

endpoint
/v1/dex/liquidity-change/list?maker= — the join
calls
11 · HTTP 200 · 5 chains followed
credits used
0 — none — CoinMarketCap keyless /public-api surface
captured
2026-09-18T23:16:31Z · 73.0 s wall clock
join call sha256
5bf63e4f3fed28fcbc93f205f1741c71469cbac6a2efa32c5bebf5f2c3eeae89
join request
re-derive
python3 scripts/verify.py · hero.json (opens in a new tab) · every call ↓
every call behind this verdict · 11 · endpoint · params · status · ms · credit_count · sha256
#endpointparamsstatusmscredit_countsha256 of the bytes received
1GET /v1/dex/tokenaddress=0x1f9840…01f984 platform=ethereum2004561 · 0 billed42305d3a63dcdb2de4a5badcec219a5fc3315e354b6fbb451308fc151dfe6054
2GET /v1/dex/liquidity-change/listaddress=0x1f9840…01f984 maker=0x4f0aa5…8bb9a9 platform=ethereum2005771 · 0 billed5bf63e4f3fed28fcbc93f205f1741c71469cbac6a2efa32c5bebf5f2c3eeae89
3GET /v1/dex/token/poolsaddress=0x1f9840…01f984 platform=ethereum size=202004851 · 0 billed3ead9d29cb7fe987d76ea0da897b883cf178fea8b2fd82fbe46889071f115955
4GET /v1/dex/searchq=0x1f9840…01f98420011851 · 0 billed84b9b2940b80bd415628476934097cf5326665d3eb4489920db2e841853463f3
5GET /v1/dex/searchq=UNI200 · 3 attempts4581 · 0 billed4ef369fd8d554aab14c2634626ee2ecf7d864f887bddbfa497a854cd4edf7415
6GET /v2/cryptocurrency/infoid=70832003551 · 0 billed06d1aa87ed66526bc133be9939cdbe4da38837683d2c9ff0f8f3100fb1366527
7GET /v1/dex/liquidity-change/listaddress=0xbf5140…2ce9b1 maker=0x4f0aa5…8bb9a9 platform=bsc20012131 · 0 billedb0f83c31998c54d5c53a67d16a2d56a82f83c2bf018e5d7d3f79ea952810de50
8GET /v1/dex/liquidity-change/listaddress=0xfa7f89…f1f7f0 maker=0x4f0aa5…8bb9a9 platform=arbitrum2005351 · 0 billed67b651417d1b6b3a5adf4e40d375d43c5034360cf977ee4444af567ed7c64d56
9GET /v1/dex/liquidity-change/listaddress=0x8f187a…e9ea21 maker=0x4f0aa5…8bb9a9 platform=unichain2006121 · 0 billedcf6b4f53059f1991f483ea48c57e6d924f6c9790506e73c5973d1f20ab883f53
10GET /v1/dex/liquidity-change/listaddress=0xb33eaa…b5180f maker=0x4f0aa5…8bb9a9 platform=polygon2005081 · 0 billed5be7b3d74569d601f12ed9f99e930c1b30ba959468f8fa1792748fa82a65e97a
11GET /v4/dex/pairs/quotes/latestcontract_address=0x8626be…4a7306 network_slug=ethereum2008851 · 0 billed9dffddafeedae6dcdc469215140cc1937c1311662b4e1649f32df2b6369fffaa
The API

6 keyless endpoints. One parameter is the whole join.

Per liquidity event, keyless, CoinMarketCap publishes the maker's own wallet — and /v1/dex/liquidity-change/list accepts maker= as a server-side filter: one call returns that wallet's adds and removes across every pool of the token. The other 5 endpoints supply the pools, the cross-chain identity, the depth and the card's header.

endpointused forkeycalls behind this page
/public-api/v1/dex/liquidity-change/listthe trigger (removals ≥ $100k) and the join: maker= returns one wallet's adds and removes across every pool of the tokennone45
/public-api/v1/dex/token/poolsevery pool of the token: identity, liquidity now (share-of-pool), pool address, creation time (Consolidation)none6
/public-api/v1/dex/searchasset resolution: address → CoinMarketCap id, then the same id on every other chainnone12
/public-api/v2/cryptocurrency/infothe canonical cross-chain contract registry the search rows are checked againstnone6
/public-api/v4/dex/pairs/quotes/latestdepth confirmation: the destination pool's liquidity now, and the source pool'snone7
/public-api/v1/dex/tokenthe card header: name, symbol, token liquiditynone7
finding 1 · the artefact
13 rows at 10⁴²

In a sweep of 1,852 rows, 13 rows (CAKE on bsc, an unlabeled venue) carried tu = −1e+42 — a removal worth more than every market on earth — and out-ranked every real removal. Rows above $10B are refused as price-feed artefacts and listed under refused; the finding went to CMC with the rows.

finding 2 · just-in-time
118 transactions

118 of the sweep's 1,852 rows were a transaction that both adds and removes (97 on DAI, 21 on CAKE). Counted as removals they make every alert a Rebalance. A JIT pair is discarded before the trigger, and naming one by hash returns a refusal, not a verdict.

finding 3 · the throttle
p50 27.0 s · p95 28.0 s

5 keyless investigations, one IP. The transcript below took 50.6 s: the anonymous tier reports its per-IP limit as error 1022, 1011 or 500 with no Retry-After, and the run hit 1 backoff of 15 s. The tool backs off 15 / 30 / 60 s, records every retry, and exits 75 when the quota is spent.

12 dated, evidenced findings for the CoinMarketCap team — and what is worth protecting — in FEEDBACK.md (opens in a new tab). Every endpoint the code calls is named above; the render of this page aborts if scripts/forwarding.py ever calls one it does not.

Run it before you score it

No key. No install. About half a minute.

The judged capability is a stdlib-only script against the keyless surface. With no flags it takes the largest non-JIT removal in the token's last 300 rows, follows the wallet on every chain the asset trades on, and rewrites the alert. The transcript is the command's output, replayed from its receipt.

forwarding — keyless
$ git clone https://github.com/edycutjong/forwarding.git && cd forwarding
$ python3 scripts/forwarding.py investigate --platform ethereum --address 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984
forwarding address — keyless · ethereum · 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984

  following the wallet
    GET /v1/dex/token                    address=0x1f9840…01f984 platform=ethereum                  200   487 ms
    GET /v1/dex/liquidity-change/list    address=0x1f9840…01f984 minVolume=100000 platform=ethereum 200  1004 ms  100 rows
    GET /v1/dex/liquidity-change/list    address=0x1f9840…01f984 lastId=AVd6RTNP…E9PQ== minVolume=100000 platform=ethereum 200   641 ms  100 rows
    GET /v1/dex/liquidity-change/list    address=0x1f9840…01f984 lastId=AVd6RTNP…YwPQ== minVolume=100000 platform=ethereum 200   573 ms  100 rows
    GET /v1/dex/token/pools              address=0x1f9840…01f984 platform=ethereum size=20          200   516 ms  20 items
    GET /v1/dex/liquidity-change/list    address=0x1f9840…01f984 maker=0x4f0aa5…8bb9a9 platform=ethereum 200   806 ms  2 rows
    GET /v1/dex/search                   q=0x1f9840…01f984                                          200   466 ms  4 tokens
    GET /v1/dex/search                   q=UNI                                                      200  1180 ms  50 tokens
    GET /v2/cryptocurrency/info          id=7083                                                    200   297 ms
    GET /v1/dex/liquidity-change/list    address=0xbf5140…2ce9b1 maker=0x4f0aa5…8bb9a9 platform=bsc 200   522 ms  0 rows
    GET /v1/dex/liquidity-change/list    address=0xfa7f89…f1f7f0 maker=0x4f0aa5…8bb9a9 platform=arbitrum 200   501 ms  0 rows
    GET /v1/dex/liquidity-change/list    address=0x8f187a…e9ea21 maker=0x4f0aa5…8bb9a9 platform=unichain 200   541 ms  0 rows
    GET /v1/dex/liquidity-change/list    address=0xb33eaa…b5180f maker=0x4f0aa5…8bb9a9 platform=polygon 200   955 ms  0 rows
    GET /v4/dex/pairs/quotes/latest      contract_address=0x8626be…4a7306 network_slug=ethereum     200   647 ms  1 items

  ● LP REMOVED  −$21,330,275   Ring Exchange (Ethereum) · UNI/WBTC   2026-09-02 03:59:11 UTC
    maker 0x4f0aa5900b8292273b2f9a178d5468f8048bb9a9 · share of pool unknown · txn 0x5081d9…dcd495

  ◆ MIGRATION · severity amber
    99.8% recovered — $21,287,255 of $21,330,275
    +$21,287,255 into Ring Exchange (Ethereum) · UNI/WETH (ethereum)   2 min 12 s later · pool now holds $102,239,395

  the rows (verbatim fields)
    remove  ts=1788321551000 tu=-21330274.564875204 m=0x4f0aa5900b8292273b2f9a178d5468f8048bb9a9 en=Ring Exchange (Ethereum) a0=-1962475.5391248302 txn=0x5081d9…dcd495
    add     ts=1788321683000 tu=21287254.934237212 m=0x4f0aa5900b8292273b2f9a178d5468f8048bb9a9 en=Ring Exchange (Ethereum) a0=1962475.5391248302 txn=0x9fafe3…71a0f3  [ethereum]
    21,287,254.93 ÷ 21,330,274.56 = 0.9980

  refused
    · Solana: 8FU95xFJ…Ceab36 is not an EVM address — a maker there cannot be this wallet
    · Gnosis: 0x4537e3…af9d74 holds $239 of DEX liquidity — below the $10,000 floor, not followed
    · HECO: 0x22c54c…5c46e6 holds $0 of DEX liquidity — below the $10,000 floor, not followed
    · Avalanche: 0x8ebaf2…ba8580 holds $214 of DEX liquidity — below the $10,000 floor, not followed

  notes
    · share of pool unknown — the pool is not in the token's pool list (20 returned), so its depth now is not known

  14 calls · 14 × 200 · 0 credits · 50.6 s · keyless
  wrote docs/proof/live_run.json

14 calls, 50.6 s, 0 credits, captured 2026-09-18 23:36:32 UTC — your numbers will differ, because the removal comes from the market rather than from this file. The agent surface is the same engine with a different caller: claude mcp add forwarding -- python3 $PWD/scripts/mcp_server.py — a real Claude Code session, 3 tool calls in 5 turns, 72 s, every figure quoted from a tool result and the JIT pair refused, is committed in mcp_session.md (opens in a new tab). Also in the repository: 126 offline tests and 10 live ones, 2,000 generated investigations with 0 violations of the six invariants, and an adjudication that replays in 0.004 ms (p50, n=1000, byte-identical every time).

Questions a judge will ask

Honest limits.

Is this page live?

The verdict is rendered from a committed run, captured 2026-09-18 23:16:31 UTC, and says so on every number. The paste box is live: the same engine, same-chain, through a keyless proxy that shares one IP across every visitor, so a 429 is shown as a 429 with the command to run instead. The CLI is the primary live path — every reader has their own quota there.

How wide is the window?

±6 h around the removal, walked with the cursor because startTime is plan-gated on the keyless surface. A wallet that comes back a day later reads as an Exit for that window, and the card says which window it searched. Adds before the removal count too, so a wallet that cycles more than once inside the window can show a share above 100 % — the verdict counts and discloses the other removals.

Is a verdict a judgment on a person?

No. A wallet is not an entity: liquidity moved through a second wallet is not followed, and the share is a floor. The page prints the two rows and the rule; the number is "the same maker address on both rows", exactly as the API reports it, and nothing more is claimed.

Why does the card say the share of pool is unknown?

Rows carry no pool address, so a pool is (venue, pair) and two fee tiers of one pair collapse into one identity. The removal's pool was not among the 20 that token/pools returns, so its depth now is unknown — and the page refuses to print a number no row supports: make check fails on "0.0 % of the pool". Both are findings in FEEDBACK.md.

What does the agent refuse?

A transaction that both adds and removes (118 of the sweep's 1,852 rows); a removal under 10 % of its pool, with the share printed; rows above $10B (13 in the sweep, price-feed artefacts); Solana as a cross-chain destination, because a Solana maker cannot be this wallet; a chain where the asset holds under $10k of liquidity. A throttled follow is INCOMPLETE, never an Exit.

What is not built?

No prediction, no wallet labelling, no second-wallet following, no swap analysis, no composite score, no key. The product follows the wallet and rewrites one alert; that verb is the whole scope. When nothing comes back the verdict says Exit with the window it searched — the rule is printed, never widened.

Liquidity that moved has a forwarding address

Follow the wallet on a token you care about.