Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion fees/futarchy-amm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ const fetch = async (_a: any, _b: any, options: FetchOptions) => {
return typeof row.trading_date === 'string' && row.trading_date.slice(0, 10) === targetDate
})
matched.forEach((row: any) => {
const fees = row.token_fees_usdc ?? 0
// Every SpotSwap pays the 0.5% taker fee on its input, so both legs count:
// usdc_fees is the buy-side (USDC in), token_fees_usdc is the sell-side (token in → USDC).
// The buy-side was previously omitted, under-counting futarchy fees.
const fees = (row.usdc_fees ?? 0) + (row.token_fees_usdc ?? 0)
dailyFees.addUSDValue(fees, 'futarchy_amm')
})

Expand Down Expand Up @@ -68,6 +71,7 @@ const breakdownMethodology = {
Fees: {
'meteora_damm': 'Ownership-weighted LP fees from Meteora DAMM pools based on actual DAO liquidity positions',
'futarchy_amm': '0.5% fees from Futarchy AMM swaps',
'futarchy_conditional': '0.5% fees from winning Futarchy Conditional Market swaps',
},
}

Expand Down
140 changes: 127 additions & 13 deletions helpers/queries/futarchy.sql
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/*
Futarchy Protocol Fees

Combines fees from two sources:
1. Meteora DAMM pools - ownership-weighted LP fees based on actual liquidity positions
2. Futarchy AMM direct swaps (0.5% fee)


Combines fees from three sources:
1. Meteora DAMM pools - ownership-weighted LP fees based on actual liquidity positions
2. Futarchy AMM spot swaps - direct SpotSwap (0.5% fee)
3. Futarchy conditional - winning-market ConditionalSwap fees (0.5%, realized only)

Parameters:
{{start}} - Unix timestamp for start of period
{{end}} - Unix timestamp for end of period
{{end}} - Unix timestamp for end of period
*/

WITH pool_map AS (
Expand All @@ -32,6 +33,24 @@ WITH pool_map AS (
UNION ALL SELECT '2zsbECzM7roqnDcuv2TNGpfv5PAnuqGmMo5YPtqmUz5p',
'w1BDxR4FvN4KryBuJwcEuohYKHkyDzD1beNH3AhF6Wn',
'98SPcyUZ2rqM2dgjCqqSXS4gJrNTLSNUAAVCF38xYj9u' -- Solomon / USDC
UNION ALL SELECT '5M3oyxAhZ68tJXaNJiTPaHXZwgBEsYDCKiYVsvN8Gq8G',
'85QbV1KLqXXCVRt3GeVezJ2zPDToKEnhetijpNSmCkWU',
'Dv5axGVuk3qcoEBNofNLerFFEE3VemmuH8VsmNWUTSR5' -- Jurassic Finance / USDC
UNION ALL SELECT '2foMdp9Y9hWttCvHedQef8GUWtCKwCfTgTiciUrTdQFF',
'EunUNcHufVkRpaj17TdKJgZZ3isZj3n5TQxrtBhuPwUh',
'9Rykf7i9fxUaXD8iD6GSGpRaoWQQP51Uiq1oxSE9oDzx' -- P2P / USDC
UNION ALL SELECT '9uuneA2PrNhDUi2nVEqKK3nTF3q5BiRw6mBm1X9QV1Dv',
'2FxsHe576R2jQKmYNVYZ2hrKVFoFF8n1tmPMuQ5jPFQA',
'6VsC8PuKkXm5xo54c2vbrAaSfQipkpGHqNuKTxXFySx6' -- Umbra New / USDC
UNION ALL SELECT 'JBFvVa5nVLjR3tsDqQwa7ZYBBVceDebQSr6DoCPqwRf8', -- pool
'44t9nPMNEf3WBGaayP2B9xrufk1f7PEK4J13tHinqiht', -- position
'8s6Jdoh7tgUqmU3D2EmpNJHSvuN5U4NybpLAdsiMitwB' -- Omnipair / USDC -- owner
UNION ALL SELECT 'E7Dt6DwBNhK5339oH7UsL4MVEm4wNjLhtb9Qmj4VYp9a', -- pool
'FPzs9xcJzTokwf375XqCt4YxNrzkhZSbRTK4EWfNHHjD', -- position
'5ZPnwQDU7dEKdMGqaY5oCQkiuQpwjtYSJNMNpiStTNvU' -- Superclaw / USDC -- owner
UNION ALL SELECT '7LWeTej42aKgcsJBTtgK1vFV152G8hUgHJZehrKYX1VT', -- pool
'A4c6o7Wby6T8BYyknD8HLKe7QgH8D2zSgvGvh7ZpUANR', -- position
'FeMyhpB3LJuuuA1oLzXFDuZ48EJz46gyyk3w2xuQA8uw' -- Futardio Cult / USDC -- owner
),

target_pools AS (
Expand Down Expand Up @@ -175,7 +194,7 @@ meteora_aggregated AS (
LEFT JOIN ownership_shares o ON f.pool = o.pool
),

-- FUTARCHY AMM
-- FUTARCHY AMM (SPOT)
futarchy_swaps AS (
SELECT
block_time,
Expand All @@ -191,7 +210,7 @@ futarchy_swaps AS (
varbinary_to_bigint(varbinary_reverse(varbinary_substring(data, 106, 8))) / 1e6 AS input_amount,
varbinary_to_bigint(varbinary_reverse(varbinary_substring(data, 114, 8))) / 1e6 AS output_amount
FROM solana.instruction_calls
WHERE
WHERE
block_time >= from_unixtime({{start}})
AND block_time <= from_unixtime({{end}})
AND tx_success = true
Expand All @@ -216,19 +235,107 @@ futarchy_token_filtered AS (
WHEN swap_type = 'sell' THEN output_amount / NULLIF(input_amount, 0)
END AS price
FROM futarchy_swaps
WHERE
WHERE
input_amount > 0
AND output_amount > 0
AND token IS NOT NULL
),

futarchy_aggregated AS (
SELECT
SUM(CASE WHEN swap_type = 'buy' THEN input_amount ELSE 0 END) AS buy_volume_usdc,
SUM(CASE WHEN swap_type = 'sell' THEN input_amount ELSE 0 END) AS sell_volume_tokens,
AVG(price) AS avg_price
SUM(CASE WHEN swap_type = 'buy' THEN input_amount ELSE 0 END) AS buy_volume_usdc,
SUM(CASE WHEN swap_type = 'sell' THEN output_amount ELSE 0 END) AS sell_volume_usdc
FROM futarchy_token_filtered
WHERE price > 0
),

-- FUTARCHY CONDITIONAL MARKETS (winning side only, realized fees)
-- NOTE: not time-bounded on purpose. A swap inside [start, end] can belong to a
-- proposal that finalizes outside the window; we need the full finalization
-- universe to resolve which side won.
finalized_proposals AS (
SELECT DISTINCT
to_base58(varbinary_substring(data, 41, 32)) AS proposal,
CASE varbinary_substring(data, 153, 1)
WHEN 0x02 THEN 'Passed'
WHEN 0x03 THEN 'Failed'
END AS state
FROM solana.instruction_calls
WHERE
tx_success = true
AND is_inner = true
AND executing_account = 'FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq'
AND inner_executing_account = 'FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq'
AND account_arguments[1] = 'DGEympSS4qLvdr9r3uGHTfACdN8snShk4iGdJtZPxuBC'
AND cardinality(account_arguments) = 1
AND varbinary_starts_with(data, 0xe445a52e51cb9a1d)
AND any_match(log_messages, x -> strpos(x, 'FinalizeProposal') > 0)
AND varbinary_substring(data, 153, 1) IN (0x02, 0x03)
),

conditional_swaps AS (
SELECT
block_time,
tx_id,
to_base58(varbinary_substring(data, 73, 32)) AS proposal,
CASE
WHEN varbinary_length(data) = 439 THEN to_base58(varbinary_substring(data, 312, 32))
WHEN varbinary_length(data) = 703 THEN to_base58(varbinary_substring(data, 576, 32))
END AS token,
CASE varbinary_substring(data, 137, 1)
WHEN 0x01 THEN 'pass'
WHEN 0x02 THEN 'fail'
END AS market,
CASE varbinary_substring(data, 138, 1)
WHEN 0x00 THEN 'buy'
WHEN 0x01 THEN 'sell'
END AS swap_type,
varbinary_to_bigint(varbinary_reverse(varbinary_substring(data, 139, 8))) / 1e6 AS input_amount,
varbinary_to_bigint(varbinary_reverse(varbinary_substring(data, 147, 8))) / 1e6 AS output_amount
FROM solana.instruction_calls
WHERE
block_time >= from_unixtime({{start}})
AND block_time < from_unixtime({{end}})
AND tx_success = true
AND is_inner = true
AND executing_account = 'FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq'
AND inner_executing_account = 'FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq'
AND account_arguments[1] = 'DGEympSS4qLvdr9r3uGHTfACdN8snShk4iGdJtZPxuBC'
AND cardinality(account_arguments) = 1
AND varbinary_starts_with(data, 0xe445a52e51cb9a1d)
AND varbinary_length(data) IN (439, 703)
AND any_match(log_messages, x -> strpos(x, 'ConditionalSwap') > 0)
),

winning_market_swaps AS (
SELECT
c.swap_type,
c.input_amount,
c.output_amount,
CASE
WHEN c.swap_type = 'buy' THEN c.input_amount / NULLIF(c.output_amount, 0)
WHEN c.swap_type = 'sell' THEN c.output_amount / NULLIF(c.input_amount, 0)
END AS price
FROM conditional_swaps c
JOIN finalized_proposals p ON p.proposal = c.proposal
WHERE
c.input_amount > 0
AND c.output_amount > 0
AND c.token IS NOT NULL
AND c.market IN ('pass', 'fail')
AND (
(p.state = 'Passed' AND c.market = 'pass')
OR
(p.state = 'Failed' AND c.market = 'fail')
)
),

conditional_aggregated AS (
SELECT
SUM(CASE WHEN swap_type = 'buy' THEN input_amount ELSE 0 END) AS buy_volume_usdc,
SUM(CASE WHEN swap_type = 'sell' THEN output_amount ELSE 0 END) AS sell_volume_usdc
FROM winning_market_swaps
WHERE price > 0
)

SELECT
Expand All @@ -240,5 +347,12 @@ UNION ALL

SELECT
'futarchy_amm' AS source,
COALESCE(buy_volume_usdc * 0.005, 0) + COALESCE(sell_volume_tokens * COALESCE(avg_price, 0) * 0.005, 0) AS total_fees_usd
(COALESCE(buy_volume_usdc, 0) + COALESCE(sell_volume_usdc, 0)) * 0.005 AS total_fees_usd
FROM futarchy_aggregated

UNION ALL

SELECT
'futarchy_conditional' AS source,
(COALESCE(buy_volume_usdc, 0) + COALESCE(sell_volume_usdc, 0)) * 0.005 AS total_fees_usd
FROM conditional_aggregated
Loading