---
# System prepended metadata

title: 'Tornado Weekly '
tags: [Developer]

---

# Tornado Weekly

## Overview

Documenting the development of "Tornado Weekly," a user-friendly, multi-chain Sankey visualization project derived from Tornado Cash activities. Given the sanctioned status of Tornado Cash and its increasing exploitation by malicious entities, enhancing the data's accessibility for blockchain participants to analyze and understand fund flows became compelling.   

The visualization leverages Dune Analytics as the data source and user interaction layer, with D3.js for rendering and ObservableHQ as the platform for distribution.   

In terms of code design, a simpler Dune Analytics query was opted for. This approach not only makes the data sources more transparent for users but also reduces operational costs and enhances user interactions. ObservableHQ was chosen as the distribution platform due to its iframe embed feature, allowing for seamless integration with various knowledge management systems like HackMD, Obsidian, and Notion. Last, D3.js was selected for its widespread recognition and use, aligning with the commitment to transparency and open-source principles.   

Despite these current decisions, it is important to note that "Tornado Weekly" remains in the early stages of ideation. Components such as the data source, visualization library, and programming language may undergo changes as the project evolves.   


## Dune Analytics TrinoSQL Code:
> [Dune Query Available Here](https://dune.com/queries/3474905)

<details>
  <summary>Dune Analytics query used for multichain capabilities</summary>
    
```sql=
    WITH
      ens_agg AS (
        SELECT
          address, MAX(name) AS ens_name
        FROM
          ens.resolver_latest
        WHERE
          address IS NOT NULL
        GROUP BY
          address
      )
    SELECT
      CASE
        WHEN contract_address = from_hex('178169B423a011fff22B9e3F3abeA13414dDD0F1') THEN 'Tornado.Cash:0.1WBTC'
        WHEN contract_address = from_hex('610B717796ad172B316836AC95a2ffad065CeaB4') THEN 'Tornado.Cash:1WBTC'
        WHEN contract_address = from_hex('bB93e510BbCD0B7beb5A853875f9eC60275CF498') THEN 'Tornado.Cash:10WBTC'
        WHEN contract_address = from_hex('12D66f87A04A9E220743712cE6d9bB1B5616B8Fc') THEN 'Tornado.Cash:.01ETH'
        WHEN contract_address = from_hex('47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936') THEN 'Tornado.Cash:1ETH'
        WHEN contract_address = from_hex('910Cbd523D972eb0a6f4cAe4618aD62622b39DbF') THEN 'Tornado.Cash:10ETH'
        WHEN contract_address = from_hex('A160cdAB225685dA1d56aa342Ad8841c3b53f291') THEN 'Tornado.Cash:100ETH'
        WHEN contract_address = from_hex('22aaA7720ddd5388A3c0A3333430953C68f1849b') THEN 'Tornado.Cash:5,000cDAI'
        WHEN contract_address = from_hex('BA214C1c1928a32Bffe790263E38B4Af9bFCD659') THEN 'Tornado.Cash:50,000cDAI'
        WHEN contract_address = from_hex('b1C8094B234DcE6e03f10a5b673c1d8C69739A00') THEN 'Tornado.Cash:500,000cDAI'
        WHEN contract_address = from_hex('2717c5e28cf931547B621a5dddb772Ab6A35B701') THEN 'Tornado.Cash:500,000cDAI2'
        WHEN contract_address = from_hex('d21be7248e0197ee08e0c20d4a96debdac3d20af') THEN 'Tornado.Cash:5,000,000cDAI'
        WHEN contract_address = from_hex('D4B88Df4D29F5CedD6857912842cff3b20C8Cfa3') THEN 'Tornado.Cash:100DAI'
        WHEN contract_address = from_hex('FD8610d20aA15b7B2E3Be39B396a1bC3516c7144') THEN 'Tornado.Cash:1,000DAI'
        WHEN contract_address = from_hex('F60dD140cFf0706bAE9Cd734Ac3ae76AD9eBC32A') THEN 'Tornado.Cash:10,000DAI'
        WHEN contract_address = from_hex('07687e702b410Fa43f4cB4Af7FA097918ffD2730') THEN 'Tornado.Cash:10,000DAI2'
        WHEN contract_address = from_hex('23773E65ed146A459791799d01336DB287f25334') THEN 'Tornado.Cash:100,000DAI'
        WHEN contract_address = from_hex('4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D') THEN 'Tornado.Cash:100USDC'
        WHEN contract_address = from_hex('d96f2B1c14Db8458374d9Aca76E26c3D18364307') THEN 'Tornado.Cash:1,000USDC'
        WHEN contract_address = from_hex('D691F27f38B395864Ea86CfC7253969B409c362d') THEN 'Tornado.Cash:10,000USDC'
        WHEN contract_address = from_hex('aEaaC358560e11f52454D997AAFF2c5731B6f8a6') THEN 'Tornado.Cash:5,000cUSDC'
        WHEN contract_address = from_hex('1356c899D8C9467C7f71C195612F8A395aBf2f0a') THEN 'Tornado.Cash:50,000cUSDC'
        WHEN contract_address = from_hex('A60C772958a3eD56c1F15dD055bA37AC8e523a0D') THEN 'Tornado.Cash:500,000cUSDC'
        WHEN contract_address = from_hex('169AD27A470D064DEDE56a2D3ff727986b15D52B') THEN 'Tornado.Cash:100USDT'
        WHEN contract_address = from_hex('0836222F2B2B24A3F36f98668Ed8F0B38D1a872f') THEN 'Tornado.Cash:1,000USDT'
        WHEN contract_address = from_hex('F67721A2D8F736E75a49FdD7FAd2e31D8676542a') THEN 'Tornado.Cash:10,000USDT'
        WHEN contract_address = from_hex('9AD122c22B14202B4490eDAf288FDb3C7cb3ff5E') THEN 'Tornado.Cash:100,000USDT'
        ELSE CAST(contract_address AS VARCHAR)
      END AS source,
      COALESCE(ens_name, TRY_CAST(recipient AS VARCHAR)) AS destination,
      DATE(block_time) AS day,
      '{{week_start_date}}' AS week_start_date,
      COUNT(*) AS number_of_withdrawals,
      '{{chain}}' AS chain,
      CASE
        WHEN '{{chain}}' = 'gnosis' THEN 'https://gnosisscan.io/address/'
        WHEN '{{chain}}' = 'bnb' THEN 'https://bscscan.com/address/'
        WHEN '{{chain}}' = 'avalanche_c' THEN 'https://app.dedaub.com/avalanche/address/'
        ELSE 'https://app.dedaub.com/' || '{{chain}}' || '/address/'
      END || TRY_CAST(recipient AS VARCHAR) AS destination_link
    FROM
      tornado_cash_{{chain}}.withdrawals
      LEFT JOIN ens_agg ON recipient = ens_agg.address
    WHERE
      block_time >= TIMESTAMP '{{week_start_date}}' AND block_time < TIMESTAMP '{{week_start_date}}' + INTERVAL '{{interval}}' DAY --optimal interval is 7 days
    GROUP BY
      recipient, DATE(block_time), contract_address, ens_name
    HAVING
      COUNT(*) > {{withdrawal_count_threshold}} --optimal threshold is 3, unit must be greater than zero
    ORDER BY
      source,COUNT(*) DESC
```
</details>

### Static Demo Prototype on ObservableHQ

<iframe width="100%" height="1000" frameborder="0"
  src="https://observablehq.com/embed/@cipher2-ws/tornado_weekly_demo?cell=*">
</iframe>

## Modularity: Input Parameters

Currently, the instance is configured to accept user inputs of: "**week_start_date**", "**chain**", "**interval**", and "**withdrawal_count_threshold**". A summary of input parameters is listed below:

**Input Parameters**: 
- "**week_start_date**": Analogous to "week to search for" and will return the flows within the user-specified interval.
- "**chain**": The blockchain to observe Tornado Cash activities on.
- "**interval**": User-specified day length time interval.
- "**withdrawal_count_threshold**": Minimum number of withdrawals per destination (default/recommended is 3).

The instance currently functions on a singular chain at a time, but could be configured for multiple chains at once. However, initial testing showed the visualization of multiple chains at intervals exceeding a singular day term, typically became convoluted and difficult to read/utilize. Furthermore, the tool is currently in a versatile state, such that switching between relays or recipients as destinations can be accomplished by using a search and replace all for the four interchangeable occurrences of "tx_from" or "recipient".

## Continued Development

A variety of different visualization libraries and programming languages are currently being tested for performance, efficiency, and scalability.
