## USER Assume that I am working with Dune Analytics, which uses DuneSQL, a fork of TrinoSQL. Please keep this context in mind when generating SQL queries or providing assistance related to Dune Analytics and its data analysis. For reference, here is a DuneSQL query that produces daily holder counts of the RPT token with holder_balance as ( with address_date_list as ( with min_max_date as ( select min(date(evt_block_time)) as min_date, max(date(evt_block_time)) as max_date from erc721_ethereum.evt_Transfer where contract_address = {{nft_contract}} ), date_series as ( select block_date from unnest(sequence(date('2018-01-01'), date(now()), interval '1' day)) as s(block_date) ), holder_address_list as ( select distinct address from ( select "from" as address from erc721_ethereum.evt_Transfer where contract_address = {{nft_contract}} union select "to" as address from erc721_ethereum.evt_Transfer where contract_address = {{nft_contract}} ) t ) select block_date, address from date_series full outer join holder_address_list on true ), daily_transfer_summary as ( with transfers_details as ( select date_trunc('day', evt_block_time) as block_date, "to" as address, count(evt_tx_hash) as transaction_count from erc721_ethereum.evt_Transfer where contract_address = {{nft_contract}} group by 1, 2 union all select date_trunc('day', evt_block_time) as block_date, "from" as address, count(evt_tx_hash)*(-1) as transaction_count from erc721_ethereum.evt_Transfer where contract_address = {{nft_contract}} group by 1, 2 ) select block_date, address, sum(transaction_count) as transaction_count from transfers_details group by block_date, address ) select a.block_date, a.address, sum(coalesce(transaction_count, 0)) over (partition by a.address order by a.block_date) as hold_count from address_date_list a left join daily_transfer_summary t on a.block_date = t.block_date and a.address = t.address ), token_holder_summary as ( select block_date, count(address) filter (where hold_count > 0) as unique_holder_count, (count(address) filter (where hold_count > 0) - lag(count(address) filter (where hold_count > 0), 1) over (order by block_date desc))*-1 as daily_holder_change from holder_balance group by 1 order by 1 desc ), diff as ( select block_date, unique_holder_count, lag(unique_holder_count) over (order by block_date) as lag_user from token_holder_summary order by block_date asc) select block_date, unique_holder_count, lag_user, (unique_holder_count - lag_user) as diff_users, (cast((unique_holder_count - lag_user) as double)) / (cast(lag_user as double)) as growth_rate from diff where block_date > date('2022-11-12') I will provide subsequent requests for your assistance in construction queries. For now, please simply acknolwedge this message and await my further instructions ## ASSISTANT Understood. I'm ready to assist with your DuneSQL queries or any Dune Analytics related inquiries. Please feel free to provide the details of the subsequent requests whenever you're ready. ## USER I want the query results to display the cumulative supply of different RTokens over time, grouped by day. The schema of the erc20_ethereum.evt_Transfer table is as follows (grouped in pairs of column header/type): contract_address varbinary from varbinary to varbinary value uint256 evt_block_number bigint evt_block_time timestamp evt_index bigint evt_tx_hash varbinary Transactions where the "from" address is 0x0000000000000000000000000000000000000000 represents an increase in supply Where the "to" address is 0x0000000000000000000000000000000000000000 means that the supply has decreased The results should be restricted to RTokens - their addresses of each RToken can be found based on an event indexed in Dune based on the event RTokenCreated and exists in the table reserve_protocol_ethereum.DeployerP1_evt_RTokenCreated contract_address varbinary main varbinary owner varbinary rToken varbinary stRSR varbinary evt_block_number bigint evt_block_time timestamp evt_index bigint evt_tx_hash varbinary version varchar Where values can become negative, you should cast the value as a double. Values should also be divided by 1e18 because all relevant ERC20s are denominated in 18 decimal places For example: -CAST(value AS double) / 1e18 ## ASSISTANT Based on your description, here's a SQL query that calculates the daily cumulative supply of each RToken. The query starts by identifying RTokens from the reserve_protocol_ethereum.DeployerP1_evt_RTokenCreated table. It then collects the token transfer events for these tokens and calculates the daily cumulative supply based on the rules you've mentioned. ```sql WITH rtoken_list AS ( SELECT rToken FROM reserve_protocol_ethereum.DeployerP1_evt_RTokenCreated ), daily_token_transfers AS ( SELECT date_trunc('day', evt_block_time) AS day, contract_address AS rToken, CASE WHEN "from" = '0x0000000000000000000000000000000000000000' THEN CAST(value AS DOUBLE) / 1e18 WHEN "to" = '0x0000000000000000000000000000000000000000' THEN -CAST(value AS DOUBLE) / 1e18 ELSE 0 END AS daily_supply_change FROM erc20_ethereum.evt_Transfer WHERE contract_address IN (SELECT rToken FROM rtoken_list) ), cumulative_supply AS ( SELECT day, rToken, SUM(daily_supply_change) OVER (PARTITION BY rToken ORDER BY day) AS cumulative_supply FROM daily_token_transfers ) SELECT * FROM cumulative_supply ORDER BY day, rToken; ``` This query results in a table where each row represents a single day for a single RToken, and the 'cumulative_supply' column indicates the cumulative supply of that RToken on that day. Please note, you need to replace `'0x0000000000000000000000000000000000000000'` with the actual 'from' and 'to' addresses representing increase and decrease in supply, if they're different. I've assumed the addresses based on your description. Additionally, since the data types and the table schemas for DuneSQL might be different from what I have worked with, please adapt the query accordingly if there are any errors. The exact structure and naming of tables and columns can vary between databases. ## USER Error: Line 10:28: Cannot apply operator: varbinary = varchar(42) at line 10, position 28. Did you mean to write a bytearray literal? Use the syntax 0x1234 instead of '0x1234' [Execution ID: 01H4XZ6X60P73Y87EV5PP2G3SC]. ## ASSISTANT Apologies for the confusion. The error is due to the incorrect comparison of a binary and a string. We need to make sure we are comparing varbinary types with varbinary types. To fix this, please replace the '0x0000000000000000000000000000000000000000' addresses with its binary equivalent. Here's the corrected SQL query. ```sql WITH rtoken_list AS ( SELECT rToken FROM reserve_protocol_ethereum.DeployerP1_evt_RTokenCreated ), daily_token_transfers AS ( SELECT date_trunc('day', evt_block_time) AS day, contract_address AS rToken, CASE WHEN "from" = 0x0000000000000000000000000000000000000000 THEN CAST(value AS DOUBLE) / 1e18 WHEN "to" = 0x0000000000000000000000000000000000000000 THEN -CAST(value AS DOUBLE) / 1e18 ELSE 0 END AS daily_supply_change FROM erc20_ethereum.evt_Transfer WHERE contract_address IN (SELECT rToken FROM rtoken_list) ), cumulative_supply AS ( SELECT day, rToken, SUM(daily_supply_change) OVER (PARTITION BY rToken ORDER BY day) AS cumulative_supply FROM daily_token_transfers ) SELECT * FROM cumulative_supply ORDER BY day, rToken; ``` This corrected version of the query should work, given that `0x0000000000000000000000000000000000000000` is the correct binary equivalent of your placeholder address. Please replace this with the correct value if needed. ## USER The cumulative supply of an asset appears to go to 0 if there are no subsequent transactions. Please modify the query so that there is a persistent entry for every non-zero supply even in the absence of supply changes on a particular day ## ASSISTANT We need to generate a date series and cross join it with the rToken list to ensure we have an entry for each date and token combination. Then we'll use a window function to carry over the last known non-zero supply to subsequent days with no supply changes. Here's the updated query: ```sql WITH rtoken_list AS ( SELECT DISTINCT rToken FROM reserve_protocol_ethereum.DeployerP1_evt_RTokenCreated ), date_series AS ( SELECT block_date FROM unnest(sequence(date('2018-01-01'), date(now()), interval '1' day)) AS s(block_date) ), token_date AS ( SELECT rToken, block_date FROM rtoken_list CROSS JOIN date_series ), daily_token_transfers AS ( SELECT date_trunc('day', evt_block_time) AS day, contract_address AS rToken, CASE WHEN "from" = 0x0000000000000000000000000000000000000000 THEN CAST(value AS DOUBLE) / 1e18 WHEN "to" = 0x0000000000000000000000000000000000000000 THEN -CAST(value AS DOUBLE) / 1e18 ELSE 0 END AS daily_supply_change FROM erc20_ethereum.evt_Transfer WHERE contract_address IN (SELECT rToken FROM rtoken_list) ), daily_supply AS ( SELECT td.rToken, td.block_date as day, COALESCE(dtt.daily_supply_change, 0) as daily_supply_change FROM token_date td LEFT JOIN daily_token_transfers dtt ON td.block_date = dtt.day AND td.rToken = dtt.rToken ), cumulative_supply AS ( SELECT day, rToken, SUM(daily_supply_change) OVER (PARTITION BY rToken ORDER BY day) AS cumulative_supply FROM daily_supply ), final_result AS ( SELECT day, rToken, MAX(cumulative_supply) OVER (PARTITION BY rToken ORDER BY day ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS adjusted_cumulative_supply FROM cumulative_supply WHERE cumulative_supply > 0 ) SELECT * FROM final_result ORDER BY day, rToken; ``` In this query, `date_series` generates a series of dates from 2018-01-01 to today, and `token_date` creates a cross join between these dates and the rToken list, ensuring we have an entry for each date and token combination. The `daily_supply` CTE is then adjusted to join this `token_date` table with the `daily_token_transfers`, ensuring that every day and token combination is represented, even if there was no transaction for a given token on a certain day. Finally, `final_result` uses a window function to carry forward the last non-zero cumulative supply to days with no supply changes. The `MAX` function is used instead of `LAST_VALUE` or `LAG` because it considers all preceding rows in the partition, not just the immediate previous row. ## USER Great, now please provide me a query that identifies unique RToken depositors based on their aggregate deposit amounts. This is based on the unique "recipient" addresses and the amount in the "baskets" parameter in this event reserve_protocol_ethereum.RTokenP1_evt_Issuance. The schema is amount uint256 baskets uint256 contract_address varbinary issuer varbinary evt_block_number bigint evt_block_time timestamp evt_index bigint evt_tx_hash varbinary recipient varbinary 0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f is the address for eUSD, so you can filter contract addresses for this ## ASSISTANT Assuming that we are interested in the `recipient` and the aggregate of the `baskets` field for each unique depositor, here is a query that should fulfill this requirement: ```sql SELECT recipient, SUM(CAST(baskets AS DOUBLE) / 1e18) AS total_baskets_deposited FROM reserve_protocol_ethereum.RTokenP1_evt_Issuance WHERE contract_address = 0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f GROUP BY recipient ORDER BY total_baskets_deposited DESC; ``` This query will group depositors by their `recipient` address and sum the `baskets` amount they've deposited, in total. Results are ordered in descending order by the total amount deposited, to list depositors who have deposited the most at the top. Note that the `baskets` values are divided by 1e18 to adjust for the 18 decimal places as previously mentioned. Remember to replace `0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f` with the correct binary equivalent if needed. ## USER Can you give me a query that groups the total deplsits to show me, for different depositor ranges: 1) The number of depositors in each range 2) The aggregate amount or baskets of eUSD that is minted in each range The ranges are as follows: <1000 1-10k 10-100k 100-300k 300k-1M `>1M` ## ASSISTANT To address your requirement, we can create bins for the total deposit ranges and then group depositors based on these bins. Here's a query that might fulfill this requirement: ```sql WITH deposit_summary AS ( SELECT recipient, SUM(CAST(baskets AS DOUBLE) / 1e18) AS total_baskets_deposited FROM reserve_protocol_ethereum.RTokenP1_evt_Issuance WHERE contract_address = 0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f GROUP BY recipient ), binned_deposits AS ( SELECT recipient, total_baskets_deposited, CASE WHEN total_baskets_deposited < 1000 THEN '<1000' WHEN total_baskets_deposited BETWEEN 1000 AND 10000 THEN '1k-10k' WHEN total_baskets_deposited BETWEEN 10000 AND 100000 THEN '10k-100k' WHEN total_baskets_deposited BETWEEN 100000 AND 300000 THEN '100k-300k' WHEN total_baskets_deposited BETWEEN 300000 AND 1000000 THEN '300k-1M' ELSE '>1M' END AS deposit_range FROM deposit_summary ) SELECT deposit_range, COUNT(recipient) AS num_depositors, SUM(total_baskets_deposited) AS total_baskets_in_range FROM binned_deposits GROUP BY deposit_range ORDER BY MIN(total_baskets_deposited); ``` In this query, the `deposit_summary` common table expression (CTE) first computes the total deposit for each depositor. The `binned_deposits` CTE then categorizes these total deposits into the specified ranges. Finally, we count the number of depositors (`num_depositors`) and sum the total deposited eUSD (`total_baskets_in_range`) in each deposit range. Again, remember to replace `0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f` with the correct binary equivalent if needed. ## USER The produced results are no ordered by the actual depositor range. Below is how the ranges should be ordered, from smallest to largest: <1000 1-10k 10-100k 100-300k 300k-1M >1M Following is the query I ended up using: WITH deposit_totals AS ( SELECT recipient, SUM(CAST(baskets AS DOUBLE) / 1e18) AS total_baskets_deposited FROM reserve_protocol_ethereum.RTokenP1_evt_Issuance WHERE contract_address = 0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f GROUP BY recipient ) SELECT deposit_range, COUNT(*) AS number_of_depositors, SUM(total_baskets_deposited) AS total_baskets_deposited FROM ( SELECT total_baskets_deposited, CASE WHEN total_baskets_deposited < 1000 THEN '<1000' WHEN total_baskets_deposited BETWEEN 1000 AND 10000 THEN '1k-10k' WHEN total_baskets_deposited BETWEEN 10000 AND 100000 THEN '10k-100k' WHEN total_baskets_deposited BETWEEN 100000 AND 300000 THEN '100k-300k' WHEN total_baskets_deposited BETWEEN 300000 AND 1000000 THEN '300k-1M' ELSE '>1M' END AS deposit_range FROM deposit_totals ) sub GROUP BY deposit_range ORDER BY deposit_range; ## ASSISTANT I see where the misunderstanding is, you're right. SQL doesn't know how to order those string values to reflect the logical order of the ranges. One way around this is to manually define the order using a CASE statement in the ORDER BY clause: ```sql WITH deposit_totals AS ( SELECT recipient, SUM(CAST(baskets AS DOUBLE) / 1e18) AS total_baskets_deposited FROM reserve_protocol_ethereum.RTokenP1_evt_Issuance WHERE contract_address = 0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f GROUP BY recipient ) SELECT deposit_range, COUNT(*) AS number_of_depositors, SUM(total_baskets_deposited) AS total_baskets_deposited FROM ( SELECT total_baskets_deposited, CASE WHEN total_baskets_deposited < 1000 THEN '<1000' WHEN total_baskets_deposited BETWEEN 1000 AND 10000 THEN '1k-10k' WHEN total_baskets_deposited BETWEEN 10000 AND 100000 THEN '10k-100k' WHEN total_baskets_deposited BETWEEN 100000 AND 300000 THEN '100k-300k' WHEN total_baskets_deposited BETWEEN 300000 AND 1000000 THEN '300k-1M' ELSE '>1M' END AS deposit_range FROM deposit_totals ) sub GROUP BY deposit_range ORDER BY CASE WHEN deposit_range = '<1000' THEN 1 WHEN deposit_range = '1k-10k' THEN 2 WHEN deposit_range = '10k-100k' THEN 3 WHEN deposit_range = '100k-300k' THEN 4 WHEN deposit_range = '300k-1M' THEN 5 ELSE 6 END; ``` This manually defines the order of the deposit_range based on its value. In this way, the result will be ordered correctly according to the logical ordering of the ranges. Again, remember to replace `0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f` with the correct binary equivalent if needed. ## USER Can you additionally give me the total sum of depositors? ## ASSISTANT Sure, to get the total sum of depositors, you can add a simple SQL `SUM()` function over the `number_of_depositors` alias. You can use the `ROLLUP()` function to get the subtotal and total grouping. Here is the updated query: ```sql WITH deposit_totals AS ( SELECT recipient, SUM(CAST(baskets AS DOUBLE) / 1e18) AS total_baskets_deposited FROM reserve_protocol_ethereum.RTokenP1_evt_Issuance WHERE contract_address = 0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f GROUP BY recipient ) SELECT CASE WHEN GROUPING(deposit_range) = 1 THEN 'All Ranges' ELSE deposit_range END AS deposit_range, COUNT(*) AS number_of_depositors, SUM(total_baskets_deposited) AS total_baskets_deposited FROM ( SELECT total_baskets_deposited, CASE WHEN total_baskets_deposited < 1000 THEN '<1000' WHEN total_baskets_deposited BETWEEN 1000 AND 10000 THEN '1k-10k' WHEN total_baskets_deposited BETWEEN 10000 AND 100000 THEN '10k-100k' WHEN total_baskets_deposited BETWEEN 100000 AND 300000 THEN '100k-300k' WHEN total_baskets_deposited BETWEEN 300000 AND 1000000 THEN '300k-1M' ELSE '>1M' END AS deposit_range FROM deposit_totals ) sub GROUP BY ROLLUP(deposit_range) ORDER BY CASE WHEN deposit_range = '<1000' THEN 1 WHEN deposit_range = '1k-10k' THEN 2 WHEN deposit_range = '10k-100k' THEN 3 WHEN deposit_range = '100k-300k' THEN 4 WHEN deposit_range = '300k-1M' THEN 5 WHEN deposit_range = '>1M' THEN 6 ELSE 7 END; ``` This query will produce an additional row at the end with a `deposit_range` of `'All Ranges'` and `number_of_depositors` being the total sum of depositors. Again, remember to replace `0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f` with the correct binary equivalent if needed. ## USER I want the total number of depositors as a separate column (all the same value for each row) ## ASSISTANT In that case, you can calculate the total number of depositors separately in a subquery and join this value to your main result set. Here is how you can do it: ```sql WITH deposit_totals AS ( SELECT recipient, SUM(CAST(baskets AS DOUBLE) / 1e18) AS total_baskets_deposited FROM reserve_protocol_ethereum.RTokenP1_evt_Issuance WHERE contract_address = 0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f GROUP BY recipient ), total_depositor_count AS ( SELECT COUNT(DISTINCT recipient) as total_depositors FROM reserve_protocol_ethereum.RTokenP1_evt_Issuance WHERE contract_address = 0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f ) SELECT deposit_range, COUNT(*) AS number_of_depositors, SUM(total_baskets_deposited) AS total_baskets_deposited, total_depositors FROM ( SELECT total_baskets_deposited, CASE WHEN total_baskets_deposited < 1000 THEN '<1000' WHEN total_baskets_deposited BETWEEN 1000 AND 10000 THEN '1k-10k' WHEN total_baskets_deposited BETWEEN 10000 AND 100000 THEN '10k-100k' WHEN total_baskets_deposited BETWEEN 100000 AND 300000 THEN '100k-300k' WHEN total_baskets_deposited BETWEEN 300000 AND 1000000 THEN '300k-1M' ELSE '>1M' END AS deposit_range FROM deposit_totals ) sub CROSS JOIN total_depositor_count GROUP BY deposit_range, total_depositors ORDER BY CASE WHEN deposit_range = '<1000' THEN 1 WHEN deposit_range = '1k-10k' THEN 2 WHEN deposit_range = '10k-100k' THEN 3 WHEN deposit_range = '100k-300k' THEN 4 WHEN deposit_range = '300k-1M' THEN 5 ELSE 6 END; ``` In this modified version of the query, I added a second CTE (`total_depositor_count`) to calculate the total number of distinct depositors in the `reserve_protocol_ethereum.RTokenP1_evt_Issuance` table where the contract address matches the specified value. This value is then added to each row of the main result set using a `CROSS JOIN`. Again, remember to replace `0xa0d69e286b938e21cbf7e51d71f6a4c8918f482f` with the correct binary equivalent if needed.