Guides
Build a Real-Time Robinhood Chain Whale Tracker in Python cover

Build a Real-Time Robinhood Chain Whale Tracker in Python

Track whale trades on Robinhood Chain in real time. Subscribe to on-chain logs over Dwellir's WebSocket, decode large USDG and Stock Token transfers, and alert instantly.

LanguagePython
NetworkRobinhood Chain

Large trades move markets, and on Robinhood Chain they are all on-chain and public. Every Stock Token swap and every USDG settlement is an event log you can subscribe to in real time. In this tutorial you will build a whale tracker that streams those logs over a WebSocket, decodes the ones above a size threshold, and prints them the moment they land. It runs in about 150 lines of Python and works on any EVM chain, but Robinhood Chain is a good target because its tokenized stocks and USDG quote asset give you a clean, dollar-denominated view of whale activity.

Quick answer: Robinhood Chain is an EVM L2 (chain ID 4663), so you track whales the standard EVM way: open a WebSocket to an archive RPC, eth_subscribe to logs for the tokens you care about, and decode ERC-20 Transfer events. Filter for transfers above your threshold and you have a live whale feed. Dwellir provides the archive WebSocket endpoint this needs.

What Counts as a Whale Here

On a tokenized-stock chain, "whale" has a clean definition. USDG is the primary quote and settlement asset, so a large USDG transfer is a large trade in dollar terms. Watching USDG movement gives you a dollar-denominated whale feed without needing a price oracle. You can layer Stock Token transfers (NVDA, AAPL, and the rest) on top to see which assets the size is flowing into.

The approach generalizes. The same subscription that catches whale transfers is the foundation for a liquidation monitor, an arbitrage scanner, or a copy-trading bot. You are just changing which events you decode and what you do with them.

Prerequisites

  • Python 3.10+
  • A Robinhood Chain archive endpoint with WebSocket access. Sign up for Dwellir to get one, or use your own node.
  • The websockets and web3 packages: pip install websockets web3
  • Token contract addresses for USDG and any Stock Tokens you want to watch. Find these on the Robinhood Chain explorer.

How It Works

An ERC-20 transfer emits a Transfer(address indexed from, address indexed to, uint256 value) event. Every swap, deposit, and settlement produces one. Each log has:

  • topics[0]: the event signature hash, identical for every ERC-20 Transfer.
  • topics[1] and topics[2]: the indexed from and to addresses.
  • data: the non-indexed value, a 32-byte integer.

Subscribe to logs filtered by the token address and that signature hash, decode the value, scale it by the token's decimals, and you have every transfer of that token in real time. Filter by size and you have whales.

Whale tracker data flow: Robinhood Chain to Dwellir WebSocket eth_subscribe to decode Transfer to filter by size to whale alert, with archive backfill via eth_getLogs.

Whale tracker data flow (left to right):

  1. Robinhood Chain (chain 4663)
  2. Dwellir WSS (eth_subscribe logs)
  3. Decode Transfer (value / decimals)
  4. Filter by size (> threshold)
  5. Whale alert (real-time)

Supporting: backfill history with the archive node via eth_getLogs.

Step 1: Connect and Subscribe

Open a WebSocket to the Dwellir endpoint and send an eth_subscribe request for logs. The filter restricts the stream to the tokens you care about and to the Transfer signature, so you are not decoding every log on the chain.

Python
import asyncio
import json
import os
import websockets
from web3 import Web3

WSS_URL = os.environ["ROBINHOOD_WSS_URL"]  # Dwellir Robinhood Chain WebSocket

# ERC-20 Transfer(address,address,uint256) signature hash
TRANSFER_TOPIC = Web3.keccak(text="Transfer(address,address,uint256)").hex()

# Tokens to watch. Fill in real Stock Token addresses from robinhoodchain.blockscout.com.
# Decimals differ per token: USDG uses 6, Stock Tokens use 18. Always confirm with a
# decimals() call rather than assuming, or a threshold check will be off by orders of magnitude.
TOKENS = {
    "USDG": {"address": "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", "decimals": 6, "threshold": 100_000},
    "NVDA": {"address": "0x...", "decimals": 18, "threshold": 5_000},
    "AAPL": {"address": "0x...", "decimals": 18, "threshold": 5_000},
}

# Reverse lookup by lowercased address
BY_ADDRESS = {t["address"].lower(): {"symbol": s, **t} for s, t in TOKENS.items()}

async def subscribe(ws):
    request = {
        "jsonrpc": "2.0",
        "id": 1,
        "method": "eth_subscribe",
        "params": [
            "logs",
            {
                "address": [t["address"] for t in TOKENS.values()],
                "topics": [TRANSFER_TOPIC],
            },
        ],
    }
    await ws.send(json.dumps(request))
    ack = json.loads(await ws.recv())
    print(f"Subscribed: {ack.get('result')}")

The address array tells the node to only send logs from those contracts, and the single-element topics array matches any Transfer from them. This is far more efficient than streaming every log and filtering client-side.

Step 2: Decode Transfers and Flag Whales

Each incoming notification carries one log. Pull the token metadata by contract address, decode the from, to, and value, scale by decimals, and compare against that token's threshold.

Python
def decode_transfer(log):
    token = BY_ADDRESS.get(log["address"].lower())
    if token is None:
        return None

    # topics[1] and topics[2] are 32-byte padded addresses
    sender = "0x" + log["topics"][1][-40:]
    recipient = "0x" + log["topics"][2][-40:]

    raw_value = int(log["data"], 16)
    amount = raw_value / (10 ** token["decimals"])

    if amount < token["threshold"]:
        return None

    return {
        "symbol": token["symbol"],
        "amount": amount,
        "from": Web3.to_checksum_address(sender),
        "to": Web3.to_checksum_address(recipient),
        "tx": log["transactionHash"],
        "block": int(log["blockNumber"], 16),
    }

The threshold lives per token, so a $100,000 USDG move and a 5,000-share NVDA move can both count as whales even though the raw numbers differ by orders of magnitude. Scaling by decimals is what turns the on-chain integer into a human amount.

Step 3: The Main Loop

Connect, subscribe, then process notifications as they arrive. Each eth_subscription message contains one log.

Python
async def track_whales():
    async with websockets.connect(WSS_URL, max_size=None) as ws:
        await subscribe(ws)
        print("Watching for whales on Robinhood Chain...\n")

        async for raw in ws:
            message = json.loads(raw)
            if message.get("method") != "eth_subscription":
                continue

            log = message["params"]["result"]
            whale = decode_transfer(log)
            if whale is None:
                continue

            print(
                f"WHALE  {whale['amount']:,.2f} {whale['symbol']}  "
                f"{whale['from'][:8]} -> {whale['to'][:8]}  "
                f"block {whale['block']}  tx {whale['tx'][:12]}"
            )

if __name__ == "__main__":
    asyncio.run(track_whales())

Run it, and every transfer above your thresholds prints the moment it is included in a block. Because Robinhood Chain has roughly 100ms preconfirmation latency, alerts arrive within a fraction of a second of the trade.

Backfilling History with the Archive Node

A live stream only sees new events. To chart whale activity over the past day or week, or to backtest a strategy, you need historical logs. This is where the archive node matters: it can serve eth_getLogs across old block ranges that a pruned node would reject.

Python
from web3 import Web3

w3 = Web3(Web3.HTTPProvider(os.environ["ROBINHOOD_HTTP_URL"]))

logs = w3.eth.get_logs({
    "fromBlock": 9_400_000,
    "toBlock": "latest",
    "address": Web3.to_checksum_address(TOKENS["USDG"]["address"]),
    "topics": [TRANSFER_TOPIC],
})
print(f"Fetched {len(logs)} USDG transfers for analysis")

Pair the live subscription with historical backfill and you have both a real-time feed and the dataset to put each whale trade in context.

Production Tips

  • Reconnect on drop. WebSocket connections fail. Wrap the loop in a retry with backoff, and re-subscribe on reconnect. Track the last processed block so you can backfill the gap with eth_getLogs.
  • Watch the DEX pools, not just tokens. To attribute a transfer to a specific venue like Arcus or Uniswap, add the pool and router addresses to your watch list and decode their Swap events for exact trade sides and prices.
  • Mind the pricing model. A busy backfill hammers eth_getLogs, which is an expensive method under compute-unit pricing. Dwellir's flat per-request pricing keeps that cost predictable.
  • De-duplicate on reorgs. Even fast L2s can reorg. Key alerts on (transactionHash, logIndex) and confirm a few blocks deep before treating an event as final.

Where to Take It Next

The same subscription pattern is the backbone of a whole class of tools:

  • Liquidation tracker: decode lending-protocol liquidation events instead of transfers.
  • Copy-trading bot: watch one target wallet's swaps and mirror them through the DEX router.
  • Arbitrage scanner: subscribe to Swap events across multiple pools and compare prices.

Each is the same core loop with a different decoder and a different action.

Getting Started

You need two things to run this: the token addresses from the explorer, and an archive WebSocket endpoint. Sign up for Dwellir to get archive and WebSocket access to Robinhood Chain on flat pricing, or contact the team if you need dedicated nodes or colocated execution for a latency-sensitive strategy.


This content is for educational purposes only and does not constitute financial advice. Trading involves significant risk.