---
title: "EVM-native risk assessment for a Robinhood Chain token (PRO+)"
method: GET
path: "/rhc/tokens/{address}/risk"
tags: ["Robinhood Chain"]
---

# EVM-native risk assessment for a Robinhood Chain token (PRO+)

`GET /rhc/tokens/{address}/risk`

## Request

- Base URL: `https://madeonsol.com/api/v1`
- URL: `https://madeonsol.com/api/v1/rhc/tokens/{address}/risk`
- Auth: HTTP bearer

Risk assessment computed **live** against our own Robinhood Chain node at the chain head.

**This is not the Solana risk model.** EVM has no mint or freeze authority. Measured across 300 random Robinhood Chain tokens, only 2.3% even expose an `owner()` function and 0% expose `mint` in their own bytecode — so an absent flag is the norm, not a safety signal. The axes that actually discriminate here are proxy upgradeability, LP custody, and sellability.

**Sellability is the strongest signal.** A sell is simulated through the router at head using state overrides, which detects a token that can be bought but not sold. It is computed live, never cached — whether a token is sellable can change the moment an owner flips a setting.

Proxies are resolved before capability scanning: EIP-1167 minimal proxies (14% of tokens) carry zero selectors of their own, and the tokenized equities are beacon proxies whose *implementation* is what exposes `mint`/`pause`/`upgradeTo`. `lp_custody` is only read for uniswap-v2 pools; v3/v4 liquidity sits in an LP NFT whose custody needs a position-manager trace, and is reported `unknown` rather than guessed.

## Path parameters

- `address` string, required

## Response `200`

OK. Absent capability flags are the norm on this chain and are not a safety guarantee.

- object
  - `chain` 'robinhood'
  - `token_address` string
  - `checked_at` string, date-time: Computed live; always current.
  - `code_size` integer: Runtime bytecode length in bytes.
  - `is_contract` boolean
  - `proxy` object
    - `kind` 'none' | 'eip1167_minimal' | 'eip1967' | 'eip1967_beacon' | 'legacy'
    - `implementation` string, nullable
    - `admin` string, nullable
    - `upgradeable` boolean: False for eip1167_minimal — the target is baked into immutable code.
  - `owner` object
    - `model` 'none' | 'renounced' | 'eoa' | 'contract': `none` = no owner function at all, which is NOT the same as renounced.
    - `address` string, nullable
  - `capabilities` object
    - `can_mint` boolean
    - `can_pause` boolean
    - `has_access_control` boolean
    - `selectors_found` string[]
  - `liquidity` object
    - `primary_pool` string, nullable
    - `dex` string, nullable
    - `lp_custody` 'burned' | 'locked' | 'at_risk' | 'unknown'
    - `lp_burned_pct` number, nullable
    - `last_removed_at` string, date-time, nullable: Most recent LP withdrawal observed on this token (uniswap-v2/v3 `Burn`, uniswap-v4 negative `ModifyLiquidity`). `null` means none seen since removal capture began on 2026-08-05 — not that none ever happened.
  - `sellability` object
    - `sellable` 'yes' | 'no' | 'unknown'
    - `reason` string, nullable
  - `flags` string[]: Derived from the fields above. Includes `lp_removed_24h` when liquidity was withdrawn from this token in the last 24 hours.
  - `score` integer, nullable: 0–100, conservative. Absence of evidence is not evidence of safety.
  - `assessment` object: Whether every input was actually read (2026-09-21). A failed read is an UNKNOWN input, never a silent all-clear: e.g. lp_removal_history unknown ⇒ the absence of `lp_removed_24h` proves nothing; observed_sells unknown ⇒ a simulated sell revert is reported sellable=unknown, not a honeypot.
    - `status` 'complete' | 'incomplete'
    - `unknown_inputs` string[]
  - `coverage` object: What this model does and does not read — see the endpoint description.
    - `model` 'evm_native'
    - `liquidity_basis` 'v4_virtual_ceiling' | 'measured': Basis of any liquidity_usd figure quoted for this token: uniswap-v4 values are virtual reserves clamped to the PoolManager's total holdings — a provable ceiling, not measured per-pool TVL.
    - `liquidity_note` string
    - `note` string

## Other responses

- `400`: Invalid EVM token address.
- `401`: Missing or invalid API key, or an expired free-tier key. Authentication-stage error: no `X-RateLimit-*` headers, no `_rid`.
- `403`: PRO tier required.
- `429`: Too many requests. Every 429 carries `Retry-After`. A quota 429 (`rate_limit_exceeded`, with `X-RateLimit-*`), a heavy-query concurrency 429 (`error_kind` `concurrency`/`capacity`), or the per-IP authentication-failure block (30 failed auths/min; plain `error` + `retry_after_seconds`, no `X-RateLimit-*`).
- `500`: Unexpected server error from the route (`Internal server error`, with `_rid`). Safe to retry with backoff. A failed API-key lookup is a 503 `auth_backend_unavailable`, never a 500.
- `503`: Temporarily unavailable — a Postgres statement timeout while the dataset is under load or maintenance (`statement_timeout`, with `X-RateLimit-*` and `_rid`), or the API-key lookup itself unavailable (`auth_backend_unavailable`, authentication stage: no `X-RateLimit-*`, no `_rid`, not counted as a failed authentication). Always carries `Retry-After`; honour it.

---

[API](https://skmtc.dev/madeonsol/apis/madeonsol-api.md) · [All operations](https://skmtc.dev/madeonsol/apis/madeonsol-api/llms.txt) · [OpenAPI document](https://skmtc.dev/madeonsol/apis/madeonsol-api/revisions/d9531a28d83b?raw)
