CRIUS Docs
API status Get API key
Concepts

How Crius measures token taxes (realized vs simulated)

How Crius measures buy, sell and transfer taxes on Robinhood Chain tokens from real trades and from a simulated buy and sell, how the two are combined, and how tax changes are tracked.

Updated 3 min read

A token’s tax is the part of a trade that doesn’t reach the trader: the pool sends 1,000 tokens and the buyer receives 930. Crius measures taxes two ways for every Robinhood Chain token. Realized taxes come from real trades: what traders actually paid. Simulated taxes come from a test buy, transfer and sell run against the latest block. Real trades are the ground truth when there are enough of them; the simulation fills the gap for new tokens and catches tokens that block sells.

Realized tax: from real trades

For every trade, Crius compares the tokens the pool sent (or received) with what the trader’s wallet received (or sent). The difference is that trade’s tax, whoever takes it: the token contract, a fee-on-transfer mechanism or the pool’s Uniswap v4 hook. Each trade on the trade tape carries it as fees.token_tax.pct.

From those per-trade taxes, /security builds the current rate:

  • The median of the latest 50 trades per side, within the last 30 days (tax.realized.window: "30d"). A median ignores the odd trade that paid much more or less than the rest.
  • The developer is measured separately. tax.realized.by_wallet_class has others_sell_pct for ordinary sellers and dev_sell_pct for the developer, so a developer who sells tax-free doesn’t hide the tax everyone else pays. The headline rate uses ordinary wallets.
  • tax.confidence is high with 20+ measured trades, medium with 3+ and low below that. tax.realized.samples gives the counts.

A median that ignores outliers: CHARGE

Charge AI (0x7b8892cea949bc60d494681d1bc3ea96071ab0b8) shows a 0% sell tax, but one sell paid far more. Which is right?

GET/v1/robinhood/token/0x7b8892cea949bc60d494681d1bc3ea96071ab0b8/security
curl "https://api.crius.sh/v1/robinhood/token/0x7b8892cea949bc60d494681d1bc3ea96071ab0b8/security"
Real response 200 Captured Oct 28, 2026 · 177 lines (arrays trimmed)
{
"data": {
"contract": {
"blacklist": false,
"can_change_limits": false,
"can_change_tax": false,
"cooldown": false,
"implementation": null,
"is_proxy": false,
"mintable": false,
"owner": null,
"ownership": "none",
"pausable": false,
"proxy_kind": null,
"self_destruct": false,
"trading_switch": false,
"upgradeable": false,
"verified": null
},
"flags": [
"unverified"
],
"honeypot": {
"is_honeypot": false,
"reason": null,
"selective": false
},
"hook": {
"address": "0xe5e702641ea86f4ae6cc3cdaed2b886f976be044",
"capabilities": {
"can_block_swaps": false,
"can_gate_liquidity": false,
"can_modify_liquidity_amounts": false,
"can_modify_swap_amounts": true
},
"code": {
"admin_functions": [],
"checked_ts": 1793207140434,
"code_hash": "0xc21b1e6c1b45403e81a581f22ed6d9c747997af1cfdac1b1dc9f4b1d346a10db",
"family": "0xabda3266e50c",
"kind": "contract",
"logic": null,
"owner": "0x263ed295dafae1d9aadd6e56c4b6f9f38ee019dd",
"ownership": "active",
"size": 15167
},
"family": {
"honeypot_pct": 0,
"hooks": 22,
"id": "0xabda3266e50c",
"liquidity_pulled_pct": 0.684086,
"pools_traded": 10525,
"taxed_pct": 51.9715,
"verified_hooks": 1
},
"flags": [],
"name": "PonsV2MemeHook",
"observed": {
"dynamic_tax_pools": 13,
"first_trade_ts": 1785866942000,
"honeypot_pools": 0,
"last_trade_ts": 1793223683000,
"liquidity_pulled_pct": 0.66813,
"liquidity_pulled_pools": 70,
"max_buy_tax_pct": 10.99,
"max_sell_tax_pct": 0.7,
"median_pool_life_min": 53423.5,
"pools": 10478,
"pools_traded": 10477,
"tax_measured_pools": 10392,
"taxed_10pct_pools": 35,
"taxed_pools": 5463,
"volume_usd": 5384607135
},
"pattern": {
"description": "hooks that change swap amounts only",
"honeypot_pct": 0.0696347,
"id": "swap_delta_only",
"liquidity_pulled_pct": 7.93603,
"pools_traded": 43082,
"taxed_pct": 15.7537
},
"permissions": [
"beforeInitialize",
"afterSwap",
"afterSwapReturnDelta"
],
"risk": [
"can_modify_swap_amounts"
],
"risk_level": "low"
},
"known_asset": false,
"launch": {
"flags": [],
"lp_top_wallet": {
"address": "0x2d424d69b1ead72b30de4c7fe4c53e4853e1419d",
"liquidity_usd": 3.0953,
"pct": 0.0551662
},
"sniper_supply_pct": 6.09195,
"sniper_top_wallet_pct": 6.09195,
"sniper_wallets": 1,
"sniper_window_blocks": 10,
"uniform_holders": null,
"wallet_farm": null
},
"limits": {},
"liquidity": {
"status": "partially_locked",
"summary": {
"burned_pct": 0,
"locked_pct": 99.8878,
"next_unlock_ts": null,
"protocol_fee_pct": 0,
"unlocked_in_wallets_pct": 0.1122,
"weighting": "by pool liquidity_usd"
}
},
"primary_dex": "uniswap_v4",
"risk": "low",
"score": 95,
"tax": {
"buy_pct": 2.99,
"confidence": "high",
"realized": {
"buy_pct": 2.99,
"by_wallet_class": {
"dev_sell_pct": null,
"others_sell_pct": 0
},
"last_sample_ts": 1793196204000,
"max_sell_pct_seen": 96.64,
"samples": {
"buys": 2539,
"sells": 1873
},
"sell_pct": 0,
"window": "30d"
},
"sell_pct": 0,
"simulated": null,
"source": "realized",
"transfer_pct": null
},
"tax_history": [
{
"from_block": 88385895,
"pct": 0,
"side": "buy"
},
{
"from_block": 88387118,
"pct": 0,
"side": "sell"
},
{
"from_block": 88461329,
"pct": 3,
"side": "buy"
}
],
"tax_history_total": 29,
"tax_mode": "dynamic",
"token": "0x7b8892cea949bc60d494681d1bc3ea96071ab0b8"
},
"meta": {
"block": 100406827,
"block_age_ms": 1163,
"block_ts": 1793225080000,
"chain": "robinhood",
"finalized_block": 100397649,
"lag_ms": 64,
"safe_block": 100401329,
"simulation": "no V2 WETH pair: realized tax only"
}
}

What this tells you

  • tax.sell_pct: 0 while tax.realized.max_sell_pct_seen: 96.64. Ordinary sells pay nothing; one sell lost almost everything. The median of recent sells is the rate a new seller can expect.
  • tax.buy_pct: 2.99, from 2,539 buys and 1,873 sells measured (confidence: "high").
  • tax_history_total: 29 and tax_mode: "dynamic": the measured rate has changed many times. The first entries show 0% on both sides at launch, then 3% on buys from block 88461329, eight blocks after its v4 liquidity was locked in the Pons launch locker at block 88461321 (see the liquidity example): the launchpad’s fee on the graduated pool.
  • tax.simulated: null, meta.simulation: "no V2 WETH pair: realized tax only": with no Uniswap v2 pair to simulate through, real trades are the whole answer.
Security checks reference Runs from your browser on the anonymous plan Data looks wrong? Report it

Simulated tax: a test trade at the latest block

When a token has a Uniswap v2 pair with WETH, Crius simulates a full round trip through the Uniswap v2 router at the latest block, without sending anything on chain:

  1. Buy about amount_usd worth (default $100) and compare the tokens received with what the router quoted: the buy tax.
  2. Transfer 10% of the tokens to a second wallet: the transfer tax (tax.transfer_pct), and whether transfers work at all (can_transfer).
  3. Approve and sell half of the rest. The sell tax is the share of tokens that never reached the pair, which doesn’t depend on price impact.

The result is in tax.simulated: can_buy, can_sell, buy_pct, sell_pct, transfer_pct, gas used, and revert_reason when a step fails. A buy that works followed by a sell that fails is a honeypot (see How to detect a honeypot). Results are cached briefly; fresh=1 runs a new simulation (Starter plan, 20 credits).

How the two are combined

Real trades availabletax.buy_pct / tax.sell_pcttax.source
Recent buys and sellsBoth realizedrealized
Recent buys but no recent sells by ordinary walletsBuy realized, sell simulatedrealized+simulated (or realized without a simulation)
No recent buysSimulated where availablesimulated, or none without a v2 WETH pair

When both a realized and a simulated sell tax exist and they differ by more than 2 percentage points, the token gets the tax_mismatch flag (−10 points). In the security score, a buy or sell tax above 5% costs 15 points (tax_over_5pct) and above 10% costs 30 (high_tax).

Tax history and dynamic taxes

tax_history turns the per-trade taxes of ordinary wallets into segments: a new entry (from_block, side, pct in whole percent) starts when a side’s rate changes and stays changed for at least two trades in a row, so one odd trade doesn’t create a change. tax_mode is dynamic when either side has more than one segment, else static. The response holds the newest 100 segments; tax_history_total counts all of them.

A dynamic tax isn’t always a trap. Launchpad tokens often go from 0% on the bonding curve to the launchpad’s fee after graduation, as CHARGE did. What matters is whether someone can still change it: contract.can_change_tax with an active owner, or a hook with a fee setter (fee_changeable in hook.flags).

Known assets

WETH, USDG and tokenized stocks (known_asset: true) have no tax to measure and no WETH pair to simulate against: their tax fields are empty and their trade noise isn’t treated as a tax history.

Frequently asked questions

Why does Crius use real trades instead of reading the tax from the contract?

Because the contract is often not where the tax is. On Robinhood Chain many taxes are taken by a Uniswap v4 hook, some contracts tax only certain wallets or sizes, and a tax variable can say one thing while the transfer logic does another. Comparing what the pool sent with what the wallet received catches all of these.

Why is the sell tax 0 when one sell paid 96%?

The headline rate is the median of the latest 50 sells by ordinary wallets, so one odd trade doesn't set it. tax.realized.max_sell_pct_seen keeps the worst single sell so you can still see it.

What does tax_mismatch mean?

The simulated sell tax and the realized sell tax differ by more than 2 percentage points. The token may tax differently by wallet or size, or the tax changed recently. It costs 10 points in the security score.

Get your free Crius API key

Every example on this page works with a free key: higher rate limits than the anonymous plan, holders, wallets and the WebSocket stream. No card needed.

  • Robinhood Chain (4663) only, from our own archive nodes
  • REST + WebSocket
  • Pay on-chain or by card