Skip to content

Commit

Permalink
feat: count protocol users from nested smart contracts (#143)
Browse files Browse the repository at this point in the history
  • Loading branch information
pradel authored Oct 26, 2024
1 parent b4d2e11 commit 80a7641
Show file tree
Hide file tree
Showing 9 changed files with 446 additions and 294 deletions.
5 changes: 5 additions & 0 deletions .changeset/spicy-spoons-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stackspulse/server": minor
---

Properly count protocol stats when there are nested smart contract calls.
8 changes: 4 additions & 4 deletions apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
"deploy": "fly deploy --remote-only"
},
"dependencies": {
"@dotenvx/dotenvx": "1.19.3",
"@dotenvx/dotenvx": "1.20.1",
"@libsql/client": "0.8.0",
"@sentry/node": "8.34.0",
"@stacks/blockchain-api-client": "8.0.4",
"@sentry/node": "8.35.0",
"@stacks/blockchain-api-client": "8.1.2",
"@stacks/stacks-blockchain-api-types": "7.14.1",
"@stackspulse/protocols": "workspace:*",
"@t3-oss/env-core": "0.11.1",
Expand All @@ -27,6 +27,6 @@
},
"devDependencies": {
"nitropack": "2.9.7",
"prisma": "5.20.0"
"prisma": "5.21.1"
}
}
53 changes: 46 additions & 7 deletions apps/server/src/api/protocols/users/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,61 @@ export default defineCachedEventHandler(async (event) => {
"7d": 7,
"30d": 30,
};
dateCondition = `AND txs.block_time >= EXTRACT(EPOCH FROM (NOW() - INTERVAL '${daysToSubtract[query.date]} days'))`;
dateCondition = `AND txs.block_time >= EXTRACT(EPOCH FROM (NOW() - INTERVAL '${
daysToSubtract[query.date]
} days'))`;
}

const result = await sql`
WITH protocol_contracts AS (
SELECT id, UNNEST(contracts) AS contract_address
FROM dapps
),
address_txs AS (
SELECT DISTINCT tx_id, index_block_hash, microblock_hash, protocol_contracts.id AS protocol_name
FROM (
SELECT tx_id, index_block_hash, microblock_hash, contract_call_contract_id AS address
FROM txs
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, principal
FROM principal_stx_txs
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, sender
FROM stx_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, recipient
FROM stx_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, sender
FROM ft_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, recipient
FROM ft_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, sender
FROM nft_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, recipient
FROM nft_events
) sub
JOIN protocol_contracts ON sub.address = protocol_contracts.contract_address
)
SELECT
dapps.id as protocol_name,
atxs.protocol_name,
COUNT(DISTINCT txs.sender_address) AS unique_senders
FROM
txs
address_txs atxs
JOIN
txs ON atxs.tx_id = txs.tx_id
JOIN
dapps ON txs.contract_call_contract_id = ANY (dapps.contracts)
blocks ON txs.block_height = blocks.block_height
WHERE
txs.type_id = 2
${sql.unsafe(dateCondition)}
1=1
${sql.unsafe(dateCondition)}
GROUP BY
dapps.id
atxs.protocol_name
ORDER BY
unique_senders DESC
LIMIT ${limit};
Expand Down
53 changes: 44 additions & 9 deletions apps/server/src/api/transactions/stats/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,57 @@ type TransactionStatsRouteResponse = {
export default defineCachedEventHandler(async (event) => {
const query = await getValidatedQueryZod(event, transactionStatsRouteSchema);

let protocolCondition = "";
let protocolContractsCondition = "";
if (query.protocol) {
protocolCondition = `AND dapps.id = '${query.protocol}'`;
protocolContractsCondition = `WHERE dapps.id = '${query.protocol}'`;
}

const result = await sql`
WITH protocol_contracts AS (
SELECT UNNEST(contracts) AS contract_address
FROM dapps
${sql.unsafe(protocolContractsCondition)}
),
address_txs AS (
SELECT DISTINCT tx_id, index_block_hash, microblock_hash
FROM (
SELECT tx_id, index_block_hash, microblock_hash, contract_call_contract_id AS address
FROM txs
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, principal
FROM principal_stx_txs
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, sender
FROM stx_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, recipient
FROM stx_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, sender
FROM ft_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, recipient
FROM ft_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, sender
FROM nft_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, recipient
FROM nft_events
) sub
WHERE address IN (SELECT contract_address FROM protocol_contracts)
)
SELECT
COUNT(txs.id) AS count,
COUNT(DISTINCT sender_address) AS unique_senders
COUNT(DISTINCT atxs.tx_id) AS count,
COUNT(DISTINCT txs.sender_address) AS unique_senders
FROM
txs
txs
JOIN
dapps ON txs.contract_call_contract_id = ANY (dapps.contracts)
WHERE
txs.type_id = 2
${sql.unsafe(protocolCondition)}
address_txs atxs ON atxs.tx_id = txs.tx_id
AND atxs.index_block_hash = txs.index_block_hash
AND atxs.microblock_hash = txs.microblock_hash
`;

const stats: TransactionStatsRouteResponse = {
Expand Down
51 changes: 42 additions & 9 deletions apps/server/src/api/transactions/unique-senders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,61 @@ export default defineCachedEventHandler(async (event) => {

const result = await sql`
WITH monthly_blocks AS (
SELECT
SELECT
DATE_TRUNC('month', TO_TIMESTAMP(burn_block_time)) AS month,
MIN(block_height) AS min_block_height,
MAX(block_height) AS max_block_height
FROM
FROM
blocks
GROUP BY
GROUP BY
DATE_TRUNC('month', TO_TIMESTAMP(burn_block_time))
),
protocol_contracts AS (
SELECT UNNEST(contracts) AS contract_address
FROM dapps
WHERE id = ${query.protocol}
),
address_txs AS (
SELECT DISTINCT tx_id, index_block_hash, microblock_hash
FROM (
SELECT tx_id, index_block_hash, microblock_hash, contract_call_contract_id AS address
FROM txs
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, principal
FROM principal_stx_txs
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, sender
FROM stx_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, recipient
FROM stx_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, sender
FROM ft_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, recipient
FROM ft_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, sender
FROM nft_events
UNION ALL
SELECT tx_id, index_block_hash, microblock_hash, recipient
FROM nft_events
) sub
WHERE address IN (SELECT contract_address FROM protocol_contracts)
)
SELECT
mb.month,
COUNT(DISTINCT txs.sender_address) AS unique_senders
FROM
FROM
monthly_blocks mb
JOIN
txs ON txs.block_height BETWEEN mb.min_block_height AND mb.max_block_height
txs ON txs.block_height BETWEEN mb.min_block_height AND mb.max_block_height
JOIN
dapps ON txs.contract_call_contract_id = ANY (dapps.contracts)
WHERE
txs.type_id = 2
AND dapps.id = ${query.protocol}
address_txs atxs ON atxs.tx_id = txs.tx_id
GROUP BY
mb.month
ORDER BY
Expand Down
6 changes: 3 additions & 3 deletions apps/web/fly.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ internal_port = 3000
force_https = true
auto_stop_machines = "suspend"
auto_start_machines = true
min_machines_running = 0
min_machines_running = 1
processes = ['app']

[http_service.concurrency]
type = "requests"
soft_limit = 400
hard_limit = 450
soft_limit = 600
hard_limit = 700

[[vm]]
size = 'shared-cpu-1x'
Expand Down
8 changes: 4 additions & 4 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
"deploy": "fly deploy --remote-only"
},
"dependencies": {
"@dotenvx/dotenvx": "1.19.3",
"@dotenvx/dotenvx": "1.20.1",
"@hirosystems/token-metadata-api-client": "2.0.0",
"@radix-ui/themes": "3.0.5",
"@sentry/nextjs": "8.34.0",
"@sentry/nextjs": "8.35.0",
"@stacks/stacks-blockchain-api-types": "7.14.1",
"@stacks/transactions": "6.16.1",
"@stacks/transactions": "6.17.0",
"@stackspulse/protocols": "workspace:*",
"@t3-oss/env-core": "0.11.1",
"@t3-oss/env-nextjs": "0.11.1",
Expand All @@ -38,7 +38,7 @@
"zod": "3.23.8"
},
"devDependencies": {
"@types/node": "22.7.5",
"@types/node": "22.8.0",
"@types/react": "18.3.11",
"@types/react-dom": "18.3.1",
"autoprefixer": "10.4.20",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"husky": "9.1.6"
},
"devDependencies": {
"@biomejs/biome": "1.9.3",
"@biomejs/biome": "1.9.4",
"@changesets/cli": "2.27.9",
"lint-staged": "15.2.10",
"turbo": "2.1.3"
Expand Down
Loading

0 comments on commit 80a7641

Please sign in to comment.