Skip to content

Commit a01bd7b

Browse files
committed
fetch top holders
1 parent 0a310cf commit a01bd7b

File tree

3 files changed

+93
-1
lines changed

3 files changed

+93
-1
lines changed

packages/plugin-reputation/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import type { Plugin } from "@elizaos/core";
22

33
import { issueAttestationAction } from "./actions/issueAttestation.ts"
44
import { attestationsProvider } from "./providers/attestations.ts"
5+
import { cnsBalanceProvider } from "./providers/cnsBalance.ts"
6+
import { topHoldersProvider } from "./providers/topHolders.ts"
57
import { tipCNSTokenAction } from "./actions/tipCNSToken.ts";
68

79
export const reputationPlugin: Plugin = {
810
name: "reputation",
911
description: "Issue points and attestations for netizens",
1012
actions: [issueAttestationAction, tipCNSTokenAction],
1113
evaluators: [],
12-
providers: [attestationsProvider],
14+
providers: [attestationsProvider, cnsBalanceProvider, topHoldersProvider],
1315
};
1416
export default reputationPlugin;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from "./attestations.ts"
2+
export * from "./topHolders.ts"
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import {
2+
elizaLogger,
3+
type IAgentRuntime,
4+
type Memory,
5+
type Provider,
6+
type State,
7+
} from "@elizaos/core";
8+
import { ethers } from "ethers";
9+
10+
// CNS Token ABI - including Transfer event and balanceOf function
11+
const CNS_TOKEN_ABI = [
12+
{
13+
"anonymous": false,
14+
"inputs": [
15+
{ "indexed": true, "name": "from", "type": "address" },
16+
{ "indexed": true, "name": "to", "type": "address" },
17+
{ "indexed": false, "name": "value", "type": "uint256" }
18+
],
19+
"name": "Transfer",
20+
"type": "event"
21+
},
22+
{
23+
"inputs": [
24+
{ "internalType": "address", "name": "account", "type": "address" }
25+
],
26+
"name": "balanceOf",
27+
"outputs": [
28+
{ "internalType": "uint256", "name": "", "type": "uint256" }
29+
],
30+
"stateMutability": "view",
31+
"type": "function"
32+
}
33+
];
34+
35+
const CNS_TOKEN_ADDRESS = process.env.CNS_TOKEN_ADDRESS;
36+
37+
const topHoldersProvider: Provider = {
38+
get: async (_runtime: IAgentRuntime, _message: Memory, _state?: State) => {
39+
elizaLogger.info(`⏳ Provider: Fetch top CNS token holders`);
40+
try {
41+
const provider = new ethers.JsonRpcProvider(process.env.EVM_PROVIDER_URL);
42+
const contract = new ethers.Contract(CNS_TOKEN_ADDRESS, CNS_TOKEN_ABI, provider);
43+
44+
// Get all Transfer events
45+
const filter = contract.filters.Transfer();
46+
const events = await contract.queryFilter(filter, 0, "latest");
47+
48+
// Create a map to track balances
49+
const balances = new Map<string, bigint>();
50+
51+
// Process all transfer events to calculate current balances
52+
for (const event of events) {
53+
if (event instanceof ethers.EventLog) {
54+
const from = event.args[0];
55+
const to = event.args[1];
56+
const value = event.args[2];
57+
58+
// Subtract from sender
59+
if (from !== ethers.ZeroAddress) {
60+
balances.set(from, (balances.get(from) || BigInt(0)) - value);
61+
}
62+
// Add to recipient
63+
balances.set(to, (balances.get(to) || BigInt(0)) + value);
64+
}
65+
}
66+
67+
// Convert to array and sort by balance
68+
const sortedHolders = Array.from(balances.entries())
69+
.filter(([address, balance]) => balance > BigInt(0)) // Filter out zero balances
70+
.sort((a, b) => (b[1] > a[1] ? 1 : -1))
71+
.slice(0, 20); // Get top 20
72+
73+
// Format the response
74+
const formattedHolders = sortedHolders
75+
.map(([address, balance], index) => {
76+
const formattedBalance = ethers.formatUnits(balance, 18);
77+
return `${index + 1}. ${address}: ${formattedBalance} CNS`;
78+
})
79+
.join("\n");
80+
console.log(`**Top 20 CNS Token Holders:**\n${formattedHolders}`);
81+
return `**Top 20 CNS Token Holders:**\n${formattedHolders}`;
82+
} catch (error) {
83+
elizaLogger.error(`Error fetching top holders: ${error}`);
84+
return "Failed to fetch top CNS token holders. Please try again later.";
85+
}
86+
},
87+
};
88+
89+
export { topHoldersProvider };

0 commit comments

Comments
 (0)