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.
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 here | 2 | the add: +$21,287,255 into Ring Exchange (Ethereum) · UNI/WETH | 1 | 200 · complete |
| bsc0xbf5140…2ce9b1 | 0 | nothing by this wallet | 1 | 200 · complete |
| arbitrum0xfa7f89…f1f7f0 | 0 | nothing by this wallet | 1 | 200 · complete |
| unichain0x8f187a…e9ea21 | 0 | nothing by this wallet | 1 | 200 · complete |
| polygon0xb33eaa…b5180f | 0 | nothing by this wallet | 1 | 200 · complete |
| Solana | — | refused · 8FU95xFJ…Ceab36 is not an EVM address — a maker there cannot be this wallet | ||
| Gnosis | — | refused · 0x4537e3…af9d74 holds $239 of DEX liquidity — below the $10,000 floor, not followed | ||
| HECO | — | refused · 0x22c54c…5c46e6 holds $0 of DEX liquidity — below the $10,000 floor, not followed | ||
| Avalanche | — | refused · 0x8ebaf2…ba8580 holds $214 of DEX liquidity — below the $10,000 floor, not followed | ||
The two rows, verbatim — one maker= call
| side | utc | pool | tu (usd) | a0 | a1 | txn |
|---|---|---|---|---|---|---|
| remove | 2026-09-02 03:59:11 UTC | Ring Exchange (Ethereum) · UNI/WBTC | -21,330,274.56 | -1,962,475.5391 UNI | -137.9592 WBTC | 0x5081d9…dcd495 |
| add | 2026-09-02 04:01:23 UTC | Ring Exchange (Ethereum) · UNI/WETH [ethereum] | 21,287,254.93 | 1,962,475.5391 UNI | 4,402.6945 WETH | 0x9fafe3…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"
}
]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.
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)
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.
every call behind this verdict · 11 · endpoint · params · status · ms · credit_count · sha256
| # | endpoint | params | status | ms | credit_count | sha256 of the bytes received |
|---|---|---|---|---|---|---|
| 1 | GET /v1/dex/token | address=0x1f9840…01f984 platform=ethereum | 200 | 456 | 1 · 0 billed | 42305d3a63dcdb2de4a5badcec219a5fc3315e354b6fbb451308fc151dfe6054 |
| 2 | GET /v1/dex/liquidity-change/list | address=0x1f9840…01f984 maker=0x4f0aa5…8bb9a9 platform=ethereum | 200 | 577 | 1 · 0 billed | 5bf63e4f3fed28fcbc93f205f1741c71469cbac6a2efa32c5bebf5f2c3eeae89 |
| 3 | GET /v1/dex/token/pools | address=0x1f9840…01f984 platform=ethereum size=20 | 200 | 485 | 1 · 0 billed | 3ead9d29cb7fe987d76ea0da897b883cf178fea8b2fd82fbe46889071f115955 |
| 4 | GET /v1/dex/search | q=0x1f9840…01f984 | 200 | 1185 | 1 · 0 billed | 84b9b2940b80bd415628476934097cf5326665d3eb4489920db2e841853463f3 |
| 5 | GET /v1/dex/search | q=UNI | 200 · 3 attempts | 458 | 1 · 0 billed | 4ef369fd8d554aab14c2634626ee2ecf7d864f887bddbfa497a854cd4edf7415 |
| 6 | GET /v2/cryptocurrency/info | id=7083 | 200 | 355 | 1 · 0 billed | 06d1aa87ed66526bc133be9939cdbe4da38837683d2c9ff0f8f3100fb1366527 |
| 7 | GET /v1/dex/liquidity-change/list | address=0xbf5140…2ce9b1 maker=0x4f0aa5…8bb9a9 platform=bsc | 200 | 1213 | 1 · 0 billed | b0f83c31998c54d5c53a67d16a2d56a82f83c2bf018e5d7d3f79ea952810de50 |
| 8 | GET /v1/dex/liquidity-change/list | address=0xfa7f89…f1f7f0 maker=0x4f0aa5…8bb9a9 platform=arbitrum | 200 | 535 | 1 · 0 billed | 67b651417d1b6b3a5adf4e40d375d43c5034360cf977ee4444af567ed7c64d56 |
| 9 | GET /v1/dex/liquidity-change/list | address=0x8f187a…e9ea21 maker=0x4f0aa5…8bb9a9 platform=unichain | 200 | 612 | 1 · 0 billed | cf6b4f53059f1991f483ea48c57e6d924f6c9790506e73c5973d1f20ab883f53 |
| 10 | GET /v1/dex/liquidity-change/list | address=0xb33eaa…b5180f maker=0x4f0aa5…8bb9a9 platform=polygon | 200 | 508 | 1 · 0 billed | 5be7b3d74569d601f12ed9f99e930c1b30ba959468f8fa1792748fa82a65e97a |
| 11 | GET /v4/dex/pairs/quotes/latest | contract_address=0x8626be…4a7306 network_slug=ethereum | 200 | 885 | 1 · 0 billed | 9dffddafeedae6dcdc469215140cc1937c1311662b4e1649f32df2b6369fffaa |
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.
| endpoint | used for | key | calls behind this page |
|---|---|---|---|
/public-api/v1/dex/liquidity-change/list | the trigger (removals ≥ $100k) and the join: maker= returns one wallet's adds and removes across every pool of the token | none | 45 |
/public-api/v1/dex/token/pools | every pool of the token: identity, liquidity now (share-of-pool), pool address, creation time (Consolidation) | none | 6 |
/public-api/v1/dex/search | asset resolution: address → CoinMarketCap id, then the same id on every other chain | none | 12 |
/public-api/v2/cryptocurrency/info | the canonical cross-chain contract registry the search rows are checked against | none | 6 |
/public-api/v4/dex/pairs/quotes/latest | depth confirmation: the destination pool's liquidity now, and the source pool's | none | 7 |
/public-api/v1/dex/token | the card header: name, symbol, token liquidity | none | 7 |
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.
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.
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.
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.
$ 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).
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.