From 3f35f627b9151de7111b094314c13985843e173c Mon Sep 17 00:00:00 2001 From: jh <0xroll.onchain@gmail.com> Date: Sat, 9 Dec 2023 23:56:40 +0800 Subject: [PATCH 01/11] add sample query --- docs/resources/index.md | 6 ++ docs/resources/sample-queries.md | 115 +++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 docs/resources/sample-queries.md diff --git a/docs/resources/index.md b/docs/resources/index.md index 7e32e1dc..c971572f 100644 --- a/docs/resources/index.md +++ b/docs/resources/index.md @@ -13,6 +13,12 @@ A collection of resources for the Dune community. [→ Citing Dune](citing-dune.md) +- #### Exploring frequently used queries on Dune + + Walkthrough of commonly used queries + + [→ Sample Queries](sample-queries.md) + - #### Find a Wizard Discover and connect with skilled builders and researchers in the Ethereum ecosystem. diff --git a/docs/resources/sample-queries.md b/docs/resources/sample-queries.md new file mode 100644 index 00000000..25ab69c5 --- /dev/null +++ b/docs/resources/sample-queries.md @@ -0,0 +1,115 @@ +--- +title: Sample Queries +description: Learn how to make use of popular tables and spells on Dune +--- + +# Sample Queries + +Dune is a platform that provides tools for querying, visualizing, and analyzing data from various chains. However, this can be difficult for new users that are not familiar with SQL/on chain data! Hence this guide will help you on your journey becoming a Dune wizard. + +Refer to [this dashboard](https://dune.com/resident_wizards/sample-queries){:target="_blank"}, which is a collection of commonly requested queries over time, and this list will continue to grow! Don't find the query that you need? Check out the [Dune AI](https://dune.com/ai){:target="_blank"} or head down to our [Discord](https://discord.gg/dunecom). + + + +### Getting Total Supply Of ERC20 Token + +[Getting Total Supply of ERC20 Token Query](https://dune.com/queries/3265730){:target="_blank"} + +To get the total supply of ERC20 token, we can make use of the erc20's Transfer event that is emitted on every transfers. + +For every token that are minted, there will be an event emitted from these transfers from the blackhole address(`0x0000000000000000000000000000000000000000`), and when tokens are burned, they will usually get sent to the similar address as well. + +``` +SELECT SUM(case when "from" = 0x0000000000000000000000000000000000000000 THEN (value/1e18) ELSE -(value/1e18) END) as lp_supply +from erc20_ethereum.evt_transfer +where contract_address = 0x046eee2cc3188071c02bfc1745a6b17c656e3f3d -- erc20 token address +and ("from" = 0x0000000000000000000000000000000000000000 or "to" = 0x0000000000000000000000000000000000000000) +-- to filter transfers that are sent from or receiving from the blackhole address +``` + +From the query above, we can make use of the `erc20_ethereum.evt_transfer` table, to get the total supply. As the `erc20_ethereum.evt_transfer` table contains all erc20 tokens' transfer, you have to filter by `contract_address`,sender(`from`) and recipient(`to`). + + +Next, you'll need to sum up all the transfer events, adding when token is minted `"from" = 0x0000000000000000000000000000000000000000` and subtracting when token is burned(sent to blackhole address(`to = 0x0000000000000000000000000000000000000000`)) + +This is done here, using `CASE` function to get the aggregated amount. + +``` +SELECT SUM(case when "from" = 0x0000000000000000000000000000000000000000 THEN (value/1e18) ELSE -(value/1e18) END) +``` + +You can then verify the token supply on blockchain explorer! + +### Getting All Token Holders Of ERC20 Token + +[Getting All ERC20 Token Holders' Balance](https://dune.com/queries/2753189){:target="_blank"} + +Similarly to getting total supply of ERC20 token, we will make use of the erc20's Transfer event table to achieve this. + +For every erc20 transfers that each wallet received / sent out, we can just aggregate them up and grouping by the wallet address. + +``` +SELECT address, + symbol, + SUM(amount) as balance +FROM ( +-- transactions that address having token outflows +SELECT tr."from" AS address, + symbol, + contract_address, + -(tr.value/POW(10,decimals)) AS amount +FROM erc20_ethereum.evt_transfer tr JOIN tokens.erc20 USING (contract_address) +WHERE "from" != 0x0000000000000000000000000000000000000000 -- exclude mint/burn addresses +AND contract_address = 0x046eee2cc3188071c02bfc1745a6b17c656e3f3d -- contract_address of the erc20 token +UNION ALL +-- transactions that address having token inflows +SELECT tr."to" AS address, + symbol, + contract_address, + (tr.value/POW(10,decimals)) AS amount +FROM erc20_ethereum.evt_transfer tr JOIN tokens.erc20 USING (contract_address) + WHERE "to" != 0x0000000000000000000000000000000000000000 -- exclude mint/burn addresses + AND contract_address = 0x046eee2cc3188071c02bfc1745a6b17c656e3f3d -- contract_address of the erc20 token + ) x + GROUP BY address,symbol + HAVING SUM(amount) > 0.1 -- having more than 0.1 balance + ORDER BY 3 DESC +``` + +``` +FROM erc20_ethereum.evt_transfer tr JOIN tokens.erc20 USING (contract_address) +``` + +As token decimals varies, we can make use of the `tokens.erc20` table, which contains the token mapping to get the `symbol` and `decimals`. +We then divide the value by the token decimals(`tr.value/POW(10,decimals)`) to get the amount. + +To get the balance of each user, you'll need to get to sum up inflows(`"to"`) and subtracting outflows(`"from"`) to get the current balance. +We then aggregate all the values from the events,to get the current balance of each holder(`GROUP BY address,symbol`). + +- You can also add in a filter to remove holders that are holding dust amount (` HAVING SUM(amount) > 0.1`) +- You can also add in a filter to get the top X holders (`LIMIT 100`) at the end of query + + + + + + + + + + + + + + + + + + + + + + + + + From d4dc3e56e5f8741e55a0d2ff2b876c79acc80653 Mon Sep 17 00:00:00 2001 From: jh <0xroll.onchain@gmail.com> Date: Sun, 10 Dec 2023 02:22:18 +0800 Subject: [PATCH 02/11] update more sample queries --- docs/data-tables/decoded/.pages | 1 - docs/resources/sample-queries.md | 154 ++++++++++++++++++++++++++++++- 2 files changed, 153 insertions(+), 2 deletions(-) diff --git a/docs/data-tables/decoded/.pages b/docs/data-tables/decoded/.pages index b3ef187c..34f00678 100644 --- a/docs/data-tables/decoded/.pages +++ b/docs/data-tables/decoded/.pages @@ -1,6 +1,5 @@ title: Decoded Tables nav: - index.md -- new_guide.md - EVM: evm - Solana: solana \ No newline at end of file diff --git a/docs/resources/sample-queries.md b/docs/resources/sample-queries.md index 25ab69c5..794bd6e1 100644 --- a/docs/resources/sample-queries.md +++ b/docs/resources/sample-queries.md @@ -7,7 +7,7 @@ description: Learn how to make use of popular tables and spells on Dune Dune is a platform that provides tools for querying, visualizing, and analyzing data from various chains. However, this can be difficult for new users that are not familiar with SQL/on chain data! Hence this guide will help you on your journey becoming a Dune wizard. -Refer to [this dashboard](https://dune.com/resident_wizards/sample-queries){:target="_blank"}, which is a collection of commonly requested queries over time, and this list will continue to grow! Don't find the query that you need? Check out the [Dune AI](https://dune.com/ai){:target="_blank"} or head down to our [Discord](https://discord.gg/dunecom). +Refer to this [dashboard](https://dune.com/resident_wizards/sample-queries){:target="_blank"}, a collection of commonly requested queries over time, and this list will continue to grow! Don't find the query that you need? Check out the [Dune AI](https://dune.com/ai){:target="_blank"} or head down to our [Discord](https://discord.gg/dunecom). @@ -40,6 +40,8 @@ SELECT SUM(case when "from" = 0x0000000000000000000000000000000000000000 THEN (v You can then verify the token supply on blockchain explorer! + + ### Getting All Token Holders Of ERC20 Token [Getting All ERC20 Token Holders' Balance](https://dune.com/queries/2753189){:target="_blank"} @@ -89,6 +91,156 @@ We then aggregate all the values from the events,to get the current balance of e - You can also add in a filter to remove holders that are holding dust amount (` HAVING SUM(amount) > 0.1`) - You can also add in a filter to get the top X holders (`LIMIT 100`) at the end of query +### Getting Number Of ERC20 Token Holders Over Time + +[Getting Number Of ERC20 Token Holders Over Time](https://dune.com/queries/2749329){:target="_blank"} + +Getting the number of holders over time will require you to modify [the previous example](#current-erc20-holder). From the transfer events, you will only get records of addresses that have transfer events on the particular day. To ensure that there will be no gaps in between, you will need to generate a date series and backfill with the previous date's data. + +``` +WITH user_balance AS ( +SELECT date, + address, + SUM(SUM(amount)) OVER (PARTITION BY address ORDER BY date) as daily_cumulative_balance -- get the cumulative balance of the holders +FROM ( +SELECT date_trunc('day',evt_block_time) as date, + tr."from" AS address, + -(tr.value/1e18) AS amount +FROM erc20_ethereum.evt_transfer tr +WHERE "from" != 0x0000000000000000000000000000000000000000 -- exclude mint/burn addresses +AND contract_address = 0x046eee2cc3188071c02bfc1745a6b17c656e3f3d -- contract_address of the erc20 token +UNION ALL +SELECT date_trunc('day',evt_block_time) as date, +tr."to" AS address, +(tr.value/1e18) AS amount +FROM erc20_ethereum.evt_transfer tr + WHERE "to" != 0x0000000000000000000000000000000000000000 -- exclude mint/burn addresses + AND contract_address = 0x046eee2cc3188071c02bfc1745a6b17c656e3f3d -- contract_address of the erc20 token + ) x + GROUP BY date,address +), + +setLeadData as ( +SELECT *,lead(date,1,NOW()) OVER (PARTITION BY address ORDER BY date) as latest_day FROM user_balance +), + +gs as ( +SELECT date FROM UNNEST(sequence(CAST('2023-06-28' AS TIMESTAMP),CAST(NOW() AS TIMESTAMP),interval '1' day)) as tbl(date) +), + +getUserDailyBalance as ( +SELECT gs.date, + address, + daily_cumulative_balance +FROM setLeadData g +INNER JOIN gs ON g.date <= gs.date AND gs.date < g.latest_day +WHERE daily_cumulative_balance > 1/1e12 +) + +SELECT date, + COUNT(address) as holders, + COUNT(address) - LAG(COUNT(address)) OVER (ORDER BY date) as change +FROM getUserDailyBalance +GROUP BY 1 +ORDER BY 1 DESC +``` + +``` +SUM(SUM(amount)) OVER (PARTITION BY address ORDER BY date) as daily_cumulative_balance +``` + +This windows function will sum up all the amount(`token transfer amount`) cumulatively, to get the token balance of each address on that particular day, which you have to aggregate them by date and address (`GROUP BY date,address`) + +``` +setLeadData as ( +SELECT *,lead(date,1,NOW()) OVER (PARTITION BY address ORDER BY date) as latest_day FROM user_balance +), + +gs as ( +SELECT date FROM UNNEST(sequence(CAST('2023-06-28' AS TIMESTAMP),CAST(NOW() AS TIMESTAMP),interval '1' day)) as tbl(date) +), +``` + +- `setLeadData`: This will get the next available date of the address (`PARTITION BY address`), in an ascending date order (`ORDER BY date`) +- `gs`: This will help you to generate a date series, which you can set the start and end date of the timeframe, in the above example its `2023-06-28` as start date and `NOW()` as current date + +``` +getUserDailyBalance as ( +SELECT gs.date, + address, + daily_cumulative_balance +FROM setLeadData g +INNER JOIN gs ON g.date <= gs.date AND gs.date < g.latest_day +WHERE daily_cumulative_balance > 0.01 +) +``` +With the generated date series(`gs` CTE), you will just have to join it with the `setLeadData` CTE to get the daily balance of each address since inception! + +- You can also add in a filter to remove dusts (`WHERE daily_cumulative_balance > 0.01`) + +Once you have the daily balance of each user, you can now get the daily number of holders by simply aggregating the number of addresses on each day. You can also get the daily change in token holders by taking each day's holder count and subtracting previous day's holder count (`COUNT(address) - LAG(COUNT(address)) OVER (ORDER BY date) as change `) + +### Getting ERC20 Token Transaction Value In USD + +[Top 100 Transfers In USD Into Binance](https://dune.com/queries/3269085){:target="_blank"} + +You are able to get the token amount for transfers from the `erc20 evt transfer` table, to get the usd amount, you will need to join with the prices table (`prices.usd`) + +``` +select "from" as sender, + symbol, + value/POW(10,decimals) as token_amount, + value/POW(10,decimals) * price as token_usd +from erc20_ethereum.evt_transfer a +LEFT JOIN prices.usd b ON date_trunc('minute',a.evt_block_time) = b.minute AND a.contract_address = b.contract_address +where "to" = 0x28c6c06298d514db089934071355e5743bf21d60 +and evt_block_time >= NOW() - interval '24' hour +ORDER BY 4 DESC +LIMIT 1000 +``` + +The above query shows you the top 1000 deposits in the past 24 hours (`evt_block_time >= NOW() - interval '24' hour`). You can simply just do a JOIN with the `prices.usd` table, with the erc20 token contract address (`contract_address`) and datetime of the transaction (`date_trunc('day',evt_block_time)`). + +- The `date_trunc` here is required as there are milliseconds in the `evt_block_time`, to match the minute-level data in `prices.usd` + + +### Getting Current NFT Holders For A Specific Collection + +[Getting Current NFT Holders](https://dune.com/queries/2858082){:target="_blank"} + +You can make use of the `erc721_ethereum.evt_trasnfer` table to get the holders of a NFT collection. The example below identifies the current holders of Bored Ape Yacht Club(BAYC) collection. + +``` +SELECT "to" as address, + COUNT(*) as nft_count, + ARRAY_AGG(tokenId) as nft_list +FROM ( +select contract_address, + to, + tokenId, + ROW_NUMBER() OVER (PARTITION BY tokenId ORDER BY evt_block_time DESC,evt_index DESC) as rn +from erc721_ethereum.evt_transfer +where contract_address = 0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D -- bayc address +) x +WHERE rn = 1 +GROUP BY 1 +``` + +For ERC721 nft token, each of them have a unique token number (`tokenId`). + +From the query above, what it does is: +- get the recipients (`to`) +- get the token number (`tokenId`) +- create a sequential numbering of rows to rank the deposits using `ROW_NUMBER()` windows function +- to get the latest transfer of each token (`PARTITION BY tokenId ORDER BY evt_block_time DESC,evt_index DESC`) + +We will just need to identify the latest recipient of each NFT token and we will just remove all other transactions (`WHERE rn = 1`). +Aggregate by address will get you the current holder of BAYC collection! + + + + + From 44bb41cea5e7e5fe9d6a2049a0b618257bd8ae48 Mon Sep 17 00:00:00 2001 From: jh <0xroll.onchain@gmail.com> Date: Sun, 10 Dec 2023 02:41:09 +0800 Subject: [PATCH 03/11] update query overview for each --- docs/resources/sample-queries.md | 52 ++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/docs/resources/sample-queries.md b/docs/resources/sample-queries.md index 794bd6e1..f84cf624 100644 --- a/docs/resources/sample-queries.md +++ b/docs/resources/sample-queries.md @@ -27,7 +27,13 @@ and ("from" = 0x0000000000000000000000000000000000000000 or "to" = 0x00000000000 -- to filter transfers that are sent from or receiving from the blackhole address ``` -From the query above, we can make use of the `erc20_ethereum.evt_transfer` table, to get the total supply. As the `erc20_ethereum.evt_transfer` table contains all erc20 tokens' transfer, you have to filter by `contract_address`,sender(`from`) and recipient(`to`). +What this query does: + +- Get all erc20 token transfer that originates and received by the blackhole address + +- Check if it is minted from blackhole address, sum them up, if not subtract them + +We can make use of the `erc20_ethereum.evt_transfer` table, to get the total supply. As the `erc20_ethereum.evt_transfer` table contains all erc20 tokens' transfer, you have to filter by `contract_address`,sender(`from`) and recipient(`to`). Next, you'll need to sum up all the transfer events, adding when token is minted `"from" = 0x0000000000000000000000000000000000000000` and subtracting when token is burned(sent to blackhole address(`to = 0x0000000000000000000000000000000000000000`)) @@ -78,6 +84,14 @@ FROM erc20_ethereum.evt_transfer tr JOIN tokens.erc20 USING (contract_address) ORDER BY 3 DESC ``` +What this query does: + +- Get all erc20 token transfer but exclude blackhole address + +- Get the token decimals from tokens.erc20 table + +- Get the sum inflows and subtract outflows + ``` FROM erc20_ethereum.evt_transfer tr JOIN tokens.erc20 USING (contract_address) ``` @@ -145,6 +159,14 @@ GROUP BY 1 ORDER BY 1 DESC ``` +What this query does: + +- Get addresses' balance of available dates + +- Generate date series to ensure no gaps in between + +- Subtract previous day data to get the daily change + ``` SUM(SUM(amount)) OVER (PARTITION BY address ORDER BY date) as daily_cumulative_balance ``` @@ -174,6 +196,7 @@ INNER JOIN gs ON g.date <= gs.date AND gs.date < g.latest_day WHERE daily_cumulative_balance > 0.01 ) ``` + With the generated date series(`gs` CTE), you will just have to join it with the `setLeadData` CTE to get the daily balance of each address since inception! - You can also add in a filter to remove dusts (`WHERE daily_cumulative_balance > 0.01`) @@ -199,6 +222,14 @@ ORDER BY 4 DESC LIMIT 1000 ``` +What this query does: + +- Get token transfers into Binance 14 Hot Wallet + +- Join the prices table to get token price + +- Filter to only get latest 24 hour data and limiting to 1000 records + The above query shows you the top 1000 deposits in the past 24 hours (`evt_block_time >= NOW() - interval '24' hour`). You can simply just do a JOIN with the `prices.usd` table, with the erc20 token contract address (`contract_address`) and datetime of the transaction (`date_trunc('day',evt_block_time)`). - The `date_trunc` here is required as there are milliseconds in the `evt_block_time`, to match the minute-level data in `prices.usd` @@ -208,7 +239,7 @@ The above query shows you the top 1000 deposits in the past 24 hours (`evt_block [Getting Current NFT Holders](https://dune.com/queries/2858082){:target="_blank"} -You can make use of the `erc721_ethereum.evt_trasnfer` table to get the holders of a NFT collection. The example below identifies the current holders of Bored Ape Yacht Club(BAYC) collection. +You can make use of the `erc721_ethereum.evt_trasnfer` table to get the holders of a NFT collection. The example below identifies the current holders of Bored Ape Yacht Club(BAYC) collection. For ERC721 nft token, each of them have a unique token number (`tokenId`). ``` SELECT "to" as address, @@ -226,16 +257,19 @@ WHERE rn = 1 GROUP BY 1 ``` -For ERC721 nft token, each of them have a unique token number (`tokenId`). +What this query does: + +- Get the recipients (`to`) + +- Get the token number (`tokenId`) -From the query above, what it does is: -- get the recipients (`to`) -- get the token number (`tokenId`) -- create a sequential numbering of rows to rank the deposits using `ROW_NUMBER()` windows function -- to get the latest transfer of each token (`PARTITION BY tokenId ORDER BY evt_block_time DESC,evt_index DESC`) +- Create a sequential numbering of rows to rank the deposits using `ROW_NUMBER()` windows function + +- To get the latest transfer of each token (`PARTITION BY tokenId ORDER BY evt_block_time DESC,evt_index DESC`) We will just need to identify the latest recipient of each NFT token and we will just remove all other transactions (`WHERE rn = 1`). -Aggregate by address will get you the current holder of BAYC collection! + +Aggregate by address will get you all current holders of BAYC collection! From e4365caef56110a16464f1c620b6f2372faf82cf Mon Sep 17 00:00:00 2001 From: jh <0xroll.onchain@gmail.com> Date: Sun, 10 Dec 2023 03:01:59 +0800 Subject: [PATCH 04/11] add eth query --- docs/resources/sample-queries.md | 45 ++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/docs/resources/sample-queries.md b/docs/resources/sample-queries.md index f84cf624..9f11392b 100644 --- a/docs/resources/sample-queries.md +++ b/docs/resources/sample-queries.md @@ -5,8 +5,6 @@ description: Learn how to make use of popular tables and spells on Dune # Sample Queries -Dune is a platform that provides tools for querying, visualizing, and analyzing data from various chains. However, this can be difficult for new users that are not familiar with SQL/on chain data! Hence this guide will help you on your journey becoming a Dune wizard. - Refer to this [dashboard](https://dune.com/resident_wizards/sample-queries){:target="_blank"}, a collection of commonly requested queries over time, and this list will continue to grow! Don't find the query that you need? Check out the [Dune AI](https://dune.com/ai){:target="_blank"} or head down to our [Discord](https://discord.gg/dunecom). @@ -271,6 +269,49 @@ We will just need to identify the latest recipient of each NFT token and we will Aggregate by address will get you all current holders of BAYC collection! +### Getting the current ETH balance + +``` + -- outbound transfers + SELECT SUM(amount) as current_eth_balance FROM ( + SELECT "from" AS address, + -CAST(tr.value AS DOUBLE)/POWER(10,18) AS amount, + tx_hash + FROM ethereum.traces tr + WHERE "from" = 0x29ffea86733d7feac7c353343f300e99b8910c77 + AND success + AND (call_type NOT IN ('delegatecall', 'callcode', 'staticcall') OR call_type IS null) + + UNION ALL + + -- inbound transfers + SELECT to AS address, (CAST(value AS DOUBLE)/POWER(10,18)) AS amount,tx_hash + FROM ethereum.traces + WHERE to = 0x29ffea86733d7feac7c353343f300e99b8910c77 + AND success + AND (call_type NOT IN ('delegatecall', 'callcode', 'staticcall') OR call_type IS null) + + UNION ALL + + -- gas costs + SELECT "from" AS address, -(CAST(gas_used AS DOUBLE)/POWER(10,18) * CAST(gas_price AS DOUBLE)),hash + FROM ethereum.transactions et + WHERE "from" = 0x29ffea86733d7feac7c353343f300e99b8910c77 + ) x +``` + +What this query does: + +- Get all outbound and inbound transfers from `traces` table + +- Get all gas costs from the `transactions` table + +- Aggregate all transfers, summing up the inflows and subtracting outflows and gas fee + +For ETH on mainnet, you will have to use the `ethereum.traces`. They do not appear in erc20 transfer table as it is not an erc20 token. + + + From 63b424f87160ce61f131ef0a3d7cc6eddb5cc719 Mon Sep 17 00:00:00 2001 From: jh <0xroll.onchain@gmail.com> Date: Sun, 10 Dec 2023 04:06:03 +0800 Subject: [PATCH 05/11] adding samples --- .../images/etherscan-logs-event-sample.png | Bin 0 -> 22066 bytes docs/resources/images/etherscan_logs.png | Bin 0 -> 23122 bytes docs/resources/sample-queries.md | 118 +++++++++++++----- 3 files changed, 88 insertions(+), 30 deletions(-) create mode 100644 docs/resources/images/etherscan-logs-event-sample.png create mode 100644 docs/resources/images/etherscan_logs.png diff --git a/docs/resources/images/etherscan-logs-event-sample.png b/docs/resources/images/etherscan-logs-event-sample.png new file mode 100644 index 0000000000000000000000000000000000000000..3011a2f8dd2bb63195cf3938416063d7b4179c6e GIT binary patch literal 22066 zcmce;byQqWwxjRQjz=sC>tT(gI>Hg6O|VQ04k%AAC2Ik*NFB~T224}TGyYySABK`CIEnX zpR~BBn!El{%Yz+`WaG)O-5t7uZ@53mypuIpN-{N;M@HW{iXuCG%)~XjSsKL0I}jBJ zVP3AFwAmcUSYv<7#v|q*FJc%Z*d@Olhyp7uRWCq=5_-?wtALt1Ff9RBh17SW&XTwK<59lyt`)Gv9|Nll1^G_ddjiIv=RlWj< z#D2jaMj3&Dg^FN-0sw@eben&&$Hj-jmR ziO2B)0BnMgv0tCz-~lo4US+$-M^-0?wiYMEeh|;X>xF;-Vtjz1KMZulAzV;80I+H} zisSPH8xSDoc5>e15MY@D?RJg!PI?X*kQ5>k@d@7RixDjVz>_eiD_WG$ZEkjhDuh5k z%j*?qc͛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�<{yr=nLG;wemp2k3<`a(b*U59>iEWspP;gS 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 literal 0 HcmV?d00001 diff --git a/docs/resources/images/etherscan_logs.png b/docs/resources/images/etherscan_logs.png new file mode 100644 index 0000000000000000000000000000000000000000..2ed674d8f3b869ebcd8a31084cef8f350328efc7 GIT binary patch literal 23122 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 0.1`) - You can also add in a filter to get the top X holders (`LIMIT 100`) at the end of query +### Getting Current ETH balance + +``` + -- outbound transfers + SELECT SUM(amount) as current_eth_balance FROM ( + SELECT "from" AS address, + -CAST(tr.value AS DOUBLE)/POWER(10,18) AS amount, + tx_hash + FROM ethereum.traces tr + WHERE "from" = 0x29ffea86733d7feac7c353343f300e99b8910c77 + AND success + AND (call_type NOT IN ('delegatecall', 'callcode', 'staticcall') OR call_type IS null) + + UNION ALL + + -- inbound transfers + SELECT to AS address, (CAST(value AS DOUBLE)/POWER(10,18)) AS amount,tx_hash + FROM ethereum.traces + WHERE to = 0x29ffea86733d7feac7c353343f300e99b8910c77 + AND success + AND (call_type NOT IN ('delegatecall', 'callcode', 'staticcall') OR call_type IS null) + + UNION ALL + + -- gas costs + SELECT "from" AS address, -(CAST(gas_used AS DOUBLE)/POWER(10,18) * CAST(gas_price AS DOUBLE)),hash + FROM ethereum.transactions et + WHERE "from" = 0x29ffea86733d7feac7c353343f300e99b8910c77 + ) x +``` + +What this query does: + +- Get all outbound and inbound transfers from `traces` table + +- Get all gas costs from the `transactions` table + +- Aggregate all transfers, summing up the inflows and subtracting outflows and gas fee + +For ETH on mainnet, you will have to use the `ethereum.traces`. They do not appear in erc20 transfer table as it is not an erc20 token. + ### Getting Number Of ERC20 Token Holders Over Time [Getting Number Of ERC20 Token Holders Over Time](https://dune.com/queries/2749329){:target="_blank"} @@ -269,46 +310,63 @@ We will just need to identify the latest recipient of each NFT token and we will Aggregate by address will get you all current holders of BAYC collection! -### Getting the current ETH balance +### Using Logs Table For Specific Event + +[$ARKM Claimers From Logs Table](https://dune.com/queries/3269288){:target="_blank"} ``` - -- outbound transfers - SELECT SUM(amount) as current_eth_balance FROM ( - SELECT "from" AS address, - -CAST(tr.value AS DOUBLE)/POWER(10,18) AS amount, - tx_hash - FROM ethereum.traces tr - WHERE "from" = 0x29ffea86733d7feac7c353343f300e99b8910c77 - AND success - AND (call_type NOT IN ('delegatecall', 'callcode', 'staticcall') OR call_type IS null) +SELECT block_time, + varbinary_ltrim(topic1) as address, + varbinary_to_uint256(topic2)/1e18 as amount_claimed +FROM ethereum.logs +where contract_address = 0x08c7676680f187a31241e83e6d44c03a98adab05 +and topic0 = 0xd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a +order by 3 desc +``` - UNION ALL +What this query does: - -- inbound transfers - SELECT to AS address, (CAST(value AS DOUBLE)/POWER(10,18)) AS amount,tx_hash - FROM ethereum.traces - WHERE to = 0x29ffea86733d7feac7c353343f300e99b8910c77 - AND success - AND (call_type NOT IN ('delegatecall', 'callcode', 'staticcall') OR call_type IS null) +- Querying logs table by filtering the contract address (`contract_address`) and event signature(`topic0`) - UNION ALL - - -- gas costs - SELECT "from" AS address, -(CAST(gas_used AS DOUBLE)/POWER(10,18) * CAST(gas_price AS DOUBLE)),hash - FROM ethereum.transactions et - WHERE "from" = 0x29ffea86733d7feac7c353343f300e99b8910c77 - ) x -``` +- Using varbinary functions to get the decoded data -What this query does: +The `ethereum.logs` table is one of the raw tables available that contains all events that are emitted. You will need to specify the contract address and topic0 to get the specific events you need. Each event has a unique signature (`topic0`), that you will be able to get this the logs page. [Here is a sample transaction hash of $ARKM claim](https://etherscan.io/tx/0xc127438f51ae6917d7b1b7709d50049162ae41bdac4201b5658cd9cc01c9c591){:target="_blank"}. -- Get all outbound and inbound transfers from `traces` table +

+
+

+ + Click on the `Logs` tab. + +

+
+

+ + In the `Claim` event, you will be able to identify: + + - Address : `contract_address` + + - Event signature : `topic0` + + - Account : `topic1` + + - Amount : `topic2` + + + ``` + varbinary_ltrim(topic1) as address, -- removes the zeros + varbinary_to_uint256(topic2)/1e18 as amount_claimed -- converting varbinary to uint256 + ``` + + In the `logs` table, they are raw data hence you will need to use [varbinary functions](https://dune.com/docs/query/DuneSQL-reference/Functions-and-operators/varbinary/?h=bytearray_to_uint#varbinary-functions){:target="_blank"} to decode them. + + Since $ARKM token has 18 decimals, we use `/1e18` to get the actual amount that each address claimed. + + It is advisable to get the contracts to be [decoded at Dune](https://dune.com/contracts/new){:target="_blank"} as querying from the raw tables can take a long time as they contain all the events emitted! To understand more about decoded tables, check out our [Decoding Docs](https://dune.com/docs/app/decoding-contracts/){:target="_blank"} + -- Get all gas costs from the `transactions` table -- Aggregate all transfers, summing up the inflows and subtracting outflows and gas fee -For ETH on mainnet, you will have to use the `ethereum.traces`. They do not appear in erc20 transfer table as it is not an erc20 token. From 595a6d4b4653eac745beddf3fbb33b7b999a3606 Mon Sep 17 00:00:00 2001 From: jh <0xroll.onchain@gmail.com> Date: Sun, 10 Dec 2023 04:50:37 +0800 Subject: [PATCH 06/11] add sample data --- .../images/etherscan-logs-event-data.png | Bin 0 -> 27893 bytes docs/resources/sample-queries.md | 70 ++++++++++++++++-- 2 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 docs/resources/images/etherscan-logs-event-data.png diff --git a/docs/resources/images/etherscan-logs-event-data.png b/docs/resources/images/etherscan-logs-event-data.png new file mode 100644 index 0000000000000000000000000000000000000000..93f21bd2f8d19e43d3124c59549b0f559a4a4103 GIT binary patch literal 27893 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 literal 0 HcmV?d00001 diff --git a/docs/resources/sample-queries.md b/docs/resources/sample-queries.md index 707bfcdf..713d2e3c 100644 --- a/docs/resources/sample-queries.md +++ b/docs/resources/sample-queries.md @@ -5,7 +5,7 @@ description: Learn how to make use of popular tables and spells on Dune # Sample Queries -Refer to this [dashboard](https://dune.com/resident_wizards/sample-queries){:target="_blank"}, a collection of commonly requested queries over time, and this list will continue to grow! Don't find the query that you need? Check out the [Dune AI](https://dune.com/ai){:target="_blank"} or head down to our [Discord](https://discord.gg/dunecom). +Refer to this [Dune Dashboard](https://dune.com/resident_wizards/sample-queries){:target="_blank"}, a collection of commonly requested queries over time, and this list will continue to grow! Don't find the query that you need? Check out the [Dune AI](https://dune.com/ai){:target="_blank"} or head down to our [Discord](https://discord.gg/dunecom). @@ -310,7 +310,7 @@ We will just need to identify the latest recipient of each NFT token and we will Aggregate by address will get you all current holders of BAYC collection! -### Using Logs Table For Specific Event +### Querying Logs Table For Specific Event (Topics) [$ARKM Claimers From Logs Table](https://dune.com/queries/3269288){:target="_blank"} @@ -354,15 +354,75 @@ The `ethereum.logs` table is one of the raw tables available that contains all e ``` - varbinary_ltrim(topic1) as address, -- removes the zeros - varbinary_to_uint256(topic2)/1e18 as amount_claimed -- converting varbinary to uint256 + varbinary_ltrim(topic1) as address, -- removes the zeros + varbinary_to_uint256(topic2)/1e18 as amount_claimed -- converting varbinary to uint256 ``` In the `logs` table, they are raw data hence you will need to use [varbinary functions](https://dune.com/docs/query/DuneSQL-reference/Functions-and-operators/varbinary/?h=bytearray_to_uint#varbinary-functions){:target="_blank"} to decode them. Since $ARKM token has 18 decimals, we use `/1e18` to get the actual amount that each address claimed. - It is advisable to get the contracts to be [decoded at Dune](https://dune.com/contracts/new){:target="_blank"} as querying from the raw tables can take a long time as they contain all the events emitted! To understand more about decoded tables, check out our [Decoding Docs](https://dune.com/docs/app/decoding-contracts/){:target="_blank"} + It is advisable to get the contracts to be [decoded at Dune](https://dune.com/contracts/new){:target="_blank"} as querying from the raw tables can take a long time as they contain all the events emitted! To understand more about decoded tables, check out our [Decoding Docs](https://dune.com/docs/app/decoding-contracts/){:target="_blank"}. + + +### Querying Logs Table For Specific Event (Topics and Data) + +[Swap Event From Logs Table](https://dune.com/queries/3269338){:target="_blank"} + +``` +select varbinary_ltrim(topic1) as sender, -- topic1 + varbinary_ltrim(topic2) as recipient, -- topic2 + varbinary_to_int256(varbinary_substring(data,1,32)) as amount0, -- int256 + varbinary_to_int256(varbinary_substring(data,33,32)) as amount1, -- int256 + varbinary_to_uint256(varbinary_substring(data,65,32)) as price, -- uint160 + varbinary_to_uint256(varbinary_substring(data,97,32)) as liquidity, -- uint128 + varbinary_to_int256(varbinary_substring(data,129,32)) as tick -- int24 +from arbitrum.logs +where contract_address = 0x3ab5dd69950a948c55d1fbfb7500bf92b4bd4c48 +and topic0 = 0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67 +and tx_hash = 0x0807434b7d837f5feffe4cf3ee0c024bf38ebf58372dcb0e77f9b61b04ee6a1f +``` + +

+
+

+ +What this query does: + +- Querying logs table by filtering the contract address (`contract_address`) and event signature(`topic0`) + +- Using varbinary functions to get the decoded data from both topics and data column + +In the `Swap` event, you will be able to identify: + +- Sender : `topic1` + +- Recipient : `topic2` + +- amount0,amount1,price,liquidity,tick : `data` + +In the `data` column, it contains all the data payload for `amount0,amount1,price,liquidity,tick`. We will have to make use of the `varbinary functions` to slice the data and convert to the correct data type. + +``` +select varbinary_ltrim(topic1) as sender, -- topic1 + varbinary_ltrim(topic2) as recipient, -- topic2 + varbinary_to_int256(varbinary_substring(data,1,32)) as amount0, -- int256 + varbinary_to_int256(varbinary_substring(data,33,32)) as amount1, -- int256 + varbinary_to_uint256(varbinary_substring(data,65,32)) as price, -- uint160 + varbinary_to_uint256(varbinary_substring(data,97,32)) as liquidity, -- uint128 + varbinary_to_int256(varbinary_substring(data,129,32)) as tick -- int24 +``` + +The above codes make use of the [varbinary functions](https://dune.com/docs/query/DuneSQL-reference/Functions-and-operators/varbinary/?h=bytearray_to_uint#varbinary-functions){:target="_blank"} to decode. We will be able to identify which varbinary function to use based on the datatype of each variable. + + As it can get complicated as there is no restriction on the data payload, it is advisable to get the contracts to be [decoded at Dune](https://dune.com/contracts/new){:target="_blank"} as querying from the raw tables can take a long time as they contain all the events emitted! To understand more about decoded tables, check out our [Decoding Docs](https://dune.com/docs/app/decoding-contracts/){:target="_blank"}. + + + + + + + From 57a3150afc33ea96231d75084ca774ab595a9d6d Mon Sep 17 00:00:00 2001 From: jh <0xroll.onchain@gmail.com> Date: Sun, 10 Dec 2023 15:05:12 +0800 Subject: [PATCH 07/11] pushing more samples --- docs/resources/sample-queries.md | 178 ++++++++++++++++++++++++------- 1 file changed, 141 insertions(+), 37 deletions(-) diff --git a/docs/resources/sample-queries.md b/docs/resources/sample-queries.md index 713d2e3c..74e1bade 100644 --- a/docs/resources/sample-queries.md +++ b/docs/resources/sample-queries.md @@ -8,6 +8,7 @@ description: Learn how to make use of popular tables and spells on Dune Refer to this [Dune Dashboard](https://dune.com/resident_wizards/sample-queries){:target="_blank"}, a collection of commonly requested queries over time, and this list will continue to grow! Don't find the query that you need? Check out the [Dune AI](https://dune.com/ai){:target="_blank"} or head down to our [Discord](https://discord.gg/dunecom). +## EVM Queries ### Getting Total Supply Of ERC20 Token @@ -34,7 +35,7 @@ What this query does: We can make use of the `erc20_ethereum.evt_transfer` table, to get the total supply. As the `erc20_ethereum.evt_transfer` table contains all erc20 tokens' transfer, you have to filter by `contract_address`,sender(`from`) and recipient(`to`). -Next, you'll need to sum up all the transfer events, adding when token is minted `"from" = 0x0000000000000000000000000000000000000000` and subtracting when token is burned(sent to blackhole address(`to = 0x0000000000000000000000000000000000000000`)) +Next, we'll need to sum up all the transfer events, adding when token is minted `"from" = 0x0000000000000000000000000000000000000000` and subtracting when token is burned(sent to blackhole address(`to = 0x0000000000000000000000000000000000000000`)) This is done here, using `CASE` function to get the aggregated amount. @@ -42,7 +43,7 @@ This is done here, using `CASE` function to get the aggregated amount. SELECT SUM(case when "from" = 0x0000000000000000000000000000000000000000 THEN (value/1e18) ELSE -(value/1e18) END) ``` -You can then verify the token supply on blockchain explorer! +We can then verify the token supply on blockchain explorer!
@@ -97,11 +98,11 @@ FROM erc20_ethereum.evt_transfer tr JOIN tokens.erc20 USING (contract_address) As token decimals varies, we can make use of the `tokens.erc20` table, which contains the token mapping to get the `symbol` and `decimals`. We then divide the value by the token decimals(`tr.value/POW(10,decimals)`) to get the amount. -To get the balance of each user, you'll need to get to sum up inflows(`"to"`) and subtracting outflows(`"from"`) to get the current balance. +To get the balance of each user, we'll need to get to sum up inflows(`"to"`) and subtracting outflows(`"from"`) to get the current balance. We then aggregate all the values from the events,to get the current balance of each holder(`GROUP BY address,symbol`). -- You can also add in a filter to remove holders that are holding dust amount (` HAVING SUM(amount) > 0.1`) -- You can also add in a filter to get the top X holders (`LIMIT 100`) at the end of query +- We can also add in a filter to remove holders that are holding dust amount (` HAVING SUM(amount) > 0.1`) +- We can also add in a filter to get the top X holders (`LIMIT 100`) at the end of query ### Getting Current ETH balance @@ -148,7 +149,7 @@ For ETH on mainnet, you will have to use the `ethereum.traces`. They do not appe [Getting Number Of ERC20 Token Holders Over Time](https://dune.com/queries/2749329){:target="_blank"} -Getting the number of holders over time will require you to modify [the previous example](#current-erc20-holder). From the transfer events, you will only get records of addresses that have transfer events on the particular day. To ensure that there will be no gaps in between, you will need to generate a date series and backfill with the previous date's data. +Getting the number of holders over time will require you to modify [the previous example](#current-erc20-holder). From the transfer events, we will only get records of addresses that have transfer events on the particular day. To ensure that there will be no gaps in between, we will need to generate a date series and backfill with the previous date's data. ``` WITH user_balance AS ( @@ -223,7 +224,7 @@ SELECT date FROM UNNEST(sequence(CAST('2023-06-28' AS TIMESTAMP),CAST(NOW() AS T ``` - `setLeadData`: This will get the next available date of the address (`PARTITION BY address`), in an ascending date order (`ORDER BY date`) -- `gs`: This will help you to generate a date series, which you can set the start and end date of the timeframe, in the above example its `2023-06-28` as start date and `NOW()` as current date +- `gs`: This will help us to generate a date series, which we can set the start and end date of the timeframe, in the above example its `2023-06-28` as start date and `NOW()` as current date ``` getUserDailyBalance as ( @@ -273,6 +274,7 @@ The above query shows you the top 1000 deposits in the past 24 hours (`evt_block - The `date_trunc` here is required as there are milliseconds in the `evt_block_time`, to match the minute-level data in `prices.usd` + ### Getting Current NFT Holders For A Specific Collection @@ -310,6 +312,111 @@ We will just need to identify the latest recipient of each NFT token and we will Aggregate by address will get you all current holders of BAYC collection! +### Getting NFT Collection Mints And Current Status + +[NFT Mints And Status](https://dune.com/queries/3098984){:target="_blank"} + +``` +WITH nftMints as ( +select "to" as address,tokenId from erc721_ethereum.evt_transfer +where "from" = 0x0000000000000000000000000000000000000000 -- mints are from blackhole address +and contract_address = 0xbd3531da5cf5857e7cfaa92426877b022e612cf8 -- pudgy penguin address +), + +-- gets the current nft holder +currentNFTHolder as ( +SELECT "to" as address, + tokenId +FROM ( +select contract_address, + to, + tokenId, + ROW_NUMBER() OVER (PARTITION BY tokenId ORDER BY evt_block_time DESC,evt_index DESC) as rn +from erc721_ethereum.evt_transfer +where contract_address = 0xbd3531da5cf5857e7cfaa92426877b022e612cf8 -- pudgy penguin address +) x +WHERE rn = 1 +) + +SELECT address, + ARRAY_AGG(tokenId) as minted_list, -- aggregate all the nft minted + COUNT(*) as total_minted, -- count total nft minted + COUNT(*) FILTER (WHERE token_id IS NOT NULL) as hold_count, -- count current holdings + COUNT(*) FILTER (WHERE token_id IS NULL) as sold_count -- count amount sold +FROM ( +SELECT a.address,a.tokenId,b.tokenId as token_id +FROM nftMints a LEFT JOIN currentNFTHolder b ON a.address = b.address AND a.tokenId = b.tokenId +) x +GROUP BY 1 +ORDER BY 3 DESC +``` + +What this query does: + +- `nftMints` : Getting all the mint transactions + +- `currentNFTHolder` : Getting all the current NFT holder + +- Joining both CTEs to get minted nft list,total counts of mint,holding and sold of each address + +In `nftMints`, we filter the sender(`"from"`) to the blackhole address (`0x0000000000000000000000000000000000000000`) and nft contract address (`contract_address`) to only get all the nft minted transactions. + +In `currentNFTHolder`, we get all the current NFT holders of the same collection. For more details, you can refer to [the previous example](#current-nft-holder). + +``` +nftMints a LEFT JOIN currentNFTHolder b ON a.address = b.address AND a.tokenId = b.tokenId +``` + +We then join both tables up by using `LEFT JOIN`, on the condition of matching the `address` and `tokenId` in both CTE. Finally, we aggregate by the address, to get the total minted list (`ARRAY_AGG(tokenId) as minted_list`), counting the total counts of mint,holding and sold. + +### Getting Total Value Locked Of Liquidity Pool Over Time + +[Uniswap USDC-WETH LP Total Value Locked Over Time](https://dune.com/queries/3269911){:target="_blank"} + +``` +-- get weth and usdc token balance in lp +WITH DailyTokensInLP as ( +SELECT date,symbol,SUM(SUM(amount)) OVER (PARTITION BY symbol ORDER BY date) as token_amount -- sum the token balance cumulatively , group by symbol +FROM ( +SELECT date_trunc('day',evt_block_time) as date,symbol,-(value/POW(10,decimals)) as amount +FROM erc20_ethereum.evt_transfer a +LEFT JOIN tokens.erc20 b ON a.contract_address = b.contract_address and blockchain = 'ethereum' -- join the tokens.erc20 to get token symbol and decimals +where "from" = 0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc -- usdc-weth pair +and a.contract_address IN (0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2,0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48) -- filter to get usdc and weth only +UNION ALL +SELECT date_trunc('day',evt_block_time) as date,symbol,(value/POW(10,decimals)) as amount +FROM erc20_ethereum.evt_transfer a +LEFT JOIN tokens.erc20 b ON a.contract_address = b.contract_address and blockchain = 'ethereum' -- join the tokens.erc20 to get token symbol and decimals +where "to" = 0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc -- usdc-weth pair +and a.contract_address IN (0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2,0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48) -- filter to get usdc and weth only +) x +GROUP BY date,symbol +), + +getDailyPrice as ( +SELECT date_trunc('day',minute) as date, + symbol, + AVG(price) as price +FROM prices.usd +WHERE contract_address IN (0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2,0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48) -- filter to get usdc and weth only +AND blockchain = 'ethereum' -- ethereum only +GROUP BY 1,2 +) + +SELECT date,SUM(token_amount * price) as daily_tvl +FROM DailyTokensInLP LEFT JOIN getDailyPrice USING (date,symbol) +GROUP BY 1 +ORDER BY 1 DESC +``` + +What this query does: + +- In `DailyTokensInLP`, we make use of the transfer event table (`erc20_ethereum.evt_transfer`) and `tokens.erc20` to get the token symbol and the correct cumulative token amount on each day (`GROUP BY date,symbol`). + +- In `getDailyPrice`, we use the prices table (`prices.usd`) , to get the daily average prices of both tokens (`AVG(price)`). + +- Joining both CTEs to will get the token amount , daily average price daily. Aggregating them will get us the daily total value locked over time! + ### Querying Logs Table For Specific Event (Topics) [$ARKM Claimers From Logs Table](https://dune.com/queries/3269288){:target="_blank"} @@ -413,48 +520,45 @@ select varbinary_ltrim(topic1) as sender, -- topic1 varbinary_to_int256(varbinary_substring(data,129,32)) as tick -- int24 ``` -The above codes make use of the [varbinary functions](https://dune.com/docs/query/DuneSQL-reference/Functions-and-operators/varbinary/?h=bytearray_to_uint#varbinary-functions){:target="_blank"} to decode. We will be able to identify which varbinary function to use based on the datatype of each variable. +The codes above utilizes [varbinary functions](https://dune.com/docs/query/DuneSQL-reference/Functions-and-operators/varbinary/?h=bytearray_to_uint#varbinary-functions){:target="_blank"} to decode. We will be able to identify which varbinary function to use based on the datatype of each variable. As it can get complicated as there is no restriction on the data payload, it is advisable to get the contracts to be [decoded at Dune](https://dune.com/contracts/new){:target="_blank"} as querying from the raw tables can take a long time as they contain all the events emitted! To understand more about decoded tables, check out our [Decoding Docs](https://dune.com/docs/app/decoding-contracts/){:target="_blank"}. +## Solana Queries +### Getting Top 100 USDC Holders And Their Balances +[Top 100 USDC Balance](https://dune.com/queries/3269780){:target="_blank"} +``` +SELECT token_balance_owner,post_token_balance FROM ( +select token_balance_owner,post_token_balance,ROW_NUMBER() OVER (PARTITION BY token_balance_owner ORDER BY block_time DESC) as latest_change +FROM solana.account_activity +where token_mint_address = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' -- usdc address +) x +where latest_change = 1 -- filter by only getting latest balance change transaction +order by 2 desc +LIMIT 100 +``` +What this query does: +- Get all transactions that involves usdc balance change - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +- Using window functions to get the latest balance (`post_token_change`) +- Filtering to only get the latest account's token balance +``` +ROW_NUMBER() OVER (PARTITION BY token_balance_owner ORDER BY block_time DESC) +``` +Using the ROW_NUMBER() windows function will help us to identify the latest transaction that involves USDC balance change, and we filter all other transactions by doing `WHERE latest_change = 1`. This allow us to get the updated USDC amount for each wallet address. +We are also able to get the total supply of USDC on Solana by editing the query above, removing the `LIMIT 1000` and adding another windows function to get the supply. +``` +SUM(post_token_balance) OVER () +``` +The above code will aggregate every addresses' USDC balance, which gets us the total USDC supply in Solana! \ No newline at end of file From 8a3292f7223258a9fc1797a7a65b3ca9e0ecf563 Mon Sep 17 00:00:00 2001 From: jh <0xroll.onchain@gmail.com> Date: Mon, 11 Dec 2023 22:52:48 +0800 Subject: [PATCH 08/11] add some sample and fix text --- docs/resources/index.md | 2 +- docs/resources/sample-queries.md | 70 ++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/docs/resources/index.md b/docs/resources/index.md index c971572f..ba278fee 100644 --- a/docs/resources/index.md +++ b/docs/resources/index.md @@ -13,7 +13,7 @@ A collection of resources for the Dune community. [→ Citing Dune](citing-dune.md) -- #### Exploring frequently used queries on Dune +- #### Exploring Frequently Used Queries Walkthrough of commonly used queries diff --git a/docs/resources/sample-queries.md b/docs/resources/sample-queries.md index 74e1bade..01f84913 100644 --- a/docs/resources/sample-queries.md +++ b/docs/resources/sample-queries.md @@ -143,13 +143,16 @@ What this query does: - Aggregate all transfers, summing up the inflows and subtracting outflows and gas fee -For ETH on mainnet, you will have to use the `ethereum.traces`. They do not appear in erc20 transfer table as it is not an erc20 token. +For ETH on mainnet, we will have to use the `ethereum.traces`. They do not appear in erc20 transfer table as it is not an erc20 token. + + ### Getting Number Of ERC20 Token Holders Over Time [Getting Number Of ERC20 Token Holders Over Time](https://dune.com/queries/2749329){:target="_blank"} -Getting the number of holders over time will require you to modify [the previous example](#current-erc20-holder). From the transfer events, we will only get records of addresses that have transfer events on the particular day. To ensure that there will be no gaps in between, we will need to generate a date series and backfill with the previous date's data. + +Getting the number of holders over time will require us to modify [the previous example](#current-erc20-holder). From the transfer events, we will only get records of addresses that have transfer events on the particular day. To ensure that there will be no gaps in between, we will need to generate a date series and backfill with the previous date's data. ``` WITH user_balance AS ( @@ -211,7 +214,7 @@ What this query does: SUM(SUM(amount)) OVER (PARTITION BY address ORDER BY date) as daily_cumulative_balance ``` -This windows function will sum up all the amount(`token transfer amount`) cumulatively, to get the token balance of each address on that particular day, which you have to aggregate them by date and address (`GROUP BY date,address`) +This windows function will sum up all the amount(`token transfer amount`) cumulatively, to get the token balance of each address on that particular day, which we have to aggregate them by date and address (`GROUP BY date,address`) ``` setLeadData as ( @@ -237,17 +240,17 @@ WHERE daily_cumulative_balance > 0.01 ) ``` -With the generated date series(`gs` CTE), you will just have to join it with the `setLeadData` CTE to get the daily balance of each address since inception! +With the generated date series(`gs` CTE), we will just have to join it with the `setLeadData` CTE to get the daily balance of each address since inception! -- You can also add in a filter to remove dusts (`WHERE daily_cumulative_balance > 0.01`) +- We can also add in a filter to remove dusts (`WHERE daily_cumulative_balance > 0.01`) -Once you have the daily balance of each user, you can now get the daily number of holders by simply aggregating the number of addresses on each day. You can also get the daily change in token holders by taking each day's holder count and subtracting previous day's holder count (`COUNT(address) - LAG(COUNT(address)) OVER (ORDER BY date) as change `) +Once we have the daily balance of each user, we can now get the daily number of holders by simply aggregating the number of addresses on each day. We can also get the daily change in token holders by taking each day's holder count and subtracting previous day's holder count (`COUNT(address) - LAG(COUNT(address)) OVER (ORDER BY date) as change `) ### Getting ERC20 Token Transaction Value In USD [Top 100 Transfers In USD Into Binance](https://dune.com/queries/3269085){:target="_blank"} -You are able to get the token amount for transfers from the `erc20 evt transfer` table, to get the usd amount, you will need to join with the prices table (`prices.usd`) +We are able to get the token amount for transfers from the `erc20 evt transfer` table. To get the usd amount, we will need to join with the prices table (`prices.usd`) ``` select "from" as sender, @@ -280,7 +283,7 @@ The above query shows you the top 1000 deposits in the past 24 hours (`evt_block [Getting Current NFT Holders](https://dune.com/queries/2858082){:target="_blank"} -You can make use of the `erc721_ethereum.evt_trasnfer` table to get the holders of a NFT collection. The example below identifies the current holders of Bored Ape Yacht Club(BAYC) collection. For ERC721 nft token, each of them have a unique token number (`tokenId`). +We can make use of the `erc721_ethereum.evt_trasnfer` table to get the holders of a NFT collection. The example below identifies the current holders of Bored Ape Yacht Club(BAYC) collection. For ERC721 nft token, each of them have a unique token number (`tokenId`). ``` SELECT "to" as address, @@ -310,7 +313,7 @@ What this query does: We will just need to identify the latest recipient of each NFT token and we will just remove all other transactions (`WHERE rn = 1`). -Aggregate by address will get you all current holders of BAYC collection! +Aggregate by address will get us all current holders of BAYC collection! ### Getting NFT Collection Mints And Current Status @@ -369,6 +372,30 @@ nftMints a LEFT JOIN currentNFTHolder b ON a.address = b.address AND a.tokenId = We then join both tables up by using `LEFT JOIN`, on the condition of matching the `address` and `tokenId` in both CTE. Finally, we aggregate by the address, to get the total minted list (`ARRAY_AGG(tokenId) as minted_list`), counting the total counts of mint,holding and sold. +### Getting NFT Platforms Daily And Cumulative Volume Over Time + +[NFT Platform Daily And Cumulative Volume Over Time](https://dune.com/queries/2858389){:target="_blank"} + +``` +select date_trunc('day', block_time) as day, + project, + sum(amount_usd) as daily_project_amount_usd, + sum(sum(amount_usd)) OVER (PARTITION BY project ORDER BY date_trunc('day',block_time)) as project_cumulative_volume_usd +from nft.trades -- from nft.trades spell table +where block_time > now() - interval '365' day -- getting 1 year data, current - 365 days +group by 1,2 +ORDER BY 1 DESC,4 DESC +``` + +What this query does: + +- Getting daily usd volume of each nft platform in nft.trades + +- Summing up cumulatively to get the total volume processed in 365 days + +`nft.trades` is a [spell](https://github.com/duneanalytics/spellbook/tree/main/models/_sector/nft/trades){:target="_blank"} that contains most transactions from the popular NFT market. We can aggregate the daily volume (`sum(amount_usd)`) and summing the volume cumulatively (`sum(sum(amount_usd)) OVER (PARTITION BY project ORDER BY date_trunc('day',block_time))`) to identify the performance between NFT platforms' volume in the past one year. + + ### Getting Total Value Locked Of Liquidity Pool Over Time [Uniswap USDC-WETH LP Total Value Locked Over Time](https://dune.com/queries/3269911){:target="_blank"} @@ -437,7 +464,7 @@ What this query does: - Using varbinary functions to get the decoded data -The `ethereum.logs` table is one of the raw tables available that contains all events that are emitted. You will need to specify the contract address and topic0 to get the specific events you need. Each event has a unique signature (`topic0`), that you will be able to get this the logs page. [Here is a sample transaction hash of $ARKM claim](https://etherscan.io/tx/0xc127438f51ae6917d7b1b7709d50049162ae41bdac4201b5658cd9cc01c9c591){:target="_blank"}. +The `ethereum.logs` table is one of the raw tables available that contains all events that are emitted. We will need to specify the contract address and topic0 to get the specific events we need. Each event has a unique signature (`topic0`), that we will be able to get this the logs page. [Here is a sample transaction hash of $ARKM claim](https://etherscan.io/tx/0xc127438f51ae6917d7b1b7709d50049162ae41bdac4201b5658cd9cc01c9c591){:target="_blank"}.


@@ -500,7 +527,7 @@ What this query does: - Using varbinary functions to get the decoded data from both topics and data column -In the `Swap` event, you will be able to identify: +In the `Swap` event, we will be able to identify: - Sender : `topic1` @@ -561,4 +588,23 @@ We are also able to get the total supply of USDC on Solana by editing the query SUM(post_token_balance) OVER () ``` -The above code will aggregate every addresses' USDC balance, which gets us the total USDC supply in Solana! \ No newline at end of file +The above code will aggregate every addresses' USDC balance, which gets us the total USDC supply in Solana! + +### Getting Wallet Balance On Solana + +[Getting Daily Solana Wallet Balances With Dates]((https://dune.com/queries/3273348){:target"_blank}) + +``` +select day,token_balance_owner,address,COALESCE(symbol,'unknown token') as symbol,token_balance +from solana_utils.daily_balances join tokens_solana.fungible USING (token_mint_address) +where token_balance_owner = 'JCNCMFXo5M5qwUPg2Utu1u6YWp3MbygxqBsBeXXJfrw' +ORDER BY 1 DESC +``` + +What this query does: + +- Get daily balance with daily balances spell (`solana_utils.daily_balances`) + +- Joining `tokens_solana.fungible` to get the token symbols + +If you need daily balances with no gaps in between, you can modify the query, referring [to this example](#erc20-holder-over-time). \ No newline at end of file From 584e96cb1ef6150273aad1d95c2d3e73ad5ade4d Mon Sep 17 00:00:00 2001 From: jh <0xroll.onchain@gmail.com> Date: Sat, 16 Dec 2023 13:03:08 +0800 Subject: [PATCH 09/11] fix syntax and add sample --- docs/resources/sample-queries.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/resources/sample-queries.md b/docs/resources/sample-queries.md index 01f84913..16bc694c 100644 --- a/docs/resources/sample-queries.md +++ b/docs/resources/sample-queries.md @@ -551,6 +551,26 @@ The codes above utilizes [varbinary functions](https://dune.com/docs/query/DuneS As it can get complicated as there is no restriction on the data payload, it is advisable to get the contracts to be [decoded at Dune](https://dune.com/contracts/new){:target="_blank"} as querying from the raw tables can take a long time as they contain all the events emitted! To understand more about decoded tables, check out our [Decoding Docs](https://dune.com/docs/app/decoding-contracts/){:target="_blank"}. +### Unnesting Arrays From Decoded Tables + +[Unnesting Arrays](https://dune.com/queries/2665544){:target=="_blank"} + +``` +SELECT account, + perc, + accounts, + percentAllocations +FROM splits_ethereum.SplitMain_call_updateSplit +CROSS JOIN UNNEST(accounts,percentAllocations) as t(account,perc) +WHERE "split" = 0x84af3D5824F0390b9510440B6ABB5CC02BB68ea1 +``` + +What this query does: + +- Unnesting arrays into multiple rows + +We have to select the columns that contains the array (`accounts,percentAllocations`) and unnest them into two new columns (`account,perc`). We will then be able to query it normally as rows after! + ## Solana Queries ### Getting Top 100 USDC Holders And Their Balances @@ -592,7 +612,7 @@ The above code will aggregate every addresses' USDC balance, which gets us the t ### Getting Wallet Balance On Solana -[Getting Daily Solana Wallet Balances With Dates]((https://dune.com/queries/3273348){:target"_blank}) +[Getting Daily Solana Wallet Balances With Dates](https://dune.com/queries/3273348){:target"_blank} ``` select day,token_balance_owner,address,COALESCE(symbol,'unknown token') as symbol,token_balance From 726446ee234cbc99a27f21e641dfc817f49f4eb5 Mon Sep 17 00:00:00 2001 From: jh <0xroll.onchain@gmail.com> Date: Sat, 16 Dec 2023 13:31:33 +0800 Subject: [PATCH 10/11] fix query add embed links --- docs/resources/sample-queries.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/docs/resources/sample-queries.md b/docs/resources/sample-queries.md index 16bc694c..28d44313 100644 --- a/docs/resources/sample-queries.md +++ b/docs/resources/sample-queries.md @@ -43,7 +43,7 @@ This is done here, using `CASE` function to get the aggregated amount. SELECT SUM(case when "from" = 0x0000000000000000000000000000000000000000 THEN (value/1e18) ELSE -(value/1e18) END) ``` -We can then verify the token supply on blockchain explorer! +We can then verify the token supply [on blockchain explorer](https://etherscan.io/token/0x046eee2cc3188071c02bfc1745a6b17c656e3f3d)! @@ -101,6 +101,8 @@ We then divide the value by the token decimals(`tr.value/POW(10,decimals)`) to g To get the balance of each user, we'll need to get to sum up inflows(`"to"`) and subtracting outflows(`"from"`) to get the current balance. We then aggregate all the values from the events,to get the current balance of each holder(`GROUP BY address,symbol`). + + - We can also add in a filter to remove holders that are holding dust amount (` HAVING SUM(amount) > 0.1`) - We can also add in a filter to get the top X holders (`LIMIT 100`) at the end of query @@ -246,23 +248,29 @@ With the generated date series(`gs` CTE), we will just have to join it with the Once we have the daily balance of each user, we can now get the daily number of holders by simply aggregating the number of addresses on each day. We can also get the daily change in token holders by taking each day's holder count and subtracting previous day's holder count (`COUNT(address) - LAG(COUNT(address)) OVER (ORDER BY date) as change `) + + + + ### Getting ERC20 Token Transaction Value In USD -[Top 100 Transfers In USD Into Binance](https://dune.com/queries/3269085){:target="_blank"} +[Top 100 Transfers In USD Into Binance(24Hrs)](https://dune.com/queries/3269085){:target="_blank"} We are able to get the token amount for transfers from the `erc20 evt transfer` table. To get the usd amount, we will need to join with the prices table (`prices.usd`) ``` +SELECT ROW_NUMBER() OVER (ORDER BY token_usd DESC) as ranking,* FROM ( select "from" as sender, symbol, value/POW(10,decimals) as token_amount, - value/POW(10,decimals) * price as token_usd -from erc20_ethereum.evt_transfer a -LEFT JOIN prices.usd b ON date_trunc('minute',a.evt_block_time) = b.minute AND a.contract_address = b.contract_address + value/POW(10,decimals) * price as token_usd, + evt_tx_hash +from erc20_ethereum.evt_transfer a LEFT JOIN prices.usd b ON date_trunc('minute',a.evt_block_time) = b.minute AND a.contract_address = b.contract_address where "to" = 0x28c6c06298d514db089934071355e5743bf21d60 and evt_block_time >= NOW() - interval '24' hour ORDER BY 4 DESC -LIMIT 1000 +LIMIT 100 +) ``` What this query does: @@ -277,6 +285,8 @@ The above query shows you the top 1000 deposits in the past 24 hours (`evt_block - The `date_trunc` here is required as there are milliseconds in the `evt_block_time`, to match the minute-level data in `prices.usd` + + ### Getting Current NFT Holders For A Specific Collection @@ -317,7 +327,7 @@ Aggregate by address will get us all current holders of BAYC collection! ### Getting NFT Collection Mints And Current Status -[NFT Mints And Status](https://dune.com/queries/3098984){:target="_blank"} +[Pudgy Penguin Mints And Status](https://dune.com/queries/3098984){:target="_blank"} ``` WITH nftMints as ( @@ -372,6 +382,8 @@ nftMints a LEFT JOIN currentNFTHolder b ON a.address = b.address AND a.tokenId = We then join both tables up by using `LEFT JOIN`, on the condition of matching the `address` and `tokenId` in both CTE. Finally, we aggregate by the address, to get the total minted list (`ARRAY_AGG(tokenId) as minted_list`), counting the total counts of mint,holding and sold. + + ### Getting NFT Platforms Daily And Cumulative Volume Over Time [NFT Platform Daily And Cumulative Volume Over Time](https://dune.com/queries/2858389){:target="_blank"} @@ -444,6 +456,8 @@ What this query does: - Joining both CTEs to will get the token amount , daily average price daily. Aggregating them will get us the daily total value locked over time! + + ### Querying Logs Table For Specific Event (Topics) [$ARKM Claimers From Logs Table](https://dune.com/queries/3269288){:target="_blank"} From 8225d4dcf402cd1a9300e3b0cb0465dfbf175e50 Mon Sep 17 00:00:00 2001 From: jh <0xroll.onchain@gmail.com> Date: Mon, 18 Dec 2023 00:02:17 +0800 Subject: [PATCH 11/11] add embeds and sample 2 --- docs/resources/sample-queries.md | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/docs/resources/sample-queries.md b/docs/resources/sample-queries.md index 28d44313..5d190338 100644 --- a/docs/resources/sample-queries.md +++ b/docs/resources/sample-queries.md @@ -325,6 +325,8 @@ We will just need to identify the latest recipient of each NFT token and we will Aggregate by address will get us all current holders of BAYC collection! + + ### Getting NFT Collection Mints And Current Status [Pudgy Penguin Mints And Status](https://dune.com/queries/3098984){:target="_blank"} @@ -585,6 +587,57 @@ What this query does: We have to select the columns that contains the array (`accounts,percentAllocations`) and unnest them into two new columns (`account,perc`). We will then be able to query it normally as rows after! +### Extracting Data From JSON Objects + +[Extract Trade Order Details From JSON Object](https://dune.com/queries/3291221){:target=="_blank"} + +``` +select json_extract_scalar(_position,'$.isLong') as isLong, + from_hex(json_extract_scalar(_position,'$.marginAsset')) as marginAsset, + from_hex(json_extract_scalar(_position,'$.player')) as player, + cast(json_extract_scalar(_position,'$.priceOpened') as decimal)/pow(10,18) as priceOpened, + cast(json_extract_scalar(_position,'$.positionSizeUsd') as decimal)/pow(10,18) as positionSizeUsd, + json_extract_scalar(_position,'$.fundingRateOpen') as fundingRateOpen, + cast(json_extract_scalar(_position,'$.marginAmountUsd') as decimal)/pow(10,18) as marginAmountUsd, + cast(json_extract_scalar(_position,'$.maxPositionProfitUsd') as decimal)/pow(10,18) as maxPositionProfitUsd, + cast(json_extract_scalar(_position,'$.positionSizeInTargetAsset') as decimal) as positionSizeInTargetAsset +from degensbet_arbitrum.DegenMain_evt_OrderExecuted +``` + +What this query does: + +- Extracting order details from JSON Object by using `json_extract_scalar` + +- Casting to the correct data type + +``` +{ + "isLong": true, + "isOpen": true, + "marginAsset": "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8", + "player": "0x101a2d2afc2f9b0b217637f53e3a3e859104a33d", + "timestampOpened": 1698168769, + "priceOpened": 1783000000000000000000, + "positionSizeUsd": 10000000000000000000000, + "fundingRateOpen": 98, + "orderIndex": 768, + "marginAmountUsd": 10000000000000000000, + "maxPositionProfitUsd": 3419059418235433408476, + "positionSizeInTargetAsset": 5608524957936062815 +} +``` + +The above is a sample of `_position` column. To extract the individual data, we can make use of `json_extract_scalar` to get each data. +We will also need to cast them to the correct data type for further calculations as they are `varchar` datatype upon extraction. + +``` +from_hex(json_extract_scalar(_position,'$.player')) as player, +``` + +For addresses, you will have to make use of `from_hex()` to convert the addresses from `varchar` to `varbinary` + + + ## Solana Queries ### Getting Top 100 USDC Holders And Their Balances