@madeonsol/plugin-madeonsol 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/kol-alerts-recent.d.ts +2 -0
- package/dist/actions/kol-alerts-recent.js +48 -0
- package/dist/actions/kol-compare.d.ts +2 -0
- package/dist/actions/kol-compare.js +57 -0
- package/dist/actions/kol-token-entry-order.d.ts +2 -0
- package/dist/actions/kol-token-entry-order.js +53 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.js +9 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +7 -0
- package/package.json +2 -2
- package/src/actions/kol-alerts-recent.ts +67 -0
- package/src/actions/kol-compare.ts +79 -0
- package/src/actions/kol-token-entry-order.ts +71 -0
- package/src/client.ts +12 -0
- package/src/index.ts +7 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { MadeOnSolClient } from "../client.js";
|
|
2
|
+
import { MADEONSOL_CLIENT_KEY } from "../index.js";
|
|
3
|
+
function getClient(runtime) {
|
|
4
|
+
return runtime[MADEONSOL_CLIENT_KEY] ?? new MadeOnSolClient();
|
|
5
|
+
}
|
|
6
|
+
export const kolAlertsRecentAction = {
|
|
7
|
+
name: "GET_KOL_ALERTS_RECENT",
|
|
8
|
+
description: "Get live KOL alerts from MadeOnSol — consensus clusters, fresh-token KOL buys, and heating-up wallets in one unified stream.",
|
|
9
|
+
similes: [
|
|
10
|
+
"kol alerts",
|
|
11
|
+
"recent alerts",
|
|
12
|
+
"kol signals",
|
|
13
|
+
"whats happening now",
|
|
14
|
+
"live kol feed",
|
|
15
|
+
],
|
|
16
|
+
validate: async (_runtime, message) => {
|
|
17
|
+
const text = (message.content?.text || "").toLowerCase();
|
|
18
|
+
return /\b(kol|smart money)\b/.test(text) && /\b(alert|signal|recent|live|now)\b/.test(text);
|
|
19
|
+
},
|
|
20
|
+
handler: async (runtime, message, _state, _options, callback) => {
|
|
21
|
+
const client = getClient(runtime);
|
|
22
|
+
const text = (message.content?.text || "").toLowerCase();
|
|
23
|
+
const window = text.includes("1h") ? "1h" : text.includes("6h") ? "6h" : text.includes("24h") ? "24h" : text.includes("5m") ? "5m" : "15m";
|
|
24
|
+
const result = await client.getKolAlertsRecent({ window, limit: "20" });
|
|
25
|
+
if (result.error) {
|
|
26
|
+
callback?.({ text: result.status === 402
|
|
27
|
+
? "Authentication required. Set MADEONSOL_API_KEY (free at madeonsol.com/developer), RAPIDAPI_KEY, or SVM_PRIVATE_KEY."
|
|
28
|
+
: `Error: ${result.error}` });
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
const data = result.data;
|
|
32
|
+
const lines = (data.alerts || []).slice(0, 10).map((a) => {
|
|
33
|
+
const subject = a.token_symbol || a.kol_name || "—";
|
|
34
|
+
return `[${a.severity}] ${a.type}: ${subject}`;
|
|
35
|
+
});
|
|
36
|
+
callback?.({
|
|
37
|
+
text: `KOL alerts (${window}):\n${lines.join("\n") || "No alerts in window."}`,
|
|
38
|
+
content: data,
|
|
39
|
+
});
|
|
40
|
+
return undefined;
|
|
41
|
+
},
|
|
42
|
+
examples: [
|
|
43
|
+
[
|
|
44
|
+
{ name: "user1", content: { text: "What are the recent KOL alerts?" } },
|
|
45
|
+
{ name: "assistant", content: { text: "Here are the live KOL alerts..." } },
|
|
46
|
+
],
|
|
47
|
+
],
|
|
48
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { MadeOnSolClient } from "../client.js";
|
|
2
|
+
import { MADEONSOL_CLIENT_KEY } from "../index.js";
|
|
3
|
+
function getClient(runtime) {
|
|
4
|
+
return runtime[MADEONSOL_CLIENT_KEY] ?? new MadeOnSolClient();
|
|
5
|
+
}
|
|
6
|
+
const ADDR_RE = /\b[1-9A-HJ-NP-Za-km-z]{32,44}\b/g;
|
|
7
|
+
export const kolCompareAction = {
|
|
8
|
+
name: "GET_KOL_COMPARE",
|
|
9
|
+
description: "Compare 2-5 Solana KOL wallets side-by-side on MadeOnSol — strategy, winrates, ROI, percentile. PRO+ adds overlap tokens (bought by 2+ in last 30d).",
|
|
10
|
+
similes: [
|
|
11
|
+
"compare kols",
|
|
12
|
+
"compare wallets",
|
|
13
|
+
"kol comparison",
|
|
14
|
+
"side by side kols",
|
|
15
|
+
"who is better kol",
|
|
16
|
+
],
|
|
17
|
+
validate: async (_runtime, message) => {
|
|
18
|
+
const text = message.content?.text || "";
|
|
19
|
+
const matches = text.match(ADDR_RE) ?? [];
|
|
20
|
+
return /\bcompare\b/i.test(text) && matches.length >= 2;
|
|
21
|
+
},
|
|
22
|
+
handler: async (runtime, message, _state, _options, callback) => {
|
|
23
|
+
const client = getClient(runtime);
|
|
24
|
+
const wallets = ((message.content?.text || "").match(ADDR_RE) ?? []).slice(0, 5);
|
|
25
|
+
if (wallets.length < 2) {
|
|
26
|
+
callback?.({ text: "Please include at least 2 wallet addresses to compare." });
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
const result = await client.getKolCompare(wallets);
|
|
30
|
+
if (result.error) {
|
|
31
|
+
callback?.({ text: result.status === 402
|
|
32
|
+
? "Authentication required. Set MADEONSOL_API_KEY (free at madeonsol.com/developer), RAPIDAPI_KEY, or SVM_PRIVATE_KEY."
|
|
33
|
+
: `Error: ${result.error}` });
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const data = result.data;
|
|
37
|
+
const lines = (data.profiles || []).map((p) => {
|
|
38
|
+
const who = p.name || `${p.wallet_address.slice(0, 4)}…${p.wallet_address.slice(-4)}`;
|
|
39
|
+
const wr = p.winrate_7d != null ? `${p.winrate_7d.toFixed(1)}%` : "—";
|
|
40
|
+
const pnl = p.pnl_30d != null ? `${p.pnl_30d > 0 ? "+" : ""}${p.pnl_30d.toFixed(1)} SOL` : "—";
|
|
41
|
+
return `${who} [${p.strategy_tag || "mixed"}] winrate 7d: ${wr}, PnL 30d: ${pnl}`;
|
|
42
|
+
});
|
|
43
|
+
const overlap = (data.overlap || []).slice(0, 5).map((o) => `${o.token_symbol || "?"} (${o.wallets.length} wallets)`);
|
|
44
|
+
callback?.({
|
|
45
|
+
text: `KOL comparison:\n${lines.join("\n")}` +
|
|
46
|
+
(overlap.length ? `\n\nOverlap (30d): ${overlap.join(", ")}` : ""),
|
|
47
|
+
content: data,
|
|
48
|
+
});
|
|
49
|
+
return undefined;
|
|
50
|
+
},
|
|
51
|
+
examples: [
|
|
52
|
+
[
|
|
53
|
+
{ name: "user1", content: { text: "Compare these two KOLs: ABC...123 and DEF...456" } },
|
|
54
|
+
{ name: "assistant", content: { text: "Here's the side-by-side comparison..." } },
|
|
55
|
+
],
|
|
56
|
+
],
|
|
57
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { MadeOnSolClient } from "../client.js";
|
|
2
|
+
import { MADEONSOL_CLIENT_KEY } from "../index.js";
|
|
3
|
+
function getClient(runtime) {
|
|
4
|
+
return runtime[MADEONSOL_CLIENT_KEY] ?? new MadeOnSolClient();
|
|
5
|
+
}
|
|
6
|
+
const MINT_RE = /\b([1-9A-HJ-NP-Za-km-z]{32,44})\b/;
|
|
7
|
+
export const kolTokenEntryOrderAction = {
|
|
8
|
+
name: "GET_KOL_TOKEN_ENTRY_ORDER",
|
|
9
|
+
description: "Get the ranked order of KOL first-buyers for a specific Solana token from MadeOnSol. Shows who entered first and how quickly others followed.",
|
|
10
|
+
similes: [
|
|
11
|
+
"who bought first",
|
|
12
|
+
"first kol buyers",
|
|
13
|
+
"kol entry order",
|
|
14
|
+
"token entry ranking",
|
|
15
|
+
"who entered first",
|
|
16
|
+
],
|
|
17
|
+
validate: async (_runtime, message) => {
|
|
18
|
+
const text = message.content?.text || "";
|
|
19
|
+
return /\b(first|entry|entered|order)\b/i.test(text) && MINT_RE.test(text);
|
|
20
|
+
},
|
|
21
|
+
handler: async (runtime, message, _state, _options, callback) => {
|
|
22
|
+
const client = getClient(runtime);
|
|
23
|
+
const mint = (message.content?.text || "").match(MINT_RE)?.[1];
|
|
24
|
+
if (!mint) {
|
|
25
|
+
callback?.({ text: "Please include a token mint address." });
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const result = await client.getKolTokenEntryOrder(mint, { limit: "20" });
|
|
29
|
+
if (result.error) {
|
|
30
|
+
callback?.({ text: result.status === 402
|
|
31
|
+
? "Authentication required. Set MADEONSOL_API_KEY (free at madeonsol.com/developer), RAPIDAPI_KEY, or SVM_PRIVATE_KEY."
|
|
32
|
+
: `Error: ${result.error}` });
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const data = result.data;
|
|
36
|
+
const lines = (data.entries || []).slice(0, 10).map((e) => {
|
|
37
|
+
const who = e.kol_name || `${e.wallet_address.slice(0, 4)}…${e.wallet_address.slice(-4)}`;
|
|
38
|
+
const when = e.seconds_after_first === 0 ? "first" : `+${e.seconds_after_first}s`;
|
|
39
|
+
return `#${e.rank} ${who} — ${e.sol_amount.toFixed(2)} SOL (${when})`;
|
|
40
|
+
});
|
|
41
|
+
callback?.({
|
|
42
|
+
text: `KOL entry order for ${mint.slice(0, 8)}…:\n${lines.join("\n") || "No KOL buys recorded."}`,
|
|
43
|
+
content: data,
|
|
44
|
+
});
|
|
45
|
+
return undefined;
|
|
46
|
+
},
|
|
47
|
+
examples: [
|
|
48
|
+
[
|
|
49
|
+
{ name: "user1", content: { text: "Who were the first KOLs to buy 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU?" } },
|
|
50
|
+
{ name: "assistant", content: { text: "Here's the KOL entry order for that token..." } },
|
|
51
|
+
],
|
|
52
|
+
],
|
|
53
|
+
};
|
package/dist/client.d.ts
CHANGED
|
@@ -89,6 +89,28 @@ export declare class MadeOnSolClient {
|
|
|
89
89
|
error?: string;
|
|
90
90
|
status: number;
|
|
91
91
|
}>;
|
|
92
|
+
getKolTokenEntryOrder(mint: string, params?: {
|
|
93
|
+
limit?: string;
|
|
94
|
+
}): Promise<{
|
|
95
|
+
data?: unknown;
|
|
96
|
+
error?: string;
|
|
97
|
+
status: number;
|
|
98
|
+
}>;
|
|
99
|
+
getKolCompare(wallets: string[]): Promise<{
|
|
100
|
+
data?: unknown;
|
|
101
|
+
error?: string;
|
|
102
|
+
status: number;
|
|
103
|
+
}>;
|
|
104
|
+
getKolAlertsRecent(params?: {
|
|
105
|
+
window?: string;
|
|
106
|
+
types?: string;
|
|
107
|
+
min_severity?: string;
|
|
108
|
+
limit?: string;
|
|
109
|
+
}): Promise<{
|
|
110
|
+
data?: unknown;
|
|
111
|
+
error?: string;
|
|
112
|
+
status: number;
|
|
113
|
+
}>;
|
|
92
114
|
getKolPnl(wallet: string, params?: {
|
|
93
115
|
period?: string;
|
|
94
116
|
}): Promise<{
|
package/dist/client.js
CHANGED
|
@@ -78,6 +78,15 @@ export class MadeOnSolClient {
|
|
|
78
78
|
getKolTrendingTokens(params) {
|
|
79
79
|
return this.query("/api/x402/kol/tokens/trending", params);
|
|
80
80
|
}
|
|
81
|
+
getKolTokenEntryOrder(mint, params) {
|
|
82
|
+
return this.query(`/api/x402/kol/tokens/${encodeURIComponent(mint)}/entry-order`, params);
|
|
83
|
+
}
|
|
84
|
+
getKolCompare(wallets) {
|
|
85
|
+
return this.query("/api/x402/kol/compare", { wallets: wallets.join(",") });
|
|
86
|
+
}
|
|
87
|
+
getKolAlertsRecent(params) {
|
|
88
|
+
return this.query("/api/x402/kol/alerts/recent", params);
|
|
89
|
+
}
|
|
81
90
|
getKolPnl(wallet, params) {
|
|
82
91
|
const qs = params?.period ? `?period=${params.period}` : "";
|
|
83
92
|
return this.restRequest("GET", `/kol/${wallet}/pnl${qs}`);
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ import { kolCoordinationAction } from "./actions/kol-coordination.js";
|
|
|
4
4
|
import { kolLeaderboardAction } from "./actions/kol-leaderboard.js";
|
|
5
5
|
import { deployerAlertsAction } from "./actions/deployer-alerts.js";
|
|
6
6
|
import { walletTrackerWatchlistAction, walletTrackerTradesAction } from "./actions/wallet-tracker.js";
|
|
7
|
+
import { kolTokenEntryOrderAction } from "./actions/kol-token-entry-order.js";
|
|
8
|
+
import { kolCompareAction } from "./actions/kol-compare.js";
|
|
9
|
+
import { kolAlertsRecentAction } from "./actions/kol-alerts-recent.js";
|
|
7
10
|
/** Key used to store the initialized client on the runtime */
|
|
8
11
|
export declare const MADEONSOL_CLIENT_KEY = "madeonsol:client";
|
|
9
12
|
export declare const madeOnSolPlugin: Plugin;
|
|
@@ -11,3 +14,4 @@ export default madeOnSolPlugin;
|
|
|
11
14
|
export { MadeOnSolClient } from "./client.js";
|
|
12
15
|
export { kolFeedAction, kolCoordinationAction, kolLeaderboardAction, deployerAlertsAction };
|
|
13
16
|
export { walletTrackerWatchlistAction, walletTrackerTradesAction };
|
|
17
|
+
export { kolTokenEntryOrderAction, kolCompareAction, kolAlertsRecentAction };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,9 @@ import { kolCoordinationAction } from "./actions/kol-coordination.js";
|
|
|
3
3
|
import { kolLeaderboardAction } from "./actions/kol-leaderboard.js";
|
|
4
4
|
import { deployerAlertsAction } from "./actions/deployer-alerts.js";
|
|
5
5
|
import { walletTrackerWatchlistAction, walletTrackerTradesAction } from "./actions/wallet-tracker.js";
|
|
6
|
+
import { kolTokenEntryOrderAction } from "./actions/kol-token-entry-order.js";
|
|
7
|
+
import { kolCompareAction } from "./actions/kol-compare.js";
|
|
8
|
+
import { kolAlertsRecentAction } from "./actions/kol-alerts-recent.js";
|
|
6
9
|
import { MadeOnSolClient } from "./client.js";
|
|
7
10
|
/** Key used to store the initialized client on the runtime */
|
|
8
11
|
export const MADEONSOL_CLIENT_KEY = "madeonsol:client";
|
|
@@ -16,6 +19,9 @@ export const madeOnSolPlugin = {
|
|
|
16
19
|
deployerAlertsAction,
|
|
17
20
|
walletTrackerWatchlistAction,
|
|
18
21
|
walletTrackerTradesAction,
|
|
22
|
+
kolTokenEntryOrderAction,
|
|
23
|
+
kolCompareAction,
|
|
24
|
+
kolAlertsRecentAction,
|
|
19
25
|
],
|
|
20
26
|
/**
|
|
21
27
|
* Initialize the MadeOnSol client.
|
|
@@ -62,3 +68,4 @@ export default madeOnSolPlugin;
|
|
|
62
68
|
export { MadeOnSolClient } from "./client.js";
|
|
63
69
|
export { kolFeedAction, kolCoordinationAction, kolLeaderboardAction, deployerAlertsAction };
|
|
64
70
|
export { walletTrackerWatchlistAction, walletTrackerTradesAction };
|
|
71
|
+
export { kolTokenEntryOrderAction, kolCompareAction, kolAlertsRecentAction };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@madeonsol/plugin-madeonsol",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "ElizaOS plugin for MadeOnSol
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "ElizaOS plugin for MadeOnSol — Solana KOL intelligence and deployer analytics via x402 micropayments",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Action, IAgentRuntime, Memory, State, HandlerCallback } from "@elizaos/core";
|
|
2
|
+
import { MadeOnSolClient } from "../client.js";
|
|
3
|
+
import { MADEONSOL_CLIENT_KEY } from "../index.js";
|
|
4
|
+
|
|
5
|
+
function getClient(runtime: IAgentRuntime): MadeOnSolClient {
|
|
6
|
+
return ((runtime as unknown as Record<string, unknown>)[MADEONSOL_CLIENT_KEY] as MadeOnSolClient) ?? new MadeOnSolClient();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const kolAlertsRecentAction: Action = {
|
|
10
|
+
name: "GET_KOL_ALERTS_RECENT",
|
|
11
|
+
description:
|
|
12
|
+
"Get live KOL alerts from MadeOnSol — consensus clusters, fresh-token KOL buys, and heating-up wallets in one unified stream.",
|
|
13
|
+
similes: [
|
|
14
|
+
"kol alerts",
|
|
15
|
+
"recent alerts",
|
|
16
|
+
"kol signals",
|
|
17
|
+
"whats happening now",
|
|
18
|
+
"live kol feed",
|
|
19
|
+
],
|
|
20
|
+
|
|
21
|
+
validate: async (_runtime: IAgentRuntime, message: Memory): Promise<boolean> => {
|
|
22
|
+
const text = (message.content?.text || "").toLowerCase();
|
|
23
|
+
return /\b(kol|smart money)\b/.test(text) && /\b(alert|signal|recent|live|now)\b/.test(text);
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
handler: async (
|
|
27
|
+
runtime: IAgentRuntime,
|
|
28
|
+
message: Memory,
|
|
29
|
+
_state?: State,
|
|
30
|
+
_options?: unknown,
|
|
31
|
+
callback?: HandlerCallback,
|
|
32
|
+
) => {
|
|
33
|
+
const client = getClient(runtime);
|
|
34
|
+
const text = (message.content?.text || "").toLowerCase();
|
|
35
|
+
const window = text.includes("1h") ? "1h" : text.includes("6h") ? "6h" : text.includes("24h") ? "24h" : text.includes("5m") ? "5m" : "15m";
|
|
36
|
+
|
|
37
|
+
const result = await client.getKolAlertsRecent({ window, limit: "20" });
|
|
38
|
+
|
|
39
|
+
if (result.error) {
|
|
40
|
+
callback?.({ text: result.status === 402
|
|
41
|
+
? "Authentication required. Set MADEONSOL_API_KEY (free at madeonsol.com/developer), RAPIDAPI_KEY, or SVM_PRIVATE_KEY."
|
|
42
|
+
: `Error: ${result.error}` });
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const data = result.data as {
|
|
47
|
+
alerts: Array<{ type: string; severity: string; token_symbol: string | null; kol_name: string | null; detected_at: string }>;
|
|
48
|
+
};
|
|
49
|
+
const lines = (data.alerts || []).slice(0, 10).map((a) => {
|
|
50
|
+
const subject = a.token_symbol || a.kol_name || "—";
|
|
51
|
+
return `[${a.severity}] ${a.type}: ${subject}`;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
callback?.({
|
|
55
|
+
text: `KOL alerts (${window}):\n${lines.join("\n") || "No alerts in window."}`,
|
|
56
|
+
content: data,
|
|
57
|
+
});
|
|
58
|
+
return undefined;
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
examples: [
|
|
62
|
+
[
|
|
63
|
+
{ name: "user1", content: { text: "What are the recent KOL alerts?" } },
|
|
64
|
+
{ name: "assistant", content: { text: "Here are the live KOL alerts..." } },
|
|
65
|
+
],
|
|
66
|
+
] as Action["examples"],
|
|
67
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Action, IAgentRuntime, Memory, State, HandlerCallback } from "@elizaos/core";
|
|
2
|
+
import { MadeOnSolClient } from "../client.js";
|
|
3
|
+
import { MADEONSOL_CLIENT_KEY } from "../index.js";
|
|
4
|
+
|
|
5
|
+
function getClient(runtime: IAgentRuntime): MadeOnSolClient {
|
|
6
|
+
return ((runtime as unknown as Record<string, unknown>)[MADEONSOL_CLIENT_KEY] as MadeOnSolClient) ?? new MadeOnSolClient();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const ADDR_RE = /\b[1-9A-HJ-NP-Za-km-z]{32,44}\b/g;
|
|
10
|
+
|
|
11
|
+
export const kolCompareAction: Action = {
|
|
12
|
+
name: "GET_KOL_COMPARE",
|
|
13
|
+
description:
|
|
14
|
+
"Compare 2-5 Solana KOL wallets side-by-side on MadeOnSol — strategy, winrates, ROI, percentile. PRO+ adds overlap tokens (bought by 2+ in last 30d).",
|
|
15
|
+
similes: [
|
|
16
|
+
"compare kols",
|
|
17
|
+
"compare wallets",
|
|
18
|
+
"kol comparison",
|
|
19
|
+
"side by side kols",
|
|
20
|
+
"who is better kol",
|
|
21
|
+
],
|
|
22
|
+
|
|
23
|
+
validate: async (_runtime: IAgentRuntime, message: Memory): Promise<boolean> => {
|
|
24
|
+
const text = message.content?.text || "";
|
|
25
|
+
const matches = text.match(ADDR_RE) ?? [];
|
|
26
|
+
return /\bcompare\b/i.test(text) && matches.length >= 2;
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
handler: async (
|
|
30
|
+
runtime: IAgentRuntime,
|
|
31
|
+
message: Memory,
|
|
32
|
+
_state?: State,
|
|
33
|
+
_options?: unknown,
|
|
34
|
+
callback?: HandlerCallback,
|
|
35
|
+
) => {
|
|
36
|
+
const client = getClient(runtime);
|
|
37
|
+
const wallets = ((message.content?.text || "").match(ADDR_RE) ?? []).slice(0, 5);
|
|
38
|
+
if (wallets.length < 2) {
|
|
39
|
+
callback?.({ text: "Please include at least 2 wallet addresses to compare." });
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const result = await client.getKolCompare(wallets);
|
|
44
|
+
|
|
45
|
+
if (result.error) {
|
|
46
|
+
callback?.({ text: result.status === 402
|
|
47
|
+
? "Authentication required. Set MADEONSOL_API_KEY (free at madeonsol.com/developer), RAPIDAPI_KEY, or SVM_PRIVATE_KEY."
|
|
48
|
+
: `Error: ${result.error}` });
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const data = result.data as {
|
|
53
|
+
profiles: Array<{ wallet_address: string; name: string | null; strategy_tag: string | null; winrate_7d: number | null; pnl_30d: number | null }>;
|
|
54
|
+
overlap?: Array<{ token_symbol: string | null; wallets: string[] }>;
|
|
55
|
+
};
|
|
56
|
+
const lines = (data.profiles || []).map((p) => {
|
|
57
|
+
const who = p.name || `${p.wallet_address.slice(0, 4)}…${p.wallet_address.slice(-4)}`;
|
|
58
|
+
const wr = p.winrate_7d != null ? `${p.winrate_7d.toFixed(1)}%` : "—";
|
|
59
|
+
const pnl = p.pnl_30d != null ? `${p.pnl_30d > 0 ? "+" : ""}${p.pnl_30d.toFixed(1)} SOL` : "—";
|
|
60
|
+
return `${who} [${p.strategy_tag || "mixed"}] winrate 7d: ${wr}, PnL 30d: ${pnl}`;
|
|
61
|
+
});
|
|
62
|
+
const overlap = (data.overlap || []).slice(0, 5).map((o) => `${o.token_symbol || "?"} (${o.wallets.length} wallets)`);
|
|
63
|
+
|
|
64
|
+
callback?.({
|
|
65
|
+
text:
|
|
66
|
+
`KOL comparison:\n${lines.join("\n")}` +
|
|
67
|
+
(overlap.length ? `\n\nOverlap (30d): ${overlap.join(", ")}` : ""),
|
|
68
|
+
content: data,
|
|
69
|
+
});
|
|
70
|
+
return undefined;
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
examples: [
|
|
74
|
+
[
|
|
75
|
+
{ name: "user1", content: { text: "Compare these two KOLs: ABC...123 and DEF...456" } },
|
|
76
|
+
{ name: "assistant", content: { text: "Here's the side-by-side comparison..." } },
|
|
77
|
+
],
|
|
78
|
+
] as Action["examples"],
|
|
79
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Action, IAgentRuntime, Memory, State, HandlerCallback } from "@elizaos/core";
|
|
2
|
+
import { MadeOnSolClient } from "../client.js";
|
|
3
|
+
import { MADEONSOL_CLIENT_KEY } from "../index.js";
|
|
4
|
+
|
|
5
|
+
function getClient(runtime: IAgentRuntime): MadeOnSolClient {
|
|
6
|
+
return ((runtime as unknown as Record<string, unknown>)[MADEONSOL_CLIENT_KEY] as MadeOnSolClient) ?? new MadeOnSolClient();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const MINT_RE = /\b([1-9A-HJ-NP-Za-km-z]{32,44})\b/;
|
|
10
|
+
|
|
11
|
+
export const kolTokenEntryOrderAction: Action = {
|
|
12
|
+
name: "GET_KOL_TOKEN_ENTRY_ORDER",
|
|
13
|
+
description:
|
|
14
|
+
"Get the ranked order of KOL first-buyers for a specific Solana token from MadeOnSol. Shows who entered first and how quickly others followed.",
|
|
15
|
+
similes: [
|
|
16
|
+
"who bought first",
|
|
17
|
+
"first kol buyers",
|
|
18
|
+
"kol entry order",
|
|
19
|
+
"token entry ranking",
|
|
20
|
+
"who entered first",
|
|
21
|
+
],
|
|
22
|
+
|
|
23
|
+
validate: async (_runtime: IAgentRuntime, message: Memory): Promise<boolean> => {
|
|
24
|
+
const text = message.content?.text || "";
|
|
25
|
+
return /\b(first|entry|entered|order)\b/i.test(text) && MINT_RE.test(text);
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
handler: async (
|
|
29
|
+
runtime: IAgentRuntime,
|
|
30
|
+
message: Memory,
|
|
31
|
+
_state?: State,
|
|
32
|
+
_options?: unknown,
|
|
33
|
+
callback?: HandlerCallback,
|
|
34
|
+
) => {
|
|
35
|
+
const client = getClient(runtime);
|
|
36
|
+
const mint = (message.content?.text || "").match(MINT_RE)?.[1];
|
|
37
|
+
if (!mint) {
|
|
38
|
+
callback?.({ text: "Please include a token mint address." });
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const result = await client.getKolTokenEntryOrder(mint, { limit: "20" });
|
|
43
|
+
|
|
44
|
+
if (result.error) {
|
|
45
|
+
callback?.({ text: result.status === 402
|
|
46
|
+
? "Authentication required. Set MADEONSOL_API_KEY (free at madeonsol.com/developer), RAPIDAPI_KEY, or SVM_PRIVATE_KEY."
|
|
47
|
+
: `Error: ${result.error}` });
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const data = result.data as { entries: Array<{ rank: number; kol_name: string | null; wallet_address: string; sol_amount: number; seconds_after_first: number }> };
|
|
52
|
+
const lines = (data.entries || []).slice(0, 10).map((e) => {
|
|
53
|
+
const who = e.kol_name || `${e.wallet_address.slice(0, 4)}…${e.wallet_address.slice(-4)}`;
|
|
54
|
+
const when = e.seconds_after_first === 0 ? "first" : `+${e.seconds_after_first}s`;
|
|
55
|
+
return `#${e.rank} ${who} — ${e.sol_amount.toFixed(2)} SOL (${when})`;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
callback?.({
|
|
59
|
+
text: `KOL entry order for ${mint.slice(0, 8)}…:\n${lines.join("\n") || "No KOL buys recorded."}`,
|
|
60
|
+
content: data,
|
|
61
|
+
});
|
|
62
|
+
return undefined;
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
examples: [
|
|
66
|
+
[
|
|
67
|
+
{ name: "user1", content: { text: "Who were the first KOLs to buy 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU?" } },
|
|
68
|
+
{ name: "assistant", content: { text: "Here's the KOL entry order for that token..." } },
|
|
69
|
+
],
|
|
70
|
+
] as Action["examples"],
|
|
71
|
+
};
|
package/src/client.ts
CHANGED
|
@@ -103,6 +103,18 @@ export class MadeOnSolClient {
|
|
|
103
103
|
return this.query("/api/x402/kol/tokens/trending", params);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
getKolTokenEntryOrder(mint: string, params?: { limit?: string }) {
|
|
107
|
+
return this.query(`/api/x402/kol/tokens/${encodeURIComponent(mint)}/entry-order`, params);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getKolCompare(wallets: string[]) {
|
|
111
|
+
return this.query("/api/x402/kol/compare", { wallets: wallets.join(",") });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
getKolAlertsRecent(params?: { window?: string; types?: string; min_severity?: string; limit?: string }) {
|
|
115
|
+
return this.query("/api/x402/kol/alerts/recent", params);
|
|
116
|
+
}
|
|
117
|
+
|
|
106
118
|
getKolPnl(wallet: string, params?: { period?: string }) {
|
|
107
119
|
const qs = params?.period ? `?period=${params.period}` : "";
|
|
108
120
|
return this.restRequest("GET", `/kol/${wallet}/pnl${qs}`);
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,9 @@ import { kolCoordinationAction } from "./actions/kol-coordination.js";
|
|
|
4
4
|
import { kolLeaderboardAction } from "./actions/kol-leaderboard.js";
|
|
5
5
|
import { deployerAlertsAction } from "./actions/deployer-alerts.js";
|
|
6
6
|
import { walletTrackerWatchlistAction, walletTrackerTradesAction } from "./actions/wallet-tracker.js";
|
|
7
|
+
import { kolTokenEntryOrderAction } from "./actions/kol-token-entry-order.js";
|
|
8
|
+
import { kolCompareAction } from "./actions/kol-compare.js";
|
|
9
|
+
import { kolAlertsRecentAction } from "./actions/kol-alerts-recent.js";
|
|
7
10
|
import { MadeOnSolClient } from "./client.js";
|
|
8
11
|
|
|
9
12
|
/** Key used to store the initialized client on the runtime */
|
|
@@ -20,6 +23,9 @@ export const madeOnSolPlugin: Plugin = {
|
|
|
20
23
|
deployerAlertsAction,
|
|
21
24
|
walletTrackerWatchlistAction,
|
|
22
25
|
walletTrackerTradesAction,
|
|
26
|
+
kolTokenEntryOrderAction,
|
|
27
|
+
kolCompareAction,
|
|
28
|
+
kolAlertsRecentAction,
|
|
23
29
|
],
|
|
24
30
|
|
|
25
31
|
/**
|
|
@@ -69,3 +75,4 @@ export default madeOnSolPlugin;
|
|
|
69
75
|
export { MadeOnSolClient } from "./client.js";
|
|
70
76
|
export { kolFeedAction, kolCoordinationAction, kolLeaderboardAction, deployerAlertsAction };
|
|
71
77
|
export { walletTrackerWatchlistAction, walletTrackerTradesAction };
|
|
78
|
+
export { kolTokenEntryOrderAction, kolCompareAction, kolAlertsRecentAction };
|