# 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.