THORChain’s cross-chain router contracts were breached on May 15, 2026. Over $10.8 million drained from Bitcoin, Ethereum, BNB Chain, and Base before nodes pulled the emergency stop.

The halt did not come from THORChain’s core team. It came from below.

A Node Operator Saw It First

SamYap, a THORChain node operator, flagged the abnormal outflows inside the thornode-mainnet channel. Reviewing transaction data for Ethereum wallet 0x82fc0d5150f3548027e971ec04c065f3c93154eb, he spotted a cluster of send transactions moving from the TC router to an external address, all without memo fields. Standard protocol behaviour requires memos. These had none.

SamYap’s alert in the thornode-mainnet channel calling for a global emergency halt after detecting memo-less transactions from THORChain’s router contract.

“Indeed this address seems weird, a lot of send Tx from TC router to this address without memo. Nodes, suggest pls do global emergency halt.” — SamYap, thornode-mainnet channel (edit: Global pause done)

On-chain investigator ZachXBT separately posted a community alert on Telegram at t.me/investigations/319, identifying the exploit across all four chains and publishing both theft addresses before THORChain’s team had made any public statement.

The breach appears to be a direct compromise of THORChain’s router contracts, not a standard bridge vulnerability. Funds were converted fast, with stolen ETH pushed into BTC through THORChain’s own liquidity pools.

The Money Trail Is Tighter Than It Looks

The main Ethereum theft wallet recorded 123 transactions on May 15 alone. Total ETH volume moving through it reached roughly 9,481 ETH, around $21.4 million at $2,262 per ETH at the time of tracking. Four separate transfers of 1,866 ETH each cleared the wallet between 09:06 and 09:19 UTC, all landing in 0xd4767e6291273dfbd16095abef12aae6628890bd. Another 1,866 ETH moved to a closely related address within the same window.

Four transfers. Thirteen minutes. That points to automated execution, not a manual attacker clicking through a wallet interface.

The funds did not scatter. A six-address cluster absorbed the bulk of the outflows:

Primary ETH theft wallet: 0x82fc0d5150f3548027e971ec04c065f3c93154eb

Main external sink: 0x0663bba921e1510366a8739776844ddb9f6edde8 (tracked value ~$77.1M across internal loops)

Secondary sink: 0x7f54f05635d15cde17a49502fedb9d1803a3be8a (~$3.06M tracked)

Additional cluster addresses: 0xd4767e6291273dfbd16095abef12aae6628890bd | 0xd4772db9c73ceff784ab93f48e92ca31f2ae90bd | 0xd477b69551f49c0519f9b18c55030676138890bd

0x7f54f05635d15cde17a49502fedb9d1803a3be8a showed extensive looping activity dating back to April 2026, hundreds of large self-transfers before May 15. This infrastructure was not built the morning of the attack.

Cross-chain conversion matched the ETH movement. The Bitcoin theft wallet bc1ql4u94klk265lnfur2ujk9p6uh52f2a8jhf6f37 received and sent out approximately 36.85 BTC on the same day, consistent with stolen ETH being routed into BTC through THORChain’s own pools.

RUNE Dropped. No Post-Mortem Came.

RUNE fell approximately 13%, moving from $0.58 to $0.50 per CoinGecko data on May 15. A RUNE holder watching that move had nothing from the team to read. No official explanation of the attack vector has been published as of the time this article was written.

This is at least the third security incident for THORChain in recent years. Cross-chain liquidity protocols have accumulated over $2.8 billion in theft since 2021, per Chainalysis data. THORChain has appeared in that record both as a target and as a laundering channel for other exploiters.

As of the latest on-chain checks, no direct deposits to any major centralised exchange appeared in the first two to three hops from the theft wallets. Private OTC arrangements or bridge usage in later hops remain possible. The funds were still moving at publication time.

Track the Wallets Yourself

For those following the fund movements directly, a Python-based tracking script targeting the primary theft addresses is available. It pulls normal and token transfer history from Etherscan’s v2 API, flags transfers above 50 ETH, and saves everything to a CSV file sorted by date.

To run it, a free Etherscan API key is required. Get one at etherscan.io/apis. After creating the key, install the dependencies:

pip install pandas requests

Save the script below as thorchain_theft_tracker.py, insert the API key where indicated, then run:

python thorchain_theft_tracker.py

Full Tracking Script:

import requests

import pandas as pd

from datetime import datetime, timezone

import time

# ================== CONFIGURATION ==================

# === PUT YOUR OWN ETHERSCAN API KEY HERE ===

ETHERSCAN_API_KEY = “YOUR_ETHERSCAN_API_KEY_HERE”   # Change this!

ETH_ADDRESSES = [

    “0x82fc0d5150f3548027e971ec04c065f3c93154eb”,   # Main Thorchain theft wallet

]

BTC_ADDRESS = “bc1ql4u94klk265lnfur2ujk9p6uh52f2a8jhf6f37”

LARGE_ETH_THRESHOLD = 50

OFFSET = 100

# ===================================================

def unix_to_date(ts):

    return datetime.fromtimestamp(int(ts), tz=timezone.utc).strftime(“%Y-%m-%d %H:%M:%S UTC”)

def get_etherscan_v2(action, address):

    if ETHERSCAN_API_KEY == “YOUR_ETHERSCAN_API_KEY_HERE”:

        print(“ERROR: Please put your Etherscan API key in the script first!”)

        exit()

    url = “https://api.etherscan.io/v2/api”

    params = {

        “chainid”: “1”, “module”: “account”, “action”: action,

        “address”: address, “sort”: “desc”, “offset”: OFFSET,

        “apikey”: ETHERSCAN_API_KEY

    }

    try:

        response = requests.get(url, params=params, timeout=30)

        data = response.json()

        return data.get(“result”, []) if data.get(“status”) == “1” else []

    except Exception as e:

        print(f”Error: {e}”)

        return []

def track_ethereum():

    print(“Fetching Ethereum Data…\n”)

    all_txs = []

    for address in ETH_ADDRESSES:

        print(f”Tracking: {address}”)

        normal = get_etherscan_v2(“txlist”, address)

        tokens = get_etherscan_v2(“tokentx”, address)

        for tx in normal:

            all_txs.append({

                “Chain”: “Ethereum”, “Type”: “Normal”, “Date”: unix_to_date(tx[“timeStamp”]),

                “Hash”: tx[“hash”], “From”: tx[“from”], “To”: tx[“to”],

                “Value”: round(float(int(tx[“value”])) / 1e18, 6),

                “Token”: “ETH”, “Method”: tx.get(“functionName”, “Transfer”)

            })

        for tx in tokens:

            decimal = int(tx.get(“tokenDecimal”, 18))

            all_txs.append({

                “Chain”: “Ethereum”, “Type”: “Token”, “Date”: unix_to_date(tx[“timeStamp”]),

                “Hash”: tx[“hash”], “From”: tx[“from”], “To”: tx[“to”],

                “Value”: round(float(int(tx[“value”])) / (10 ** decimal), 6),

                “Token”: tx.get(“tokenSymbol”, “Unknown”), “Method”: “Transfer”

            })

    df = pd.DataFrame(all_txs)

    if not df.empty:

        df = df.sort_values(“Date”, ascending=False)

        df.to_csv(“thorchain_theft_tracker.csv”, index=False)

        print(f”\nTotal Records: {len(df)}”)

        print(f”Total ETH Volume: {df[df[‘Token’]==’ETH’][‘Value’].sum():,.2f} ETH”)

        print(f”\nLarge Transfers (>{LARGE_ETH_THRESHOLD} ETH)”)

        large = df[(df[“Token”] == “ETH”) & (df[“Value”] > LARGE_ETH_THRESHOLD)]

        print(large[[“Date”, “Value”, “From”, “To”]].to_string(index=False))

    return df

def track_bitcoin():

    print(“\nBitcoin Data:”)

    url = f”https://blockstream.info/api/address/{BTC_ADDRESS}/txs”

    try:

        txs = requests.get(url, timeout=20).json()

        print(f”   Address : {BTC_ADDRESS}”)

        print(f”   Total Tx: {len(txs)}”)

    except:

        print(”   Bitcoin data fetch failed.”)

if __name__ == ‘__main__’:

    print(“=” * 60)

    print(“THORCHAIN THEFT WALLET TRACKER”)

    print(“=” * 60)

    track_ethereum()

    track_bitcoin()

    print(“\nFile saved: thorchain_theft_tracker.csv”)

    print(“Done! Run again anytime to refresh data.”)

Setup and Troubleshooting

Step 1: Get a free Etherscan API key at etherscan.io/apis. Click Create New API Key, give it any name, copy the key, and replace YOUR_ETHERSCAN_API_KEY_HERE in the script.

Step 2: Install dependencies with pip install pandas requests. Step 3: Run the script. Output saves to thorchain_theft_tracker.csv. Open it in Excel or Google Sheets, sort by Date.

Common errors: if the script returns empty data or a NOTOK status, the API key may still be activating. Wait 5 to 15 minutes and run again. To track additional wallet addresses in the cluster, add them to the ETH_ADDRESSES list. The LARGE_ETH_THRESHOLD can be changed from 50 to any value.

To monitor continuously, run the script every 5 to 10 minutes. New transactions appear at the top of the sorted CSV. At the time of publication, the attacker cluster had not moved funds to any major exchange in the first two to three hops.