Skip to content

Commit 7241cc3

Browse files
committed
Add SVM Token Holders by Mint endpoint
- Get token holders by `mint` - Only recent active holders (less than 1 month) are considered
1 parent 0dd50b1 commit 7241cc3

File tree

3 files changed

+67
-30
lines changed

3 files changed

+67
-30
lines changed

src/routes/token/holders/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { Hono } from 'hono';
22
import evm from './evm.js';
3+
import svm from './svm.js';
34

45
const router = new Hono();
56

67
router.route('/evm', evm);
7-
// router.route('/svm', svm)
8+
router.route('/svm', svm);
89

910
export default router;

src/routes/token/holders/svm.ts

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,16 @@ import { z } from 'zod';
55
import { config } from '../../../config.js';
66
import { handleUsageQueryError, makeUsageQueryJson } from '../../../handleQuery.js';
77
import { sqlQueries } from '../../../sql/index.js';
8-
import {
9-
apiUsageResponse,
10-
orderBySchemaValue,
11-
orderDirectionSchema,
12-
paginationQuery,
13-
SVM_networkIdSchema,
14-
svmAddressSchema,
15-
WSOL,
16-
} from '../../../types/zod.js';
8+
import { apiUsageResponse, paginationQuery, SVM_networkIdSchema, svmAddressSchema, WSOL } from '../../../types/zod.js';
179
import { validatorHook, withErrorResponses } from '../../../utils.js';
1810

1911
const paramSchema = z.object({
20-
contract: WSOL,
12+
mint: WSOL,
2113
});
2214

2315
const querySchema = z
2416
.object({
2517
network_id: SVM_networkIdSchema,
26-
orderBy: orderBySchemaValue.optional(),
27-
orderDirection: orderDirectionSchema.optional(),
2818
})
2919
.extend(paginationQuery.shape);
3020

@@ -57,8 +47,8 @@ const responseSchema = apiUsageResponse.extend({
5747

5848
const openapi = describeRoute(
5949
withErrorResponses({
60-
summary: 'Token Holders',
61-
description: 'Returns token holders ranked by balance with holdings and supply percentage.',
50+
summary: 'Solana Token Holders by Mint',
51+
description: 'Returns token holders ranked by balance with holdings.',
6252

6353
tags: ['SVM'],
6454
security: [{ bearerAuth: [] }],
@@ -96,7 +86,7 @@ const openapi = describeRoute(
9686
const route = new Hono<{ Variables: { validatedData: z.infer<typeof querySchema> } }>();
9787

9888
route.get(
99-
'/:contract',
89+
'/:mint',
10090
openapi,
10191
validator('param', paramSchema, validatorHook),
10292
validator('query', querySchema, validatorHook),
Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,62 @@
1+
WITH filtered_balances AS
2+
(
3+
SELECT
4+
max(block_num) AS block_num,
5+
max(timestamp) AS timestamp,
6+
program_id,
7+
account,
8+
argMax(amount, b.block_num) AS amount,
9+
mint,
10+
any(decimals) AS decimals,
11+
{network_id:String} as network_id
12+
FROM balances AS b
13+
WHERE
14+
mint = {mint:String}
15+
AND b.timestamp > now() - INTERVAL 1 MONTH
16+
GROUP BY program_id, mint, account
17+
ORDER BY amount DESC
18+
LIMIT {limit:UInt64}
19+
OFFSET {offset:UInt64}
20+
),
21+
owners AS
22+
(
23+
SELECT
24+
account,
25+
owner
26+
FROM owner_state_latest AS o
27+
WHERE account IN (SELECT account FROM filtered_balances)
28+
),
29+
metadata AS
30+
(
31+
SELECT
32+
mint,
33+
if(empty(name), NULL, name) AS name,
34+
if(empty(symbol), NULL, symbol) AS symbol,
35+
if(empty(uri), NULL, uri) AS uri
36+
FROM metadata_view
37+
WHERE metadata IN (
38+
SELECT metadata
39+
FROM metadata_mint_state_latest
40+
WHERE mint IN (SELECT mint FROM filtered_balances)
41+
GROUP BY metadata
42+
)
43+
)
144
SELECT
2-
block_num,
3-
timestamp as last_balance_update,
4-
toString(owner) AS owner,
5-
amount,
6-
toString(b.amount / pow(10, decimals)) as value,
45+
b.timestamp AS last_update,
46+
block_num AS last_update_block_num,
47+
toUnixTimestamp(b.timestamp) AS last_update_timestamp,
48+
toString(program_id) AS program_id,
49+
toString(owner) AS owner,
50+
toString(account) AS token_account,
51+
toString(mint) AS mint,
52+
toString(b.amount) AS amount,
53+
b.amount / pow(10, decimals) AS value,
754
decimals,
8-
'TO IMPLEMENT' as symbol,
9-
{network_id: String} as network_id
10-
FROM balances_by_mint AS b
11-
FINAL
12-
WHERE
13-
mint = {contract: String} AND amount > 0
14-
ORDER BY value DESC
15-
LIMIT {limit:UInt64}
16-
OFFSET {offset:UInt64}
55+
name,
56+
symbol,
57+
uri,
58+
{network_id:String} AS network_id
59+
FROM filtered_balances AS b
60+
LEFT JOIN metadata USING mint
61+
LEFT JOIN owners USING account
62+
ORDER BY value DESC

0 commit comments

Comments
 (0)