diff --git a/deployments/deployment_mainnet_2025-09-21.json b/deployments/deployment_mainnet_2025-09-21.json new file mode 100644 index 0000000..d16eb3f --- /dev/null +++ b/deployments/deployment_mainnet_2025-09-21.json @@ -0,0 +1,49 @@ +{ + "USDU": "0x4695252ccdd73f1d8ce7d7c78b1d3f55a127161ddbba5fb1174d10a6825397c", + "gasToken": "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "collateralRegistry": "0xf21c277fcd910dc78e6ef205ec769aa3cdbc3b0535cde5c304bf175c72ba20", + "UBTC": { + "collateral": "0x3687f0b57779ce0b6720285f4db622ab000725411183690ea37c5c995298e49", + "addressesRegistry": "0x94dcb2432a4af4b0b9a6f69a725567d3f1f7b626387a0634f18d8750708a77", + "borrowerOperations": "0x1fbc3c3f3758c9d637a4720a85c7c15ef5e2c9a0a94b7692e672c68791f635c", + "troveManager": "0x116b8ccdab70af2662fb9b8d29a505db315ef6bb32dd4370615b074a352274e", + "troveNft": "0x57b1e2a9babd2f90fb56fe55b67b859474f84e81291cb05be1340a7f7042359", + "stabilityPool": "0x7c8543248623eddaba11b596cd4802e55b46814355b8a1cb2ce7ef523d299a", + "sortedTroves": "0x61dadaf5bf71d81d4bffc2232880fed4720bace175868a51f309c0f64fd05aa", + "activePool": "0x37b1dd4c931081138df0b004493aa50af5b71b3924462a2cfc245d8bb083f3f", + "defaultPool": "0x5b78a98e3d9073612621b7b57cdc4d87c5e9ee531b1add8a895bc06c9347be9", + "collSurplusPool": "0x57675cd68e36101553ad1eba03bc10d0b2548968d2732645bd49834708e886b", + "gasPool": "0x6d4bca3cc94999353d821da04cb67ab6cd0594d1cfcf2859042e5bb352c9e2c", + "interestRouter": "0x4b20f1b3b44060abdcb0b1f1018edc78622ea0065605c7cec1754bf522f497a", + "liquidationManager": "0x63a139f850ce8526ab7d65e18341fb79189668c46fc15fca86046088a1b681f", + "redemptionManager": "0x7687a3e75b73bf2fb80ab442bf67707ef21e1c7ca8a4b764146ede9143bb416", + "batchManager": "0x74d9cb4bce38b8f018ea344a70c822fabd2e1f807d4e53abd5c956cfa2d148", + "priceFeed": "0x7b24488112e5a222db34bf2536cd1e1dc0c6a4626eb4eb256bc3e3f990c71be", + "hintHelpers": "0x27d0d8e2a800303e28b7f41bd49933133c4853586e2613aa4457d9400adc81e", + "multiTroveGetter": "0x37b1dd4c931081138df0b004493aa50af5b71b3924462a2cfc245d8bb083f3f", + "troveManagerEventsEmitter": "0x8d5d5ead639f66d455c29f43bae0332c04282fd0f25e5c1b0e6a71c943b63e", + "underlyingAddress": "0x3687f0b57779ce0b6720285f4db622ab000725411183690ea37c5c995298e49" + }, + "GBTC": { + "collateral": "0x4d931635e7d9d617823f2b7431cad3ea021dbfa3ebb07df2229cd6c8bdc7b55", + "addressesRegistry": "0x305078f5237d5654f159148c9e4fc067ec10e76ef56bfe460336a73efa4a27f", + "borrowerOperations": "0x186174c5af724ef59c278b59872ecbcdcbfde4c7e780fa794aad732708dfb52", + "troveManager": "0x78507dfaedce3558c5b98b4cc34f265452230b53225e5fbe4d91d700b481322", + "troveNft": "0x5e0bcd997c22de21ac7716cd38fdb881b56764d20fa6bbe1647281abc3e1800", + "stabilityPool": "0x65ccbeb1516bd1ae841b0729dd0798cb86027fbbbce929f647a2d057655c054", + "sortedTroves": "0x7f706d36f1d2f8faad1ed03c276ee4573919bcb04189fae1b076c554be41786", + "activePool": "0x2f8bc83077a82b0fb7121de4952a1c23e68685a6ad30009a619ef1848b895dc", + "defaultPool": "0x7ed69ae7f5196804f6ba3ebbe3790d8fde0c4d1b835258223c43f67c95f4932", + "collSurplusPool": "0x6b049703ae7d2edc03348806d831cecadbe5306e84431c5c6b592a3714f7cc6", + "gasPool": "0x1aa2c3e690e1b1210ab6698f97281505369ac517b2e983b137bb3b47c32a323", + "interestRouter": "0x307d7378c2543140d607297f4a863fbfe83cb0804297532c52bdd246a7b7d69", + "liquidationManager": "0x34eb92dff01b310eb48a654067bfa1603f92a6b27eed5ad226a9a9d41ae42d1", + "redemptionManager": "0x7f451319b676e7e2a9aca8a8a3f1827d6b49bc97231f15793d6f944eb39b4e3", + "batchManager": "0x79e28912b8b4c2a5ba2a27e3e4490f185ea4ae1e87575e259abfee60a75b026", + "priceFeed": "0x240af0b78af03ce5bf3e037a7582e989b8169e2d30fd3e6fbb55a79bafbed6a", + "hintHelpers": "0x27d0d8e2a800303e28b7f41bd49933133c4853586e2613aa4457d9400adc81e", + "multiTroveGetter": "0x2f8bc83077a82b0fb7121de4952a1c23e68685a6ad30009a619ef1848b895dc", + "troveManagerEventsEmitter": "0x6dcfc4ce8ca0c664d5f6c5ee434c9379d15e616246db092cd9e21e5871e4377", + "underlyingAddress": "0x4d931635e7d9d617823f2b7431cad3ea021dbfa3ebb07df2229cd6c8bdc7b55" + } +} \ No newline at end of file diff --git a/dune_queries/obl/obl_query.sql b/dune_queries/obl/obl_query.sql new file mode 100644 index 0000000..13f0307 --- /dev/null +++ b/dune_queries/obl/obl_query.sql @@ -0,0 +1,515 @@ +-- USDU Protocol - Daily User Balances Query +-- Network: Starknet Mainnet +-- Purpose: Track daily user positions for BTCFi incentive system integration +-- This query extracts data directly from USDU protocol events and tracks NFT ownership + +WITH +-- ============================================================================ +-- CONFIGURATION CONSTANTS +-- ============================================================================ +-- Shared constants across all branches +shared_cfg AS ( + SELECT + CAST(0x02f94539f80158f9a48a7acf3747718dfbec9b6f639e2742c1fb44ae7ab5aa04 AS VARBINARY) AS usdu_addr, + 'USDU' AS debt_asset_symbol, + 18 AS debt_decimals, + 0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac AS btc_price_addr, + 1.0 AS debt_price, + 1e18 AS protocol_scale, + 'Uncap Protocol' AS market_name, + 'Uncap' AS market_owner +), + +-- Per-branch configuration +cfg AS ( + -- WBTC Branch + SELECT + 'WBTC' AS branch_id, + 'WWBTC' AS asset_symbol, + 18 AS asset_decimals, + 2759669 AS deployment_block, + 0x042a37aa9263b01191286f0f800cc85c676441fb9d27d74bbf3ebcbf4e373d81 AS market_address_raw, + CAST(0x042a37aa9263b01191286f0f800cc85c676441fb9d27d74bbf3ebcbf4e373d81 AS VARBINARY) AS market_address, + 0x075d9e518f46a9ca0404fb0a7d386ce056dadf57fd9a0e8659772cb517be4a18 AS asset_addr_raw, + CAST(0x075d9e518f46a9ca0404fb0a7d386ce056dadf57fd9a0e8659772cb517be4a18 AS VARBINARY) AS asset_addr, + 0x038a9949900e7905f648cf0b50335efdac16a8acb8dfc870835da21c3f68e934 AS events_emitter, + 0x04a1e17f1e4d5fdd839fa6a4c52a742b9d10871028b6ffcfb2f1de8dd5dc2cd4 AS trove_nft + UNION ALL + -- solvBTC Branch + SELECT + 'solvBTC' AS branch_id, + 'solvBTC' AS asset_symbol, + 18 AS asset_decimals, + 5572500 AS deployment_block, + 0x074ffc9708c6d3b23ff6eb89a5b780ce1e23de71735b7e3353cde9172d5321af AS market_address_raw, + CAST(0x074ffc9708c6d3b23ff6eb89a5b780ce1e23de71735b7e3353cde9172d5321af AS VARBINARY) AS market_address, + 0x0593e034DdA23eea82d2bA9a30960ED42CF4A01502Cc2351Dc9B9881F9931a68 AS asset_addr_raw, + CAST(0x0593e034DdA23eea82d2bA9a30960ED42CF4A01502Cc2351Dc9B9881F9931a68 AS VARBINARY) AS asset_addr, + 0x053fc2fb416e7d5e8e44594c989b638ffabf8a8f804a2041e564db7febccc2a9 AS events_emitter, + 0x034db33f4917ad9f8ca423e7295c303bd21cdf1724749576e35b022d9d70d006 AS trove_nft + UNION ALL + -- tBTC Branch + SELECT + 'tBTC' AS branch_id, + 'tBTC' AS asset_symbol, + 18 AS asset_decimals, + 5572000 AS deployment_block, + 0x07f3317c9c26f3956759eb8de992e474dd8feaaccdd3f53d7c8e808503258d77 AS market_address_raw, + CAST(0x07f3317c9c26f3956759eb8de992e474dd8feaaccdd3f53d7c8e808503258d77 AS VARBINARY) AS market_address, + 0x04daa17763b286d1e59b97c283C0b8C949994C361e426A28F743c67bDfE9a32f AS asset_addr_raw, + CAST(0x04daa17763b286d1e59b97c283C0b8C949994C361e426A28F743c67bDfE9a32f AS VARBINARY) AS asset_addr, + 0x02456ca1b9fcf7851577d92c3a4bc26b59c4f44ee222d67ae7032350ee195ac1 AS events_emitter, + 0x063ed1e2a28424366a10123713d601ce0158fc0a7026ace0cf98bd1872d93f1e AS trove_nft +), + +-- ============================================================================ +-- 1. EXTRACT RAW EVENTS +-- ============================================================================ + +-- Regular Trove Updates (Standard TroveUpdated events) +-- Captures updates for troves NOT in a batch (individual troves) +trove_updated_events AS ( + SELECT + cfg.branch_id, + DATE(e.block_time AT TIME ZONE 'UTC') AS date, + e.block_time AS evt_block_time, + e.transaction_hash AS evt_tx_hash, + 'REGULAR' AS update_type, + LOWER(CONCAT('0x', + "right"(SUBSTRING(TO_HEX(e.data[2]), 3), 32), + "right"(SUBSTRING(TO_HEX(e.data[1]), 3), 32) + )) AS trove_id, + CAST(bytearray_to_uint256(e.data[3]) AS DOUBLE) / 1e18 AS debt, + CAST(bytearray_to_uint256(e.data[5]) AS DOUBLE) / 1e18 AS coll, + CAST(bytearray_to_uint256(e.data[9]) AS DOUBLE) / 1e18 AS annual_interest_rate, + NULL AS shares, + NULL AS interest_batch_manager + FROM starknet.events e + INNER JOIN cfg ON e.from_address = cfg.events_emitter + WHERE e.keys[1] = 0x01babc9e592593f609d7e88cca6a04e21db92f5faf85fb83153cc9b369b2b3e6 + AND e.block_number >= cfg.deployment_block +), + +-- Batched Trove Updates (BatchedTroveUpdated events) +-- Captures updates for troves IN a batch. +-- Note: These events do NOT contain direct 'debt' values, but 'shares' which must be resolved against the batch state. +batched_trove_updated_events AS ( + SELECT + cfg.branch_id, + DATE(e.block_time AT TIME ZONE 'UTC') AS date, + e.block_time AS evt_block_time, + e.transaction_hash AS evt_tx_hash, + 'BATCHED' AS update_type, + LOWER(CONCAT('0x', + "right"(SUBSTRING(TO_HEX(e.data[2]), 3), 32), + "right"(SUBSTRING(TO_HEX(e.data[1]), 3), 32) + )) AS trove_id, + NULL AS debt, -- Debt is calculated dynamically via shares + CAST(bytearray_to_uint256(e.data[6]) AS DOUBLE) / 1e18 AS coll, + NULL AS annual_interest_rate, -- Rate is determined by the batch + CAST(bytearray_to_uint256(e.data[4]) AS DOUBLE) / 1e18 AS shares, + e.data[3] AS interest_batch_manager + FROM starknet.events e + INNER JOIN cfg ON e.from_address = cfg.events_emitter + WHERE e.keys[1] = 0x03c054baf812ebdae34d56f1322bd89e0b0c4aed0829fe0e7658fce049d25f71 + AND e.block_number >= cfg.deployment_block +), + +-- Batch Updates (BatchUpdated events) +-- Tracks global state of interest batches (total debt, total shares) required to resolve individual trove debt. +batch_updated_events AS ( + SELECT + cfg.branch_id, + DATE(e.block_time AT TIME ZONE 'UTC') AS date, + e.block_time AS evt_block_time, + e.transaction_hash AS evt_tx_hash, + e.data[1] AS interest_batch_manager, + CAST(bytearray_to_uint256(e.data[3]) AS DOUBLE) / 1e18 AS batch_debt, + CAST(bytearray_to_uint256(e.data[7]) AS DOUBLE) / 1e18 AS batch_annual_interest_rate, + CAST(bytearray_to_uint256(e.data[11]) AS DOUBLE) / 1e18 AS batch_total_shares + FROM starknet.events e + INNER JOIN cfg ON e.from_address = cfg.events_emitter + WHERE e.keys[1] = 0x039a6ae9867ab3ddba84021a987ac7776389a6cc5bad711eda5e4a44d1a2fe80 + AND e.block_number >= cfg.deployment_block +), + +-- NFT Transfers +-- Tracks ownership of troves (Troves are ERC721 tokens) +nft_transfer_events AS ( + SELECT + cfg.branch_id, + DATE(e.block_time AT TIME ZONE 'UTC') AS date, + e.block_time AS evt_block_time, + e.transaction_hash AS evt_tx_hash, + LOWER(CONCAT('0x', + "right"(SUBSTRING(TO_HEX(e.keys[5]), 3), 32), + "right"(SUBSTRING(TO_HEX(e.keys[4]), 3), 32) + )) AS trove_id, + e.keys[2] AS from_addr, + e.keys[3] AS to_addr + FROM starknet.events e + INNER JOIN cfg ON e.from_address = cfg.trove_nft + WHERE e.keys[1] = 0x0099cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9 + AND e.block_number >= cfg.deployment_block +), + +-- ============================================================================ +-- 2. UNIFY AND AGGREGATE DAILY STATES (SPARSE) +-- ============================================================================ + +-- Combine regular and batched updates into a single stream +all_trove_updates AS ( + SELECT * FROM trove_updated_events + UNION ALL + SELECT * FROM batched_trove_updated_events +), + +-- Get the last known state for each trove on any day it had an event +daily_trove_states_sparse AS ( + SELECT * FROM ( + SELECT + branch_id, + date, + trove_id, + debt, + coll, + annual_interest_rate, + shares, + interest_batch_manager, + update_type, + ROW_NUMBER() OVER (PARTITION BY branch_id, trove_id, date ORDER BY evt_block_time DESC) AS rn + FROM all_trove_updates + ) WHERE rn = 1 +), + +-- Get the last known state for each batch on any day it had an event +daily_batch_states_sparse AS ( + SELECT * FROM ( + SELECT + branch_id, + date, + interest_batch_manager, + batch_debt, + batch_total_shares, + batch_annual_interest_rate, + ROW_NUMBER() OVER (PARTITION BY branch_id, interest_batch_manager, date ORDER BY evt_block_time DESC) AS rn + FROM batch_updated_events + ) WHERE rn = 1 +), + +-- Define ownership periods (start_time, end_time) for each trove owner +trove_ownership_history AS ( + SELECT + branch_id, + trove_id, + to_addr AS owner, + evt_block_time AS start_time, + LEAD(evt_block_time) OVER (PARTITION BY branch_id, trove_id ORDER BY evt_block_time) AS end_time + FROM nft_transfer_events + WHERE to_addr != 0x0000000000000000000000000000000000000000 -- Exclude burns +), + +-- ============================================================================ +-- 3. CREATE SPINES AND FILL FORWARD +-- ============================================================================ + +-- Create a continuous sequence of dates to ensure we have a row for every day, even if no events occurred. +date_spine AS ( + SELECT x AS date + FROM UNNEST(SEQUENCE( + (SELECT MIN(date) FROM all_trove_updates), + DATE(CURRENT_TIMESTAMP AT TIME ZONE 'UTC') + )) t(x) +), + +-- Trove Spine: Every date for every trove since its first interaction (per branch) +trove_spine AS ( + SELECT t.branch_id, t.trove_id, d.date + FROM (SELECT branch_id, trove_id, MIN(date) as start_date FROM all_trove_updates GROUP BY 1, 2) t + CROSS JOIN date_spine d + WHERE d.date >= t.start_date +), + +-- Batch Spine: Every date for every batch since its inception (per branch) +batch_spine AS ( + SELECT b.branch_id, b.interest_batch_manager, d.date + FROM (SELECT branch_id, interest_batch_manager, MIN(date) as start_date FROM batch_updated_events GROUP BY 1, 2) b + CROSS JOIN date_spine d + WHERE d.date >= b.start_date +), + +-- Filled Trove States: Carry forward the last known values to subsequent days using IGNORE NULLS +trove_states_filled AS ( + SELECT + ts.branch_id, + ts.date, + ts.trove_id, + LAST_VALUE(dts.debt) IGNORE NULLS OVER ( + PARTITION BY ts.branch_id, ts.trove_id ORDER BY ts.date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ) AS debt, + LAST_VALUE(dts.coll) IGNORE NULLS OVER ( + PARTITION BY ts.branch_id, ts.trove_id ORDER BY ts.date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ) AS coll, + LAST_VALUE(dts.annual_interest_rate) IGNORE NULLS OVER ( + PARTITION BY ts.branch_id, ts.trove_id ORDER BY ts.date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ) AS annual_interest_rate, + LAST_VALUE(dts.shares) IGNORE NULLS OVER ( + PARTITION BY ts.branch_id, ts.trove_id ORDER BY ts.date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ) AS shares, + LAST_VALUE(dts.interest_batch_manager) IGNORE NULLS OVER ( + PARTITION BY ts.branch_id, ts.trove_id ORDER BY ts.date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ) AS interest_batch_manager, + LAST_VALUE(dts.update_type) IGNORE NULLS OVER ( + PARTITION BY ts.branch_id, ts.trove_id ORDER BY ts.date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ) AS update_type + FROM trove_spine ts + LEFT JOIN daily_trove_states_sparse dts + ON ts.branch_id = dts.branch_id AND ts.trove_id = dts.trove_id AND ts.date = dts.date +), + +-- Filled Batch States: Carry forward batch parameters (global debt, total shares) +batch_states_filled AS ( + SELECT + bs.branch_id, + bs.date, + bs.interest_batch_manager, + LAST_VALUE(dbs.batch_debt) IGNORE NULLS OVER ( + PARTITION BY bs.branch_id, bs.interest_batch_manager ORDER BY bs.date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ) AS batch_debt, + LAST_VALUE(dbs.batch_total_shares) IGNORE NULLS OVER ( + PARTITION BY bs.branch_id, bs.interest_batch_manager ORDER BY bs.date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ) AS batch_total_shares, + LAST_VALUE(dbs.batch_annual_interest_rate) IGNORE NULLS OVER ( + PARTITION BY bs.branch_id, bs.interest_batch_manager ORDER BY bs.date + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW + ) AS batch_annual_interest_rate + FROM batch_spine bs + LEFT JOIN daily_batch_states_sparse dbs + ON bs.branch_id = dbs.branch_id AND bs.interest_batch_manager = dbs.interest_batch_manager AND bs.date = dbs.date +), + +-- ============================================================================ +-- 4. RESOLVE OWNERSHIP AND CALCULATE DEBT +-- ============================================================================ + +-- Determine the owner for each trove on each specific date (per branch) +trove_ownership_resolved AS ( + SELECT + ts.branch_id, + ts.date, + ts.trove_id, + LOWER(CONCAT('0x', TO_HEX(toh.owner))) AS user + FROM trove_spine ts + JOIN trove_ownership_history toh + ON ts.branch_id = toh.branch_id + AND ts.trove_id = toh.trove_id + AND ts.date >= DATE(toh.start_time) + AND (toh.end_time IS NULL OR ts.date < DATE(toh.end_time)) +), + +-- Calculate effective debt: +-- For Regular troves: use the 'debt' field directly. +-- For Batched troves: calculate debt as (trove_shares * batch_debt) / batch_total_shares. +positions_resolved AS ( + SELECT + t.branch_id, + t.date, + COALESCE(o.user, CONCAT('UNKNOWN_', t.trove_id)) AS user, + t.trove_id, + t.coll AS entire_coll, + CASE + WHEN t.update_type = 'BATCHED' THEN + CASE + WHEN b.batch_total_shares > 0 THEN + (t.shares * b.batch_debt) / b.batch_total_shares + ELSE 0 + END + ELSE t.debt + END AS entire_debt, + CASE + WHEN t.update_type = 'BATCHED' THEN b.batch_annual_interest_rate + ELSE t.annual_interest_rate + END AS annual_interest_rate, + FALSE AS has_orphan_troves + FROM trove_states_filled t + LEFT JOIN trove_ownership_resolved o + ON t.branch_id = o.branch_id AND t.trove_id = o.trove_id AND t.date = o.date + LEFT JOIN batch_states_filled b + ON t.branch_id = b.branch_id AND t.interest_batch_manager = b.interest_batch_manager AND t.date = b.date +), + +-- ============================================================================ +-- 5. AGGREGATE BY USER AND CALCULATE INTEREST +-- ============================================================================ + +-- Aggregate positions by user per day per branch +continuous_positions_raw AS ( + SELECT + branch_id, + date, + user, + SUM(entire_debt) AS entire_debt, + SUM(entire_coll) AS entire_coll, + -- Compute debt-weighted average interest rate for the user + CASE + WHEN SUM(entire_debt) > 0 + THEN SUM(entire_debt * annual_interest_rate) / SUM(entire_debt) + ELSE 0 + END AS annual_interest_rate + FROM positions_resolved + WHERE (entire_debt > 0 OR entire_coll > 0) + GROUP BY branch_id, date, user +), + +-- Apply daily interest accrual (Simple Interest) +continuous_positions AS ( + SELECT + branch_id, + date, + user, + entire_debt * (1 + annual_interest_rate * + DATE_DIFF('day', + LAG(date, 1, date) OVER (PARTITION BY branch_id, user ORDER BY date), + date + ) / 365 + ) AS entire_debt, + entire_coll, + annual_interest_rate + FROM continuous_positions_raw +), + +-- Calculate daily changes (deltas) for reporting +daily_changes AS ( + SELECT + branch_id, + date, + user, + entire_debt, + entire_coll, + annual_interest_rate, + LAG(entire_debt, 1) OVER (PARTITION BY branch_id, user ORDER BY date) AS prev_debt, + LAG(entire_coll, 1) OVER (PARTITION BY branch_id, user ORDER BY date) AS prev_coll, + COALESCE(entire_debt, 0) - COALESCE(LAG(entire_debt, 1) OVER (PARTITION BY branch_id, user ORDER BY date), 0) AS debt_change, + COALESCE(entire_coll, 0) - COALESCE(LAG(entire_coll, 1) OVER (PARTITION BY branch_id, user ORDER BY date), 0) AS coll_change + FROM continuous_positions + WHERE entire_debt > 0 OR entire_coll > 0 +), + +-- ============================================================================ +-- 6. PRICES AND FINAL OUTPUT +-- ============================================================================ + +-- Daily Price Data from OpenBlockLabs (using shared BTC price for all branches) +daily_prices AS ( + SELECT + date, + price AS btc_price + FROM ( + SELECT + DATE(timestamp AT TIME ZONE 'UTC') AS date, + price, + ROW_NUMBER() OVER (PARTITION BY DATE(timestamp AT TIME ZONE 'UTC') ORDER BY timestamp DESC) as rn + FROM dune.openblocklabs.result_starknet_prices_daily + CROSS JOIN shared_cfg + WHERE contract_address = shared_cfg.btc_price_addr + ) + WHERE rn = 1 +), + +-- Latest Price Fallback +latest_price AS ( + SELECT price AS btc_price + FROM ( + SELECT price, ROW_NUMBER() OVER (ORDER BY timestamp DESC) AS rn + FROM dune.openblocklabs.result_starknet_prices_daily + CROSS JOIN shared_cfg + WHERE contract_address = shared_cfg.btc_price_addr + ) t WHERE rn = 1 +), + +-- Merged Price Data +price_data AS ( + SELECT + d.date, + COALESCE(dp.btc_price, lp.btc_price) AS btc_price + FROM (SELECT DISTINCT date FROM daily_changes) d + LEFT JOIN daily_prices dp ON dp.date = d.date + CROSS JOIN latest_price lp +), + +-- Final Formatting +final_output AS ( + SELECT + dc.branch_id, + dc.date, + dc.user, + cfg.market_address, + scfg.market_name, + scfg.market_owner, + cfg.asset_addr AS asset, + cfg.asset_symbol, + cfg.asset_decimals, + scfg.usdu_addr AS debt_asset, + scfg.debt_asset_symbol, + scfg.debt_decimals, + 1.0 AS latest_accumulator, + scfg.protocol_scale, + pd.btc_price AS asset_price, + scfg.debt_price AS debt_price, + dc.entire_coll AS total_supplied_raw, + dc.entire_debt AS total_borrowed_raw, + dc.coll_change AS raw_amount_deposit, + dc.coll_change AS adjusted_amount_deposit, + dc.debt_change AS raw_amount_borrowed, + dc.debt_change AS adjusted_amount_borrowed, + dc.entire_coll AS total_supplied_adjusted, + dc.entire_debt AS total_borrowed_adjusted, + dc.entire_coll AS total_supplied_tokens, + dc.entire_debt AS total_borrowed_tokens, + dc.entire_coll * pd.btc_price AS total_supplied_usd, + dc.entire_debt * scfg.debt_price AS total_borrowed_usd, + (dc.entire_coll * pd.btc_price) - (dc.entire_debt * scfg.debt_price) AS net_usd, + dc.annual_interest_rate AS effective_apr_daily, + dc.entire_debt * (dc.annual_interest_rate / 365) AS interest_tokens_daily, + dc.entire_debt * (dc.annual_interest_rate / 365) * scfg.debt_price AS interest_usd_daily, + FALSE AS has_orphan_troves + FROM daily_changes dc + INNER JOIN cfg ON dc.branch_id = cfg.branch_id + CROSS JOIN shared_cfg scfg + LEFT JOIN price_data pd ON pd.date = dc.date +) + +-- Final Selection and Ordering +SELECT + branch_id, + date, + user, + market_address, + market_name, + asset, + asset_symbol, + asset_decimals, + total_supplied_tokens, + total_borrowed_tokens, + debt_asset, + debt_asset_symbol, + debt_decimals, + total_supplied_usd, + total_borrowed_usd, + effective_apr_daily, + interest_tokens_daily, + interest_usd_daily +FROM final_output +WHERE + (total_supplied_adjusted IS NOT NULL AND total_supplied_adjusted <> 0) + OR + (total_borrowed_adjusted IS NOT NULL AND total_borrowed_adjusted <> 0) +ORDER BY date DESC, branch_id, user diff --git a/dune_queries/points/points_query.sql b/dune_queries/points/points_query.sql new file mode 100644 index 0000000..ceb8ca7 --- /dev/null +++ b/dune_queries/points/points_query.sql @@ -0,0 +1,716 @@ +-- Uncap Protocol - 6-Hourly Trove Snapshots +-- Network: Starknet Mainnet +-- Purpose: Track debt, collateral, and interest rate for each trove every 6 hours + +with + +-- Shared constants (Ekubo, prices, USDU/USDC tokens) +shared_cfg as ( + select + 0x00000005dd3d2f4429af886cd1a3b08289dbcea99a294197e9eb43b0e0325b4b as ekubo_core, + 0x07b696af58c967c1b14c9dde0ace001720635a660a8e90c565ea459345318b30 as ekubo_positions_nft, + 0x02f94539f80158f9a48a7acf3747718dfbec9b6f639e2742c1fb44ae7ab5aa04 as usdu_token, + 0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8 as usdc_token, + 0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac as wbtc_price_addr +), + +-- Per-branch configuration +cfg as ( + -- WBTC Branch + select + 'WBTC' as branch_id, + 0x038a9949900e7905f648cf0b50335efdac16a8acb8dfc870835da21c3f68e934 as events_emitter, + 0x04a1e17f1e4d5fdd839fa6a4c52a742b9d10871028b6ffcfb2f1de8dd5dc2cd4 as trove_nft, + 0x001ba4a9e2e86a41c6ed15016eda0404d12bf7b01052cccff1ace84d818335c7 as stability_pool, + 2759369 as deployment_block + union all + -- solvBTC Branch + select + 'solvBTC' as branch_id, + 0x053fc2fb416e7d5e8e44594c989b638ffabf8a8f804a2041e564db7febccc2a9 as events_emitter, + 0x034db33f4917ad9f8ca423e7295c303bd21cdf1724749576e35b022d9d70d006 as trove_nft, + 0x0154d14c879ce7dfe559628ed1abff2df38974efd27abf10d9236d05c6aa4741 as stability_pool, + 5572500 as deployment_block + union all + -- tBTC Branch + select + 'tBTC' as branch_id, + 0x02456ca1b9fcf7851577d92c3a4bc26b59c4f44ee222d67ae7032350ee195ac1 as events_emitter, + 0x063ed1e2a28424366a10123713d601ce0158fc0a7026ace0cf98bd1872d93f1e as trove_nft, + 0x000a36230f3d17cba0acb9635810209fe430c26ae585cbfd61e39cac0a9af6fc as stability_pool, + 5572000 as deployment_block +), + +-- Get deployment date for time series (rounded to nearest 6-hour boundary) +-- Use MIN deployment_block across all branches +deployment_date as ( + select date_trunc('hour', min(time)) - + (cast(extract(hour from min(time)) as integer) % 6) * interval '1' hour as start_hour + from starknet.blocks + where number >= (select min(deployment_block) from cfg where deployment_block > 0) +), + +-- ============================================================================ +-- 1. EXTRACT RAW EVENTS +-- ============================================================================ + +-- Regular Trove Updates (Standard TroveUpdated events) +trove_updated_events as ( + select + cfg.branch_id, + e.block_time, + e.block_number, + e.event_index, + 'REGULAR' as update_type, + lower(concat('0x', + "right"(substring(to_hex(e.data[2]), 3), 32), + "right"(substring(to_hex(e.data[1]), 3), 32) + )) as trove_id, + cast(bytearray_to_uint256(e.data[3]) as double) / 1e18 as debt, + cast(bytearray_to_uint256(e.data[5]) as double) / 1e18 as collateral, + cast(bytearray_to_uint256(e.data[9]) as double) / 1e16 as interest_rate, + cast(bytearray_to_uint256(e.data[7]) as double) / 1e18 as stake, + NULL as shares, + NULL as interest_batch_manager + from starknet.events e + inner join cfg on e.from_address = cfg.events_emitter + where e.keys[1] = 0x01babc9e592593f609d7e88cca6a04e21db92f5faf85fb83153cc9b369b2b3e6 + and e.block_number >= cfg.deployment_block +), + +-- Batched Trove Updates (BatchedTroveUpdated events) +batched_trove_updated_events as ( + select + cfg.branch_id, + e.block_time, + e.block_number, + e.event_index, + 'BATCHED' as update_type, + lower(concat('0x', + "right"(substring(to_hex(e.data[2]), 3), 32), + "right"(substring(to_hex(e.data[1]), 3), 32) + )) as trove_id, + NULL as debt, + cast(bytearray_to_uint256(e.data[6]) as double) / 1e18 as collateral, + NULL as interest_rate, + cast(bytearray_to_uint256(e.data[8]) as double) / 1e18 as stake, + -- Standard ordering: + -- trove_id: data[1], data[2] + -- interest_batch_manager: data[3] + -- batch_debt_shares: data[4], data[5] + -- coll: data[6], data[7] + -- stake: data[8], data[9] + cast(bytearray_to_uint256(e.data[4]) as double) / 1e18 as shares, + e.data[3] as interest_batch_manager + from starknet.events e + inner join cfg on e.from_address = cfg.events_emitter + where e.keys[1] = 0x03c054baf812ebdae34d56f1322bd89e0b0c4aed0829fe0e7658fce049d25f71 + and e.block_number >= cfg.deployment_block +), + +-- Batch Updates (BatchUpdated events) +batch_updated_events as ( + select + cfg.branch_id, + e.block_time, + e.block_number, + e.event_index, + e.data[1] as interest_batch_manager, + cast(bytearray_to_uint256(e.data[3]) as double) / 1e18 as batch_debt, + -- annual_interest_rate is data[7], data[8] + cast(bytearray_to_uint256(e.data[7]) as double) / 1e16 as batch_interest_rate, + -- total_debt_shares is data[11], data[12] + cast(bytearray_to_uint256(e.data[11]) as double) / 1e18 as batch_total_shares + from starknet.events e + inner join cfg on e.from_address = cfg.events_emitter + where e.keys[1] = 0x039a6ae9867ab3ddba84021a987ac7776389a6cc5bad711eda5e4a44d1a2fe80 + and e.block_number >= cfg.deployment_block +), + +-- Combine regular and batched updates +all_trove_updates as ( + select * from trove_updated_events + union all + select * from batched_trove_updated_events +), + +-- Get NFT transfer events to track ownership +nft_transfers as ( + select + cfg.branch_id, + e.block_time, + e.block_number, + e.event_index, + -- Extract to address (new owner) from keys[3] + e.keys[3] as owner, + -- Extract token_id (u256 = keys[4] low, keys[5] high) + lower(concat('0x', + "right"(substring(to_hex(e.keys[5]), 3), 32), + "right"(substring(to_hex(e.keys[4]), 3), 32) + )) as trove_id + from starknet.events e + inner join cfg on e.from_address = cfg.trove_nft + where e.keys[1] = 0x0099cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9 + and e.block_number >= cfg.deployment_block +), + +-- Get StabilityPool deposit events (per-branch) +sp_deposit_events as ( + select + cfg.branch_id, + e.block_time, + e.block_number, + e.event_index, + e.data[1] as user_address, + cast(bytearray_to_uint256(e.data[2]) as double) / 1e18 as deposit_amount + from starknet.events e + inner join cfg on e.from_address = cfg.stability_pool + where e.keys[1] = 0x0056985e064b256ecdb949713158ae0953473139aaf1fd57197a852fe36c4f00 + and e.block_number >= cfg.deployment_block +), + +-- Track Ekubo NFT position transfers (shared across all branches) +ekubo_position_transfers as ( + select + block_time, + block_number, + event_index, + transaction_hash, + data[1] as from_address, + data[2] as to_address, + lower(to_hex(data[3])) as position_id + from starknet.events + cross join shared_cfg + where from_address = shared_cfg.ekubo_positions_nft + and keys[1] = 0x0099cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9 + and block_number >= (select min(deployment_block) from cfg where deployment_block > 0) +), + +-- Track Ekubo position liquidity updates (shared across all branches) +ekubo_position_updates as ( + select + block_time, + block_number, + event_index, + transaction_hash, + lower(to_hex(data[7])) as position_id, + data[1] as locker_address, + cast(bytearray_to_uint256(data[14]) as double) as amount0_mag, + cast(bytearray_to_uint256(data[15]) as bigint) as amount0_sign, + cast(bytearray_to_uint256(data[16]) as double) as amount1_mag, + cast(bytearray_to_uint256(data[17]) as bigint) as amount1_sign, + data[2] as token0, + data[3] as token1, + cast(bytearray_to_uint256(data[5]) as bigint) as tick_spacing, + cast(bytearray_to_uint256(data[8]) as bigint) as lower_mag, + cast(bytearray_to_uint256(data[9]) as bigint) as lower_sign, + cast(bytearray_to_uint256(data[10]) as bigint) as upper_mag, + cast(bytearray_to_uint256(data[11]) as bigint) as upper_sign + from starknet.events + cross join shared_cfg + where from_address = shared_cfg.ekubo_core + and keys[1] = 0x03a7adca3546c213ce791fabf3b04090c163e419c808c9830fb343a4a395946e + and block_number >= (select min(deployment_block) from cfg where deployment_block > 0) + and data[2] = shared_cfg.usdu_token + and data[3] = shared_cfg.usdc_token + and cast(bytearray_to_uint256(data[5]) as bigint) = 100 +), + +-- Filter positions to relevant price range +ekubo_filtered_positions as ( + select + block_time, + block_number, + event_index, + transaction_hash, + position_id, + locker_address, + amount0_mag, + amount0_sign, + amount1_mag, + amount1_sign + from ekubo_position_updates + where lower_sign = 1 + and lower_mag < 27682400 + and upper_sign = 1 + and upper_mag > 27581700 +), + +-- Calculate liquidity deltas per position +ekubo_position_liquidity_changes as ( + select + block_time, + block_number, + event_index, + transaction_hash, + position_id, + locker_address, + (case when amount0_sign = 0 then amount0_mag / 1e18 else -amount0_mag / 1e18 end) as usdu_delta, + (case when amount1_sign = 0 then amount1_mag / 1e6 else -amount1_mag / 1e6 end) as usdc_delta, + (case when amount0_sign = 0 then amount0_mag / 1e18 else -amount0_mag / 1e18 end + + case when amount1_sign = 0 then amount1_mag / 1e6 else -amount1_mag / 1e6 end) as total_usd_delta + from ekubo_filtered_positions +), + +-- Calculate cumulative liquidity per position over time +ekubo_position_cumulative as ( + select + block_time, + block_number, + event_index, + position_id, + locker_address, + usdu_delta, + usdc_delta, + total_usd_delta, + -- Since we cannot accurately track the split between USDU and USDC without tracking all swaps, + -- we stop tracking individual token balances to avoid negative values from IL. + -- We rely solely on cumulative_usd_value. + 0 as cumulative_usdu, + 0 as cumulative_usdc, + -- Clamp the cumulative USD value to 0. If it drops below 0 (due to fee accrual or tiny impermanent loss on withdrawal), + -- we assume the position is closed/empty. We use 1.0 USD as a dust threshold. + case when sum(total_usd_delta) over ( + partition by position_id + order by block_time, event_index + rows between unbounded preceding and current row + ) < 1.0 then 0 + else sum(total_usd_delta) over ( + partition by position_id + order by block_time, event_index + rows between unbounded preceding and current row + ) end as cumulative_usd_value + from ekubo_position_liquidity_changes +), + +-- ============================================================================ +-- 2. CREATE TIME SPINES AND AGGREGATE +-- ============================================================================ + +-- Get all unique Ekubo positions +ekubo_positions as ( + select distinct position_id + from ekubo_position_cumulative +), + +-- Create 6-hourly time series +time_seq as ( + select + sequence( + cast((select start_hour from deployment_date) as timestamp), + date_trunc('hour', cast(now() as timestamp)), + interval '6' hour + ) as time +), + +six_hour_slots as ( + select + time.time as slot_start + from time_seq + cross join unnest(time) as time(time) +), + +-- Get all unique troves (per branch) +troves as ( + select distinct branch_id, trove_id + from all_trove_updates +), + +-- Get all unique batches (per branch) +batches as ( + select distinct branch_id, interest_batch_manager + from batch_updated_events +), + +-- Get all unique users with their branch associations +-- Note: A user can have positions in multiple branches +all_branch_users as ( + select distinct branch_id, owner as user_address + from nft_transfers + where owner != 0x0000000000000000000000000000000000000000 + union + select distinct branch_id, user_address + from sp_deposit_events +), + +-- Get all unique LP users (no branch association) +all_lp_users as ( + select distinct to_address as user_address + from ekubo_position_transfers + where to_address != 0x0000000000000000000000000000000000000000 +), + +-- Cross join slots and troves (per branch) +slot_trove_combinations as ( + select + s.slot_start, + t.branch_id, + t.trove_id + from six_hour_slots s + cross join troves t +), + +-- Cross join slots and batches (per branch) +slot_batch_combinations as ( + select + s.slot_start, + b.branch_id, + b.interest_batch_manager + from six_hour_slots s + cross join batches b +), + +-- Cross join slots and branch users (for SP deposits per branch) +slot_branch_user_combinations as ( + select + s.slot_start, + u.branch_id, + u.user_address + from six_hour_slots s + cross join all_branch_users u +), + +-- Cross join slots and LP users (no branch) +slot_lp_user_combinations as ( + select + s.slot_start, + u.user_address + from six_hour_slots s + cross join all_lp_users u +), + +-- Cross join slots and Ekubo positions +slot_position_combinations as ( + select + s.slot_start, + p.position_id + from six_hour_slots s + cross join ekubo_positions p +), + +-- For each 6-hour slot, branch, and trove, get the most recent state +latest_trove_states as ( + select + stc.slot_start, + stc.branch_id, + stc.trove_id, + te.debt, + te.collateral, + te.stake, + te.interest_rate, + te.shares, + te.interest_batch_manager, + te.update_type, + row_number() over ( + partition by stc.slot_start, stc.branch_id, stc.trove_id + order by te.block_time desc, te.event_index desc + ) as rn + from slot_trove_combinations stc + left join all_trove_updates te + on stc.branch_id = te.branch_id + and stc.trove_id = te.trove_id + and te.block_time <= stc.slot_start + interval '6' hour + where te.trove_id is not null +), + +-- For each 6-hour slot, branch, and batch, get the most recent state +latest_batch_states as ( + select + sbc.slot_start, + sbc.branch_id, + sbc.interest_batch_manager, + be.batch_debt, + be.batch_total_shares, + be.batch_interest_rate, + row_number() over ( + partition by sbc.slot_start, sbc.branch_id, sbc.interest_batch_manager + order by be.block_time desc, be.event_index desc + ) as rn + from slot_batch_combinations sbc + left join batch_updated_events be + on sbc.branch_id = be.branch_id + and sbc.interest_batch_manager = be.interest_batch_manager + and be.block_time <= sbc.slot_start + interval '6' hour + where be.interest_batch_manager is not null +), + +-- For each 6-hour slot, branch, and trove, get the most recent owner +latest_owners as ( + select + stc.slot_start, + stc.branch_id, + stc.trove_id, + nt.owner, + row_number() over ( + partition by stc.slot_start, stc.branch_id, stc.trove_id + order by nt.block_time desc, nt.event_index desc + ) as rn + from slot_trove_combinations stc + left join nft_transfers nt + on stc.branch_id = nt.branch_id + and stc.trove_id = nt.trove_id + and nt.block_time <= stc.slot_start + interval '6' hour + where nt.trove_id is not null +), + +-- For each 6-hour slot, branch, and user, get the most recent SP deposit +latest_sp_deposits as ( + select + suc.slot_start, + suc.branch_id, + suc.user_address, + sp.deposit_amount, + row_number() over ( + partition by suc.slot_start, suc.branch_id, suc.user_address + order by sp.block_time desc, sp.event_index desc + ) as rn + from slot_branch_user_combinations suc + left join sp_deposit_events sp + on suc.branch_id = sp.branch_id + and suc.user_address = sp.user_address + and sp.block_time <= suc.slot_start + interval '6' hour + where sp.user_address is not null +), + +-- For each time slot and position, get the most recent liquidity state +latest_position_liquidity as ( + select + spc.slot_start, + spc.position_id, + epc.cumulative_usdu, + epc.cumulative_usdc, + epc.cumulative_usd_value, + row_number() over ( + partition by spc.slot_start, spc.position_id + order by epc.block_time desc, epc.event_index desc + ) as rn + from slot_position_combinations spc + left join ekubo_position_cumulative epc + on spc.position_id = epc.position_id + and epc.block_time <= spc.slot_start + interval '6' hour + where epc.position_id is not null +), + +-- For each time slot and position, get the most recent owner +latest_position_owners as ( + select + spc.slot_start, + spc.position_id, + ept.to_address as owner, + row_number() over ( + partition by spc.slot_start, spc.position_id + order by ept.block_time desc, ept.event_index desc + ) as rn + from slot_position_combinations spc + left join ekubo_position_transfers ept + on spc.position_id = ept.position_id + and ept.block_time <= spc.slot_start + interval '6' hour + and ept.to_address != 0x0000000000000000000000000000000000000000 + where ept.position_id is not null +), + +-- Combine position liquidity and ownership, then aggregate by user +user_lp_positions as ( + select + lpl.slot_start, + lpo.owner as user_address, + sum(lpl.cumulative_usd_value) as total_lp_value_usd + from latest_position_liquidity lpl + inner join latest_position_owners lpo + on lpl.slot_start = lpo.slot_start + and lpl.position_id = lpo.position_id + and lpo.rn = 1 + where lpl.rn = 1 + and lpl.cumulative_usd_value > 0 + group by lpl.slot_start, lpo.owner +), + +-- ============================================================================ +-- 3. RESOLVE TROVE STATE AND COMBINE +-- ============================================================================ + +-- Combine trove state, ownership, and batch data to resolve final values (per branch) +trove_snapshots as ( + select + ls.branch_id, + ls.slot_start, + ls.trove_id, + lo.owner as user_address, + -- Resolved Debt Calculation + case + when ls.update_type = 'BATCHED' then + case + when lbs.batch_total_shares > 0 then + (ls.shares * lbs.batch_debt) / lbs.batch_total_shares + else 0 + end + else ls.debt + end as debt, + ls.collateral, + ls.stake, + -- Resolved Interest Rate + case + when ls.update_type = 'BATCHED' then lbs.batch_interest_rate + else ls.interest_rate + end as interest_rate + from latest_trove_states ls + left join latest_owners lo + on ls.slot_start = lo.slot_start + and ls.branch_id = lo.branch_id + and ls.trove_id = lo.trove_id + and lo.rn = 1 + left join latest_batch_states lbs + on ls.slot_start = lbs.slot_start + and ls.branch_id = lbs.branch_id + and ls.interest_batch_manager = lbs.interest_batch_manager + and lbs.rn = 1 + where ls.rn = 1 +), + +-- Get users with SP deposits but no troves in that branch +sp_only_users as ( + select distinct + suc.branch_id, + suc.slot_start, + suc.user_address, + coalesce(lsd.deposit_amount, 0) as sp_deposit + from slot_branch_user_combinations suc + left join latest_sp_deposits lsd + on suc.branch_id = lsd.branch_id + and suc.user_address = lsd.user_address + and suc.slot_start = lsd.slot_start + and lsd.rn = 1 + where not exists ( + select 1 + from trove_snapshots ts + where ts.branch_id = suc.branch_id + and ts.user_address = suc.user_address + and ts.slot_start = suc.slot_start + ) + and coalesce(lsd.deposit_amount, 0) > 0 +), + +-- Combine trove snapshots with branch-specific SP deposits +-- LP is handled separately to avoid duplication +final_snapshots as ( + -- Trove owners with their branch-specific SP deposits (no LP here) + select + ts.branch_id, + ts.slot_start, + ts.trove_id, + ts.user_address, + ts.debt, + ts.collateral, + ts.interest_rate, + coalesce(lsd.deposit_amount, 0) as sp_deposit, + 0 as lp_value_usd + from trove_snapshots ts + left join latest_sp_deposits lsd + on ts.branch_id = lsd.branch_id + and ts.user_address = lsd.user_address + and ts.slot_start = lsd.slot_start + and lsd.rn = 1 + + union all + + -- Users with only SP deposits in a branch (no troves in that branch) + select + branch_id, + slot_start, + null as trove_id, + user_address, + 0 as debt, + 0 as collateral, + 0 as interest_rate, + sp_deposit, + 0 as lp_value_usd + from sp_only_users + + union all + + -- LP-only rows (separate, not duplicated across branches) + select + 'LP' as branch_id, + slot_start, + null as trove_id, + user_address, + 0 as debt, + 0 as collateral, + 0 as interest_rate, + 0 as sp_deposit, + total_lp_value_usd as lp_value_usd + from user_lp_positions +), + +-- Get WBTC prices (deduplicated daily) - using shared_cfg +daily_prices as ( + select + date, + price as wbtc_price + from ( + select + date(timestamp at time zone 'UTC') as date, + price, + row_number() over (partition by date(timestamp at time zone 'UTC') order by timestamp desc) as rn + from dune.openblocklabs.result_starknet_prices_daily + cross join shared_cfg + where contract_address = shared_cfg.wbtc_price_addr + ) + where rn = 1 +), + +-- Get latest WBTC price for fallback +latest_price as ( + select price as wbtc_price + from ( + select price, + row_number() over (order by timestamp desc) as rn + from dune.openblocklabs.result_starknet_prices_daily + cross join shared_cfg + where contract_address = shared_cfg.wbtc_price_addr + ) t + where rn = 1 +), + +-- Combine snapshots with prices +snapshots_with_prices as ( + select + fs.branch_id, + fs.slot_start, + fs.trove_id, + fs.user_address, + fs.collateral as collateral_btc, + coalesce(dp.wbtc_price, lp.wbtc_price) as wbtc_price, + fs.collateral * coalesce(dp.wbtc_price, lp.wbtc_price) as collateral_usd, + fs.debt, + fs.interest_rate, + fs.sp_deposit, + fs.lp_value_usd + from final_snapshots fs + left join daily_prices dp + on date(fs.slot_start) = dp.date + cross join latest_price lp +) + +select + branch_id, + slot_start as snapshot_time, + trove_id, + user_address as owner, + collateral_btc, + collateral_usd, + debt, + -- Collateral ratio: debt / collateral in USD + case + when collateral_usd > 0 then debt / collateral_usd + else null + end as collateral_ratio, + interest_rate, + sp_deposit as in_stability_pool, + lp_value_usd, + wbtc_price +from snapshots_with_prices +where (debt > 0.000001 or sp_deposit > 0 or lp_value_usd > 0) -- Show active troves or users with SP/LP deposits + -- OPTIONAL: Uncomment to filter to last N days for weekly reports + -- and slot_start >= current_timestamp - interval '7' day +order by snapshot_time desc, branch_id, owner asc, trove_id asc diff --git a/dune_queries/uncap/interest_rates.sql b/dune_queries/uncap/interest_rates.sql new file mode 100644 index 0000000..54df114 --- /dev/null +++ b/dune_queries/uncap/interest_rates.sql @@ -0,0 +1,132 @@ +-- Uncap Protocol - Interest Rates Over Time +-- Network: Starknet Mainnet +-- Purpose: Track debt-weighted average interest rates hourly +-- Simplified version (no batch management) for USDU + +with + +-- Get deployment date for time series +deployment_date as ( + select min(date_trunc('hour', time)) as start_hour + from starknet.blocks + where number >= 2759369 +), + +time_seq as ( + select + sequence( + CAST((select start_hour from deployment_date) as timestamp), + date_trunc('hour', cast(now() as timestamp)), + interval '1' hour + ) as time +), + +hours as ( + select + time.time as hour + from time_seq + cross join unnest(time) as time(time) +), + +hourly_rates as ( + select + *, + lead(hour, 1, date_trunc('hour', cast(now() as timestamp)) + interval '1' hour) over (partition by trove_id, collateral_type order by hour asc) as next_hour + from ( + select + date_trunc('hour', block_time) as hour, + trove_id, + collateral_type, + max_by(interest_rates, (block_number, tx_index)) as interest_rates + from + query_6091847 -- trove_operations + group by 1, 2, 3 + ) +), + +filled_rates as ( + select + h.hour, + c.trove_id, + c.collateral_type, + c.interest_rates + from + hourly_rates c + inner join + hours h + on c.hour <= h.hour + and h.hour < c.next_hour +), + +-- Get hourly debts from trove_debt_and_interest query +-- TODO: Replace query_YYYYYY with actual query ID for trove_debt_and_interest.sql +hourly_debts as ( + select + *, + lead(hour, 1, date_trunc('hour', cast(now() as timestamp)) + interval '1' hour) over (partition by trove_id, collateral_type order by hour asc) as next_hour + from ( + select + date_trunc('hour', block_time) as hour, + trove_id, + collateral_type, + max_by(debt, (block_number, tx_index)) as debt + from + query_6091903 -- trove_debt_and_interest + group by 1, 2, 3 + ) +), + +filled_debts as ( + select + h.hour, + c.trove_id, + c.collateral_type, + c.debt + from + hourly_debts c + inner join + hours h + on c.hour <= h.hour + and h.hour < c.next_hour +), + +combine_with_debts as ( + select + fr.*, + fd.debt + from + filled_rates fr + inner join + filled_debts fd + on fr.hour = fd.hour + and fr.trove_id = fd.trove_id + and fr.collateral_type = fd.collateral_type +), + +hourly_rates_summary as ( + select + date_trunc('hour', hour) as hour, + collateral_type, + (sum(interest_rates * debt) / sum(debt))/100 as hourly_rates, + -- Daily rates: moving 24h window from this hour backwards + (sum(sum(interest_rates * debt)) over (partition by collateral_type order by date_trunc('hour', hour) rows between 24 preceding and current row) / + sum(sum(debt)) over (partition by collateral_type order by date_trunc('hour', hour) rows between 24 preceding and current row))/100 as daily_rates, + -- Monthly rates: moving 30-day window from this hour backwards + (sum(sum(interest_rates * debt)) over (partition by collateral_type order by date_trunc('hour', hour) rows between 720 preceding and current row) / + sum(sum(debt)) over (partition by collateral_type order by date_trunc('hour', hour) rows between 720 preceding and current row))/100 as monthly_rates + from + combine_with_debts + where interest_rates > 0 and debt > 0 + and hour >= date_trunc('hour', now() - interval '30' day) + group by 1, 2 +) + +select + hour, + collateral_type, + hourly_rates, + daily_rates, + monthly_rates +from hourly_rates_summary +where hour >= date_trunc('hour', now() - interval '30' day) +order by hour desc, lower(collateral_type) desc diff --git a/dune_queries/uncap/interest_rates/trove_operations.sql b/dune_queries/uncap/interest_rates/trove_operations.sql new file mode 100644 index 0000000..1247086 --- /dev/null +++ b/dune_queries/uncap/interest_rates/trove_operations.sql @@ -0,0 +1,44 @@ +-- Uncap Protocol - Trove Operations +-- Network: Starknet Mainnet +-- Purpose: Track all trove operations (open, close, adjust, etc.) +-- Equivalent to query_5155365 for Liquity v2 + +with + +cfg as ( + select + 0x038a9949900e7905f648cf0b50335efdac16a8acb8dfc870835da21c3f68e934 as events_emitter, + 2759369 as deployment_block +) + +select + from_address as contract_address, + 'WBTC' as collateral_type, + transaction_hash as tx_hash, + event_index as tx_index, + block_time, + block_number, + -- Extract trove_id (u256 = data[1] low, data[2] high) + lower(concat('0x', + "right"(substring(to_hex(data[2]), 3), 32), + "right"(substring(to_hex(data[1]), 3), 32) + )) as trove_id, + -- Extract operation type (u8 stored as felt = data[3]) + cast(bytearray_to_uint256(data[3]) as bigint) as operation, + -- Extract annual interest rate (u256 = data[4] low, data[5] high) + cast(bytearray_to_uint256(data[4]) as double) / 1e16 as interest_rates, + -- Extract debt_increase_from_redist (u256 = data[6] low, data[7] high) + cast(bytearray_to_uint256(data[6]) as double) / 1e18 as debt_increase_from_redist, + -- Extract debt_increase_from_upfront_fee (u256 = data[8] low, data[9] high) + cast(bytearray_to_uint256(data[8]) as double) / 1e18 as upfront_fees, + -- Extract debt_change_from_operation (i257 = data[10] low, data[11] high, data[12] sign) + cast(bytearray_to_uint256(data[10]) as double) / 1e18 as debt_change, + -- Extract coll_increase_from_redist (u256 = data[13] low, data[14] high) + cast(bytearray_to_uint256(data[13]) as double) / 1e18 as coll_increase_from_redist, + -- Extract coll_change_from_operation (i257 = data[15] low, data[16] high, data[17] sign) + cast(bytearray_to_uint256(data[15]) as double) / 1e18 as collateral_change +from starknet.events +where from_address = (select events_emitter from cfg) + and keys[1] = 0x02c0b995ca82e6ae9ef5d19fd0659d7748540971842bd71843cfc904d240c4bf -- TroveOperation event + and block_number >= (select deployment_block from cfg) +order by block_number desc, event_index desc diff --git a/dune_queries/uncap/interest_rates/trove_updates.sql b/dune_queries/uncap/interest_rates/trove_updates.sql new file mode 100644 index 0000000..95b0b68 --- /dev/null +++ b/dune_queries/uncap/interest_rates/trove_updates.sql @@ -0,0 +1,39 @@ +-- Uncap Protocol - Trove Updates (Debt and Interest Rates) +-- Network: Starknet Mainnet +-- Purpose: Track debt and interest rates for all troves over time +-- Simplified version (no batch management) equivalent to query_5720005 for Liquity v2 + +with + +cfg as ( + select + 0x038a9949900e7905f648cf0b50335efdac16a8acb8dfc870835da21c3f68e934 as events_emitter, + 2759369 as deployment_block +) + +select + 'WBTC' as collateral_type, + transaction_hash as tx_hash, + event_index as tx_index, + block_time, + block_number, + -- Extract trove_id (u256 = data[1] low, data[2] high) + lower(concat('0x', + "right"(substring(to_hex(data[2]), 3), 32), + "right"(substring(to_hex(data[1]), 3), 32) + )) as trove_id, + -- Extract debt (u256 = data[3] low, data[4] high) + cast(bytearray_to_uint256(data[3]) as double) / 1e18 as debt, + -- Extract collateral (u256 = data[5] low, data[6] high) + cast(bytearray_to_uint256(data[5]) as double) / 1e18 as collateral, + -- Extract stake (u256 = data[7] low, data[8] high) + cast(bytearray_to_uint256(data[7]) as double) / 1e18 as stake, + -- Extract annual interest rate (u256 = data[9] low, data[10] high) + cast(bytearray_to_uint256(data[9]) as double) / 1e16 as interest_rates, + 0x0000000000000000000000000000000000000000 as interest_batch_manager, -- No batch manager in simplified version + 'troveUpdated' as event +from starknet.events +where from_address = (select events_emitter from cfg) + and keys[1] = 0x01babc9e592593f609d7e88cca6a04e21db92f5faf85fb83153cc9b369b2b3e6 -- TroveUpdated event + and block_number >= (select deployment_block from cfg) +order by block_number desc, event_index desc diff --git a/dune_queries/uncap/protocol_revenue.sql b/dune_queries/uncap/protocol_revenue.sql new file mode 100644 index 0000000..330fefb --- /dev/null +++ b/dune_queries/uncap/protocol_revenue.sql @@ -0,0 +1,176 @@ +-- Uncap Protocol - Protocol Revenue +-- Network: Starknet Mainnet +-- Purpose: Track protocol revenue from redemption fees, interest, and liquidations +-- Start date: October 6, 2025 + +with + +cfg as ( + select + 0x02f94539f80158f9a48a7acf3747718dfbec9b6f639e2742c1fb44ae7ab5aa04 as usdu_token, + 0x001ba4a9e2e86a41c6ed15016eda0404d12bf7b01052cccff1ace84d818335c7 as stability_pool, + 0x0477a98816f0298D678a8C74Bd06E898Ee4E3bB62FdB9bb05c397f3135aE8398 as pil_address, + 0x038a9949900e7905f648cf0b50335efdac16a8acb8dfc870835da21c3f68e934 as events_emitter, + 2759369 as deployment_block +), + +time_seq AS ( + select + sequence( + CAST('2025-10-06' as timestamp), + date_trunc('day', cast(now() as timestamp)), + interval '1' day + ) as time +), + +days AS ( + select + time.time as day + from time_seq + cross join unnest(time) as time(time) +), + +-- Redemption fees paid to troves (in WWBTC) +-- Event: RedemptionFeePaidToTrove +-- keys[1] = event selector +-- data[1], data[2] = trove_id (u256) +-- data[3], data[4] = coll_fee (u256) +wbtc_fee as ( + select + date_trunc('minute', block_time) as minute, + transaction_hash as evt_tx_hash, + cast(bytearray_to_uint256(data[3]) as double) / 1e18 as fee -- WWBTC has 18 decimals + from starknet.events + where from_address = (select events_emitter from cfg) + and keys[1] = 0x03b605674b6c4bd98149476fb8ed7db31a620da8d46f77070759e7b152252ef6 -- RedemptionFeePaidToTrove event selector + and block_number >= (select deployment_block from cfg) +), + +-- Add WBTC price to redemption fees +add_price as ( + select + wf.minute, + wf.evt_tx_hash, + wf.fee, + wp.wbtc_price as price, + wf.fee * wp.wbtc_price as fee_usd + from + wbtc_fee wf + left join ( + select + DATE(timestamp AT TIME ZONE 'UTC') as day, + price as wbtc_price + from dune.openblocklabs.result_starknet_prices_daily + where contract_address = 0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac + ) wp + on date_trunc('day', wf.minute) = wp.day +), + +-- Interest rewards minted to StabilityPool (75% of interest) +-- Using Transfer events where from = 0x0 (mint) and to = StabilityPool +-- Event: Transfer +-- keys[1] = event selector (0x99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9) +-- keys[2] = from_address +-- keys[3] = to_address +-- data[1], data[2] = amount (u256) +interest_rewards as ( + select + case + when keys[3] = (select stability_pool from cfg) then 'WBTC' + when keys[3] = (select pil_address from cfg) then 'PIL' + end as collateral_type, + date_trunc('day', block_time) as day, + sum(cast(bytearray_to_uint256(data[1]) as double) / 1e18) as usdu_amount + from starknet.events + where from_address = (select usdu_token from cfg) + and keys[1] = 0x0099cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9 -- Transfer event + and keys[2] = 0x0000000000000000000000000000000000000000000000000000000000000000 -- from = 0x0 (mint) + and (keys[3] = (select stability_pool from cfg) or keys[3] = (select pil_address from cfg)) -- to = StabilityPool or PIL + and block_number >= (select deployment_block from cfg) + group by 1, 2 +), + +-- Liquidation rewards (revenue from liquidations) +-- Revenue = collateral_sent_sp * price - debt_offset_sp +liquidation_rewards as ( + select + collateral_type, + date_trunc('day', block_time) as day, + sum(collateral_sent_sp * _price - debt_offset_sp) as liquidation_rewards + from + query_6089704 + group by 1, 2 +), + +all_interest as ( + select + day, + case + when collateral_type = 'WBTC' then 'SP Yield' + else 'PIL Yield' + end as fee_type, + sum(usdu_amount) as fee + from + interest_rewards + where collateral_type in ('WBTC', 'PIL') + group by 1, 2 + + union all + + select + date_trunc('day', minute) as day, + 'Redemption Fees' as fee_type, + sum(fee_usd) as fee + from + add_price + group by 1, 2 + + union all + + select + day, + 'Liquidation Rewards' as fee_type, + sum(liquidation_rewards) as fee + from + liquidation_rewards + where collateral_type = 'WBTC' + group by 1, 2 +), + +get_next_day as ( + select + *, + sum(fee) over (partition by fee_type order by day asc) as fee_total, + lead(day, 1, date_trunc('day', current_timestamp) + interval '1' day) over (partition by fee_type order by day asc) as next_day + from + all_interest +), + +-- Fill in missing days with forward-filled values +filled_days as ( + select + d.day, + c.fee_type, + c.fee_total, + case when c.day = d.day then c.fee else 0 end as fee + from + get_next_day c + inner join + days d + on c.day <= d.day + and d.day < c.next_day +) + +select + day, + fee_type, + fee, + fee_total, + sum(fee_total/1e3) over (partition by day) as fee_total_all_k, + sum(case + when day > date_trunc('day', now()) - interval '1' day then fee/1e3 + else 0 + end) over (order by day) as fee_param +from filled_days +where day >= cast('2025-10-06' as date) +order by day desc diff --git a/dune_queries/uncap/protocol_revenue/liquidations.sql b/dune_queries/uncap/protocol_revenue/liquidations.sql new file mode 100644 index 0000000..b28ae25 --- /dev/null +++ b/dune_queries/uncap/protocol_revenue/liquidations.sql @@ -0,0 +1,44 @@ +-- Uncap Protocol - Liquidations +-- Network: Starknet Mainnet +-- Purpose: Track all liquidation events with collateral and debt details + +with + +cfg as ( + select + 0x038a9949900e7905f648cf0b50335efdac16a8acb8dfc870835da21c3f68e934 as events_emitter, + 2759369 as deployment_block +) + +select + from_address as contract_address, + 'WBTC' as collateral_type, + transaction_hash as tx_hash, + event_index as tx_index, + block_time, + block_number, + -- Extract debt_offset_by_SP (u256 = data[1] low, data[2] high) + cast(bytearray_to_uint256(data[1]) as double) / 1e18 as debt_offset_sp, + -- Extract debt_redistributed (u256 = data[3] low, data[4] high) + cast(bytearray_to_uint256(data[3]) as double) / 1e18 as debt_redistributed, + -- Extract strk_gas_compensation (u256 = data[5] low, data[6] high) + cast(bytearray_to_uint256(data[5]) as double) / 1e18 as strk_gas_compensation, + -- Extract coll_gas_compensation (u256 = data[7] low, data[8] high) + cast(bytearray_to_uint256(data[7]) as double) / 1e18 as coll_gas_compensation, + -- Extract coll_sent_to_SP (u256 = data[9] low, data[10] high) + cast(bytearray_to_uint256(data[9]) as double) / 1e18 as collateral_sent_sp, + -- Extract coll_redistributed (u256 = data[11] low, data[12] high) + cast(bytearray_to_uint256(data[11]) as double) / 1e18 as collateral_redistributed, + -- Extract coll_surplus (u256 = data[13] low, data[14] high) + cast(bytearray_to_uint256(data[13]) as double) / 1e18 as collateral_surplus, + -- Extract l_coll (u256 = data[15] low, data[16] high) + cast(bytearray_to_uint256(data[15]) as double) / 1e18 as l_coll, + -- Extract l_usdu_debt (u256 = data[17] low, data[18] high) + cast(bytearray_to_uint256(data[17]) as double) / 1e18 as l_usdu_debt, + -- Extract _price (u256 = data[19] low, data[20] high) + cast(bytearray_to_uint256(data[19]) as double) / 1e18 as _price +from starknet.events +where from_address = (select events_emitter from cfg) + and keys[1] = 0x0238a25785a13ab3138feb8f8f517e5a21a377cc1ad47809e9fd5e76daf01df7 + and block_number >= (select deployment_block from cfg) +order by block_number desc, event_index desc diff --git a/dune_queries/uncap/tvl/active_pool_updates.sql b/dune_queries/uncap/tvl/active_pool_updates.sql new file mode 100644 index 0000000..bf010fe --- /dev/null +++ b/dune_queries/uncap/tvl/active_pool_updates.sql @@ -0,0 +1,25 @@ +-- Uncap Protocol - Active Pool Collateral Balance Updates +-- Network: Starknet Mainnet +-- Purpose: Track WBTC collateral balance changes in the ActivePool + +with + +cfg as ( + select + 0x0780627de12ac84a7887b7f83496a8ece5ea3c5eb7170f9f00587dabfdbe18d1 as active_pool_address, + 2763027 as deployment_block +) + +select + from_address as contract_address, + 'WBTC' as collateral_type, + transaction_hash as tx_hash, + event_index as tx_index, + block_time, + block_number, + cast(bytearray_to_uint256(data[1]) as double) / 1e18 as collateral_balance +from starknet.events +where from_address = (select active_pool_address from cfg) + and keys[1] = 0x02540b6268ec773045b5da968c6a2bfa1442c16c8c2822b49b7aa5abb78d43b3 + and block_number >= (select deployment_block from cfg) +order by block_number desc, event_index desc diff --git a/dune_queries/uncap/tvl/token_holders.sql b/dune_queries/uncap/tvl/token_holders.sql new file mode 100644 index 0000000..7340d88 --- /dev/null +++ b/dune_queries/uncap/tvl/token_holders.sql @@ -0,0 +1,110 @@ +-- Uncap Protocol - USDU Token Holders Query +-- Network: Starknet Mainnet +-- Purpose: Track daily USDU token holder balances over time + +with + +-- Configuration +cfg as ( + select + 2759369 as deployment_block, + 0x04695252ccdd73f1d8ce7d7c78b1d3f55a127161ddbba5fb1174d10a6825397c as usdu_token_address +), + +-- Extract Transfer events from Starknet +-- Transfer event structure on Starknet: +-- keys[1] = event selector (0x99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9) +-- keys[2] = from_address +-- keys[3] = to_address +-- data[1], data[2] = amount (u256 as low, high) +erc20_aggregated as ( + select + day, + address, + token_address, + token_symbol, + sum(amount) as amount + from ( + -- Incoming transfers (to address) + select + date_trunc('day', block_time) as day, + keys[3] as address, + from_address as token_address, + 'USDU' as token_symbol, + sum(cast(bytearray_to_uint256(data[1]) as double) / 1e18) as amount + from starknet.events + where from_address = (select usdu_token_address from cfg) + and keys[1] = 0x0099cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9 + and block_number >= (select deployment_block from cfg) + group by 1, 2, 3, 4 + + union all + + -- Outgoing transfers (from address) + select + date_trunc('day', block_time) as day, + keys[2] as address, + from_address as token_address, + 'USDU' as token_symbol, + -sum(cast(bytearray_to_uint256(data[1]) as double) / 1e18) as amount + from starknet.events + where from_address = (select usdu_token_address from cfg) + and keys[1] = 0x0099cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9 + and block_number >= (select deployment_block from cfg) + group by 1, 2, 3, 4 + ) x + group by 1, 2, 3, 4 +), + +cum_aggregated as ( + select + day, + address, + token_address, + token_symbol, + sum(amount) over (partition by address, token_address, token_symbol order by day asc) as token_balance, + lead(day, 1, current_timestamp) over (partition by address, token_address, token_symbol order by day asc) as next_day + from + erc20_aggregated +), + +-- Get deployment date from first block +deployment_date as ( + select min(date_trunc('day', time)) as start_date + from starknet.blocks + where number >= (select deployment_block from cfg) +), + +time_seq AS ( + select + sequence( + CAST((select start_date from deployment_date) as timestamp), + date_trunc('day', cast(now() as timestamp)), + interval '1' day + ) as time +), + +days AS ( + select + time.time as day + from time_seq + cross join unnest(time) as time(time) +) + +select + * +from ( +select + d.day, + c.address, + c.token_address, + c.token_symbol, + c.token_balance +from +cum_aggregated c +inner join +days d + on c.day <= d.day + and d.day < c.next_day +) +where token_balance > 0.001 -- Exclude users who hold dust diff --git a/dune_queries/uncap/tvl/tvl.sql b/dune_queries/uncap/tvl/tvl.sql new file mode 100644 index 0000000..21b803d --- /dev/null +++ b/dune_queries/uncap/tvl/tvl.sql @@ -0,0 +1,132 @@ +-- Uncap Protocol - Total Value Locked (TVL) +-- Network: Starknet Mainnet +-- Purpose: Track total value locked in WBTC collateral and USDU in StabilityPool + +with + +-- Get deployment date for time series +deployment_date as ( + select min(date_trunc('day', time)) as start_date + from starknet.blocks + where number >= 2759369 +), + +troves as ( + select + date_trunc('day', block_time) as day, + collateral_type, + max_by(collateral_balance, (block_number, tx_index)) as balance + + from + query_5904577 -- active_pool_updates query + group by 1, 2 +), + +missing_days as ( + select + *, + lead(day, 1, current_timestamp) over (partition by collateral_type order by day asc) as next_day + from + troves +), + +time_seq AS ( + select + sequence( + CAST((select start_date from deployment_date) as timestamp), + date_trunc('day', cast(now() as timestamp)), + interval '1' day + ) as time +), + +days AS ( + select + time.time as day + from time_seq + cross join unnest(time) as time(time) +), + +active_pools as ( + select + day, + collateral_type, + balance + from ( + select + d.day, + c.collateral_type, + c.balance + from + missing_days c + inner join + days d + on c.day <= d.day + and d.day < c.next_day + ) + where balance > 0 +), + +-- Get WBTC prices +wbtc_prices as ( + select + DATE(timestamp AT TIME ZONE 'UTC') as day, + price as wbtc_price + from dune.openblocklabs.result_starknet_prices_daily + where contract_address = 0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac -- WBTC on Starknet +), + +-- Get latest WBTC price for fallback +latest_wbtc_price as ( + select max(wbtc_price) as latest_price + from wbtc_prices +), + +-- Calculate collateral TVL in USD and WWBTC +enrich_tvl as ( + select + day, + sum(balance_usd) as tvl_usd, + sum(balance_btc) as tvl_btc + from ( + select + ap.*, + ap.balance * coalesce(wp.wbtc_price, lwp.latest_price) as balance_usd, + ap.balance as balance_btc -- Balance already normalized from active_pool_updates + from + active_pools ap + left join + wbtc_prices wp + on ap.day = wp.day + cross join + latest_wbtc_price lwp + ) + group by 1 +), + +-- USDU in StabilityPool +usdu_tvl as ( + select + day, + sum(token_balance) as usdu_tvl + from + query_5904342 -- token_holders query + where address = 0x001ba4a9e2e86a41c6ed15016eda0404d12bf7b01052cccff1ace84d818335c7 -- WWBTC StabilityPool + group by 1 +) + +select + et.day, + et.tvl_usd as collateral_tvl_usd, + et.tvl_btc as collateral_tvl_btc, + coalesce(ut.usdu_tvl, 0) as usdu_in_sp, + et.tvl_usd + coalesce(ut.usdu_tvl, 0) as total_tvl_usd, + et.tvl_btc as total_tvl_btc +from +enrich_tvl et +left join +usdu_tvl ut + on et.day = ut.day +order by et.day desc + + + diff --git a/scripts/utils.ts b/scripts/utils.ts index 0c2f640..bd3fd19 100644 --- a/scripts/utils.ts +++ b/scripts/utils.ts @@ -66,32 +66,6 @@ export function serializeArray( return result; } -export async function invokeContract( - context: DeploymentContext, - contractAddress: string, - selector: string, - calldata: string[] = [] -): Promise { - const { account } = context; - - log(`Invoking ${selector} on ${contractAddress}`); - log(`Calldata:`, calldata); - - try { - const result = await account.execute({ - contractAddress, - entrypoint: selector, - calldata, - }); - - log(`Invoke result:`, result); - return result; - } catch (error) { - log(`Error invoking ${selector}:`, error); - throw error; - } -} - export function saveDeploymentAddresses( addresses: ContractAddresses, filename: string = 'deployment_addresses.json'