-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
## 分析token持有者 | ||
|
||
通过 `getParsedProgramAccounts` 查询所有代币账户,然后根据 `mint` 地址过滤出持有该代币的账户。 | ||
|
||
这样就可以构建一个简单的 token 持有者列表,同时对 token 的持仓进行一个分析。 | ||
|
||
分析结果如下: | ||
|
||
``` | ||
前10大持有者分析: | ||
================================================== | ||
1. 地址: 5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1 | ||
持仓量: 345,412,370.048 | ||
占比: 34.55% | ||
2. 地址: D6kyD96Tfkz5fA8JYhWkRPCdJxJ1dAk2ALpchjyUxApu | ||
持仓量: 35,007,630.972 | ||
占比: 3.50% | ||
3. 地址: ALCzpmL4jHVQNooT8PrxjiFDU5YYynVDqLam5FrPgn7b | ||
持仓量: 33,554,743.151 | ||
占比: 3.36% | ||
4. 地址: APUnpSv4HDpNPfed4dPqAnz75REWYbM5vTHaTnApLe1P | ||
持仓量: 24,850,304.554 | ||
占比: 2.49% | ||
5. 地址: 6xBTJa2VzLUnZnSPbvaTNKGYMoxG4Pm3d6B6nsPVbWUU | ||
持仓量: 23,736,153.23 | ||
占比: 2.37% | ||
6. 地址: GR3a6esWvmcE33LqwW1FgQEy7E6Mq8cE2uEWXc4VtcsK | ||
持仓量: 21,725,403.233 | ||
占比: 2.17% | ||
7. 地址: 2dM6wMKS2is3jpwMueXvLfCgwveMiwB1iQNowkjx3x8S | ||
持仓量: 20,923,943.722 | ||
占比: 2.09% | ||
8. 地址: FFL94Rd9ZBorakrH7okwHNMrhiWwK6WT53PazjATW2zg | ||
持仓量: 18,385,694.794 | ||
占比: 1.84% | ||
9. 地址: 5aXGUFvh2v5wFzYAsfMsX7YLEpFw4uPQza9UatRZnL4c | ||
持仓量: 16,428,619.503 | ||
占比: 1.64% | ||
10. 地址: Cw4vsaQ9JpeS4CRn66KmF5oRazo6C7AwMk1BSVYTMjQR | ||
持仓量: 16,397,086.408 | ||
占比: 1.64% | ||
代币持仓分布分析报告: | ||
================================================== | ||
总供应量: 999,837,357.511 | ||
总持有者: 7,101 | ||
================================================== | ||
持仓区间: 1-100 | ||
持有者数量: 402 (5.66%) | ||
持仓总量: 4,136.054 (0.00%) | ||
平均持仓: 10.29 | ||
主要持有者: | ||
- 4xr8U1TMkKHi7H2BC2dYVv3yXnUkeUphvmauK541SRsX | ||
- 8GbasKKd4yHH83CvS4mCuMEJvLemSrrV7De5hmNQ59Hb | ||
- F9Br3tp1kKqNuXSZgFMvevWocn8YyX5H89g76cCBqiv | ||
- PCnb69Pi1T8DkxMBQahtteRZ9QdC5QbQS44DeUDaDum | ||
- 2a2NqsUWFR8eFaXNkhhuFSpVkTbctGao9ZZvbkLGcrEm | ||
持仓区间: 101-1,000 | ||
持有者数量: 83 (1.17%) | ||
持仓总量: 47,501.135 (0.00%) | ||
平均持仓: 572.30 | ||
主要持有者: | ||
- 79P6UsqG1Y2cB2xcmKcaCoBn3Gba44ACVgS9xmQFULs7 | ||
- C7VDfuACjqoT6rckWusPk8NSy6JmvYgcGmu4w6p7V3yx | ||
- Axd1HqUHxxW1peeGEwNmeckxuNATDJFeeEEMsmkJ6gGn | ||
- AqSR8UrBUG2dsT4LRMF9ceSayXu8oMXxfXc1FZbpHcbp | ||
- DoiUTaHyuEsMFQGanitoSScdEh78AWGpuNe5i1jp3Xdd | ||
持仓区间: 1,001-10,000 | ||
持有者数量: 274 (3.86%) | ||
持仓总量: 1,193,665.167 (0.12%) | ||
平均持仓: 4356.44 | ||
主要持有者: | ||
- 35tj37dCmiLxTV4cLRwXq6ycEhUqzG25PMaz4akMCHzV | ||
- 9vPVcvWomyDirhif6TeAa1tLPi5DkznFHmNsLHb6BWkf | ||
- 7APv2exT4QyhEHkDyEwY4TJR3pCSQPc7T3P9ZPA6sQNG | ||
- EfB3Q81JNNXuRsHt6gDN8L1CEqCuj5Gxgwgruj8bJdDi | ||
- Ci4s3nrWuhQriwgpEHwB8tUbzsx724bqugo5snHYJR7u | ||
持仓区间: 10,001-100,000 | ||
持有者数量: 333 (4.69%) | ||
持仓总量: 12,212,409.527 (1.22%) | ||
平均持仓: 36673.90 | ||
主要持有者: | ||
- HKtfKesi64ubxkoBkkDEvgEtXAEmwprtwbxgSxyDQXqN | ||
- An2vTG4AgYTtK7ED9qF6RaLsSmVRoe5QSxrke441wrjN | ||
- HwmkZo36DMvbRjsereuLmGFBdeJPSFKgfVWf6jUZd76Y | ||
- Et1M87kKDLpxMywjVKoFUusBFEW7rQzSKsffTCvgqBxd | ||
- EZgyr2aZESMvgzSfa2JjxjKrRCeYe2uc7YHTLt1eEKMM | ||
持仓区间: 100,001-1,000,000 | ||
持有者数量: 209 (2.94%) | ||
持仓总量: 69,417,348.63 (6.94%) | ||
平均持仓: 332140.42 | ||
主要持有者: | ||
- 4EKUCa6YCDwjHS5giQ4DJpvdyZ4XoRsmDZNSPwPgScwC | ||
- HkhCAiPabzFmUiHQAASg7rYiHthJ4sqGH5ygHHVhWsRf | ||
- T2ESjZiPmv7Fb2rAWebLpYnyTa8yyVCWaGMot7DpGjG | ||
- EpmSLKSnwZxpXzQkE76woiCTF6UkFmjAsSWpovANVMRy | ||
- 973rVqBCtaExEVGvEoYqyJta6THeXCC9pCRZuPoEUCMc | ||
持仓区间: 1,000,000+ | ||
持有者数量: 83 (1.17%) | ||
持仓总量: 916,962,296.997 (91.71%) | ||
平均持仓: 11047738.52 | ||
主要持有者: | ||
- 5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1 | ||
- D6kyD96Tfkz5fA8JYhWkRPCdJxJ1dAk2ALpchjyUxApu | ||
- ALCzpmL4jHVQNooT8PrxjiFDU5YYynVDqLam5FrPgn7b | ||
- APUnpSv4HDpNPfed4dPqAnz75REWYbM5vTHaTnApLe1P | ||
- 6xBTJa2VzLUnZnSPbvaTNKGYMoxG4Pm3d6B6nsPVbWUU | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import { Connection, PublicKey, ParsedAccountData } from "@solana/web3.js"; | ||
|
||
const connection = new Connection("https://api.mainnet-beta.solana.com", "confirmed"); | ||
|
||
async function getTokenHolders(mintAddress: string) { | ||
// 1. 参数说明:mintAddress 是代币的 Mint 地址 | ||
// 例如:USDC 的 Mint 地址是 "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" | ||
const mint = new PublicKey(mintAddress); | ||
|
||
// 2. 调用 getParsedProgramAccounts 查询所有代币账户 | ||
const accounts = await connection.getParsedProgramAccounts( | ||
// Token 程序的地址(固定值) | ||
new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'), | ||
{ | ||
filters: [ | ||
// Token 账户的大小固定为 165 字节 | ||
{ dataSize: 165 }, | ||
{ | ||
// 在数据开始位置匹配 Mint 地址 | ||
memcmp: { | ||
offset: 0, | ||
bytes: mint.toBase58(), | ||
}, | ||
}, | ||
], | ||
} | ||
); | ||
|
||
// 3. 处理返回数据 | ||
const holders = accounts.map(account => ({ | ||
// 代币账户地址 | ||
address: account.pubkey.toString(), | ||
// 代币余额(已考虑小数位) | ||
amount: (account.account.data as ParsedAccountData).parsed.info.tokenAmount.uiAmount, | ||
// 代币账户所有者(钱包地址) | ||
owner: (account.account.data as ParsedAccountData).parsed.info.owner | ||
})); | ||
|
||
// 4. 按持有量从大到小排序 | ||
return holders.sort((a, b) => b.amount - a.amount); | ||
} | ||
|
||
interface TokenDistribution { | ||
range: string; | ||
holders: number; | ||
totalAmount: number; | ||
percentage: string; | ||
addresses: string[]; | ||
} | ||
|
||
async function getTokenDistribution(holders: any[]) { | ||
// 计算总供应量 | ||
const totalSupply = holders.reduce((sum, h) => sum + Number(h.amount), 0); | ||
|
||
// 定义分布区间(可以根据需要调整) | ||
const ranges = [ | ||
{ min: 0, max: 100, label: "1-100" }, | ||
{ min: 100, max: 1000, label: "101-1,000" }, | ||
{ min: 1000, max: 10000, label: "1,001-10,000" }, | ||
{ min: 10000, max: 100000, label: "10,001-100,000" }, | ||
{ min: 100000, max: 1000000, label: "100,001-1,000,000" }, | ||
{ min: 1000000, max: Infinity, label: "1,000,000+" } | ||
]; | ||
|
||
// 初始化结果 | ||
const distribution: { [key: string]: TokenDistribution } = {}; | ||
ranges.forEach(range => { | ||
distribution[range.label] = { | ||
range: range.label, | ||
holders: 0, | ||
totalAmount: 0, | ||
percentage: "0%", | ||
addresses: [] | ||
}; | ||
}); | ||
|
||
// 统计分布 | ||
holders.forEach(holder => { | ||
const amount = Number(holder.amount); | ||
const range = ranges.find(r => amount > r.min && amount <= r.max); | ||
if (range) { | ||
const label = range.label; | ||
distribution[label].holders++; | ||
distribution[label].totalAmount += amount; | ||
distribution[label].addresses.push(holder.owner); | ||
} | ||
}); | ||
|
||
// 计算百分比 | ||
Object.values(distribution).forEach(d => { | ||
d.percentage = ((Number(d.totalAmount) / Number(totalSupply)) * 100).toFixed(2) + '%'; | ||
}); | ||
|
||
// 生成报告 | ||
console.log("\n代币持仓分布分析报告:"); | ||
console.log("=".repeat(50)); | ||
console.log(`总供应量: ${totalSupply.toLocaleString()}`); | ||
console.log(`总持有者: ${holders.length.toLocaleString()}`); | ||
console.log("=".repeat(50)); | ||
|
||
Object.values(distribution).forEach(d => { | ||
if (d.holders > 0) { | ||
console.log(`\n持仓区间: ${d.range}`); | ||
console.log(`持有者数量: ${d.holders.toLocaleString()} (${((d.holders/holders.length)*100).toFixed(2)}%)`); | ||
console.log(`持仓总量: ${d.totalAmount.toLocaleString()} (${d.percentage})`); | ||
console.log(`平均持仓: ${(Number(d.totalAmount)/Number(d.holders)).toFixed(2)}`); | ||
|
||
// 只显示前5个大户地址 | ||
if (d.addresses.length > 0) { | ||
console.log("主要持有者: "); | ||
d.addresses.slice(0, 5).forEach(addr => | ||
console.log(` - ${addr}`) | ||
); | ||
} | ||
} | ||
}); | ||
|
||
return distribution; | ||
} | ||
|
||
|
||
|
||
async function main() { | ||
const holders = await getTokenHolders("Dp4fXozKtwgK1cL5KQeeNbuAgFpJtY3FbAvL8JrWpump"); | ||
// 计算总供应量 | ||
const totalSupply = holders.reduce((sum, h) => sum + Number(h.amount), 0); | ||
|
||
const topHolders = holders.slice(0, 10); // 前10大持有者 | ||
console.log("\n前10大持有者分析:"); | ||
console.log("=".repeat(50)); | ||
topHolders.forEach((holder, index) => { | ||
console.log(`${index + 1}. 地址: ${holder.owner}`); | ||
console.log(` 持仓量: ${Number(holder.amount).toLocaleString()}`); | ||
console.log(` 占比: ${((Number(holder.amount) / Number(totalSupply)) * 100).toFixed(2)}%\n`); | ||
}); | ||
|
||
await getTokenDistribution(holders) | ||
} | ||
|
||
main(); |