There is no score: the verdict is a decision table (packages/core/src/classify.ts), evaluated top to bottom, first match wins. Every condition is a Nansen response field, and every evidence line on the card names the endpoint and field it came from. The numbers below are from the recorded fixtures (fixtures/*.json, live 2026-09-16).
| # | Condition (Nansen field) | Route · state | Confidence | Evidence code | Fixture that hits it |
|---|---|---|---|---|---|
| 1 | profiler/address/transactions or counterparties → HTTP 422 Burn address not allowed |
contract-or-burn · burn | high | BURN_422 |
0x…dEaD |
| 2 | search/general → tokens[].address == recipient and tokens[].chain == the chain asked about (search is chain-agnostic; review 2026-09-17) |
contract-or-burn · token-contract | high | TOKEN_CONTRACT |
USDC 0xa0b8…eb48 (0 credits, 1 call) |
| 3 | transaction-with-token-transfer-lookup → token_transfer_array[].from/to_address_label of the recipient matches ^<Entity>: Deposit$ |
exchange-deposit · direct-label | high | OWN_DEPOSIT_LABEL (+ SWEEP_TO_EXCHANGE, OUTFLOW_CONCENTRATED, FUNDED_BY_EXCHANGE as corroboration) |
0xe460…bbef → "🏦 Binance: Deposit", 100 % of $2,953 out to Binance 14, gas from "🏦 Binance [0x943080]" |
| 3b | the recipient's own label carries Nansen's 🏦 marker, is not a deposit label and not a contract label, and no deployer exists | exchange-deposit · exchange-wallet | high | OWN_EXCHANGE_LABEL |
(unit test: Binance 14 itself) |
| 4 | ≥1 outbound transfer and every looked-up destination label is 🏦-marked, not a : Deposit label, not a contract label, and all name one exchange (outflow split across several exchanges is a person's pattern — warning, falls through) |
exchange-deposit · sweep-pattern | medium → high if first-funder label is the same exchange or counterparties.volume_out_usd share ≥ 0.95 |
SWEEP_TO_EXCHANGE, FUNDED_BY_EXCHANGE, OUTFLOW_CONCENTRATED |
0x6465…e0e2 (Coinbase deposit address minutes old, no own label yet) |
| 5 | related-wallets.relation ∈ {Deployed by, Created by} or own label contains Contract / Router / Proxy / Pool / …Token |
contract-or-burn · contract, or · forwarder when every outbound goes to one named custodian wallet (a labelled pool/router destination is a swap, not custody — the Uniswap V3 SwapRouter stays contract) |
high (deployer) / medium | DEPLOYED_BY, CONTRACT_LABEL, FORWARDS_TO_CUSTODIAN |
Uniswap V2 router (Deployed by 0x9c33… 2020-06-05); 0xfcfd…a69c forwarder → "🤖 BitGo MultiSig" |
| 6 | sender given (and not itself an exchange wallet) and: recipient ∈ related-wallets(sender), or first-funder(recipient) == sender, or sender ∈ related-wallets(recipient). When the only link is "sender was the first funder", the recipient must also have sent something since — a mistaken ETH send to a fresh address is that address's first funding too (warning, falls through to the stranger rules) |
your-own-wallet · related / funded | high | RELATED_TO_SENDER, FUNDED_BY_SENDER |
0x3ff4…62b0 --from 0xb0ae…03b5 (First Funder, 13 sends since) |
| 7 | transactions.data empty (all-time, HTTP 200) |
active-stranger · fresh — the headline says Nansen may simply not index the address (vitalik.eth returns empty profiler pages) | low | NO_HISTORY |
0xd8da…c6ba (live QA 2026-09-16, 0 rows on every profiler endpoint) |
| 8 | ≥2 inbound transfers with a homoglyph/digit-swap token_symbol (≥ half of inbound) and 0 outbound |
active-stranger · poisoner | high | SPOOF_TOKEN_TRANSFERS |
0x50b3…eac9 ("ÚЅDТ" ×3) |
| 9 | 0 outbound ever | active-stranger · dormant | low | ACTIVITY, FUNDED_BY_EXCHANGE, COUNTERPARTIES |
0x4004…a02c (1 in / 0 out, first-funder empty) |
| 10 | otherwise; last outbound ≤ 90 days → medium | active-stranger · active | medium / low | ACTIVITY, DEPOSITS_INTO_EXCHANGE, … |
0x3ff4…62b0 |
| — | transactions, counterparties and related-wallets all failed (non-422) | retry — no verdict | — | LOOKUPS_FAILED |
(unit test) |
| — | quiet address whose all-time transactions page failed, and the 14-day page shows nothing sent (transactionsWindow = 14d-partial) |
retry — no verdict: "nothing on record" / "never sent" cannot be read off 14 days | — | HISTORY_INCOMPLETE |
(unit test) |
| — | transactions failed (timeout / 5xx) and no rule above fired |
retry — no verdict: the stranger states are read off the transactions page, so a timeout must never become "nothing on record" | — | TRANSACTIONS_FAILED |
(unit test) |
Warnings are attached, never hidden: failed lookups with their error, "only one sweep seen", look-alike counterparties (address poisoning), "your address is an exchange wallet", a --deep disagreement.
- Burn and token contract first because they are cheapest (0–1 credits) and end the search before profiler calls.
- Exchange evidence (3, 3b, 4) outranks the contract flag (5) because exchanges issue forwarder contracts as deposit addresses (
createForwarder). - A
: Depositlabel on a destination is excluded from rule 4: that is a person depositing into an exchange, not a sweep (live regression, fixture 11). - 🏦 alone is not "exchange": Nansen marks DEX routers with it too ("🤖 🏦 Uniswap: V2 Router 2"), so contract labels are excluded from 3b and 4.
- Own-wallet (6) needs the sender and comes after contract detection because "you deployed it" is still a contract.
- The 1–5-credit profiler label fields (
counterparty_address_label,first_funder_name,address_label) carry wealth/activity tags only: Binance 14 is["Token Billionaire"], Coinbase 10["High Activity"]. The entity label "Binance" lives in the 100-creditprofiler/address/labels— and intransaction-with-token-transfer-lookup.token_transfer_array[].*_address_labelfor 1 credit, where user-level deposit addresses read "🏦 Binance: Deposit". The whole engine hangs on that field. profiler/address/transactionswith an all-time date range makes Nansen return 500 after 30 s on the Uniswap router; a 14-day window answers in ≤ 6 s on the USDT contract and Binance 14. Hence the two-stage window (lookups.ts).- Burn addresses are refused with a 422 — the refusal is the signal.
first-funderreturnsdata: []for wallets that never received native gas (a fresh withdrawal recipient): documented, and handled as "no funder", not an error.- Address poisoning is visible in two Nansen fields: look-alike
counterparty_addressvalues (same first/last hex) and inboundtoken_symbolvalues with homoglyphs ("ÚЅDТ", "U5DТ") thathide_spam_token: truedoes not filter.
sha256({route, sub, confidence, entity, evidence[code, value], action.text}) — timing, credits, cache state and warnings are excluded. npm run verify replays every fixture offline and requires the same hash; npm run bench checks that a warm (cached) verdict has the same hash as the cold one.