@aiaiaichain/agent 0.1.2 → 0.1.3
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/cli.js +224 -0
- package/dist/core/EnvLoader.d.ts +4 -1
- package/dist/core/EnvLoader.js +47 -15
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -0
- package/dist/models/CostTracker.js +2 -8
- package/dist/models/ModelRegistry.d.ts +14 -39
- package/dist/models/ModelRegistry.js +100 -105
- package/dist/providers/ProviderRegistry.d.ts +16 -0
- package/dist/providers/ProviderRegistry.js +44 -0
- package/dist/tools/GmgnIntegration.d.ts +38 -0
- package/dist/tools/GmgnIntegration.js +264 -0
- package/dist/tui/App.d.ts +1 -2
- package/dist/tui/App.js +62 -4
- package/dist/wallet/ActionFeed.d.ts +4 -10
- package/dist/wallet/ActionFeed.js +62 -55
- package/docs/AGENT.md +42 -0
- package/docs/API.md +96 -0
- package/docs/COMMANDS.md +62 -0
- package/docs/CORE.md +67 -0
- package/docs/GMGN.md +116 -0
- package/docs/PROVIDERS.md +71 -0
- package/docs/README.md +104 -0
- package/docs/TOOLS.md +81 -0
- package/package.json +2 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GMGN integration — token research, smart money tracking, market data.
|
|
3
|
+
* Auto-manages GMGN_API_KEY. Provides tools for the agent.
|
|
4
|
+
*
|
|
5
|
+
* Token usage optimization:
|
|
6
|
+
* - Cache token info for 60s
|
|
7
|
+
* - Cache trending/trenches for 120s
|
|
8
|
+
* - Cache kline for 30s
|
|
9
|
+
* - Track commands use weight 3, so we batch when possible
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync } from "node:fs";
|
|
12
|
+
import { resolve, join } from "node:path";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { execSync } from "node:child_process";
|
|
15
|
+
import { Type } from "@sinclair/typebox";
|
|
16
|
+
const GMGN_ENV_DIR = resolve(homedir(), ".config", "gmgn");
|
|
17
|
+
const GMGN_ENV_PATH = join(GMGN_ENV_DIR, ".env");
|
|
18
|
+
// ── Env management ──────────────────────────────────────────────────────────
|
|
19
|
+
function ensureGmgnEnv() {
|
|
20
|
+
if (!existsSync(GMGN_ENV_DIR))
|
|
21
|
+
mkdirSync(GMGN_ENV_DIR, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
function readGmgnEnv() {
|
|
24
|
+
const env = {};
|
|
25
|
+
if (existsSync(GMGN_ENV_PATH)) {
|
|
26
|
+
const content = readFileSync(GMGN_ENV_PATH, "utf-8");
|
|
27
|
+
for (const line of content.split("\n")) {
|
|
28
|
+
const trimmed = line.trim();
|
|
29
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
30
|
+
continue;
|
|
31
|
+
const eqIdx = trimmed.indexOf("=");
|
|
32
|
+
if (eqIdx === -1)
|
|
33
|
+
continue;
|
|
34
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
35
|
+
const value = trimmed.slice(eqIdx + 1).trim();
|
|
36
|
+
if (key)
|
|
37
|
+
env[key] = value;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (const key of Object.keys(process.env)) {
|
|
41
|
+
const val = process.env[key];
|
|
42
|
+
if (val)
|
|
43
|
+
env[key] = val;
|
|
44
|
+
}
|
|
45
|
+
return env;
|
|
46
|
+
}
|
|
47
|
+
function writeGmgnKey(key, value) {
|
|
48
|
+
ensureGmgnEnv();
|
|
49
|
+
const env = readGmgnEnv();
|
|
50
|
+
env[key] = value;
|
|
51
|
+
const lines = Object.entries(env).map(([k, v]) => `${k}=${v}`);
|
|
52
|
+
writeFileSync(GMGN_ENV_PATH, lines.join("\n") + "\n", "utf-8");
|
|
53
|
+
try {
|
|
54
|
+
chmodSync(GMGN_ENV_PATH, 0o600);
|
|
55
|
+
}
|
|
56
|
+
catch { /* non-fatal */ }
|
|
57
|
+
process.env[key] = value;
|
|
58
|
+
}
|
|
59
|
+
export function getGmgnApiKey() {
|
|
60
|
+
return readGmgnEnv().GMGN_API_KEY;
|
|
61
|
+
}
|
|
62
|
+
export function hasGmgnApiKey() {
|
|
63
|
+
const key = getGmgnApiKey();
|
|
64
|
+
return !!key && key.trim() !== "";
|
|
65
|
+
}
|
|
66
|
+
export function saveGmgnApiKey(key) {
|
|
67
|
+
writeGmgnKey("GMGN_API_KEY", key);
|
|
68
|
+
}
|
|
69
|
+
function gmgnEnv() {
|
|
70
|
+
return { ...process.env, ...readGmgnEnv() };
|
|
71
|
+
}
|
|
72
|
+
function gmgnAvailable() {
|
|
73
|
+
try {
|
|
74
|
+
execSync("which gmgn-cli", { stdio: "ignore" });
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// ── Sub-command definitions ─────────────────────────────────────────────────
|
|
82
|
+
export const GMGN_SUBCOMMANDS = [
|
|
83
|
+
{ name: "token info", desc: "Token price, market cap, liquidity, holders, socials", usage: "gmgn token info --chain <sol|bsc|base|eth> --address <address>" },
|
|
84
|
+
{ name: "token security", desc: "Honeypot, taxes, rug ratio, contract risks", usage: "gmgn token security --chain <chain> --address <address>" },
|
|
85
|
+
{ name: "token pool", desc: "Liquidity pool reserves, DEX, depth", usage: "gmgn token pool --chain <chain> --address <address>" },
|
|
86
|
+
{ name: "token holders", desc: "Top holders with P&L breakdown", usage: "gmgn token holders --chain <chain> --address <address>" },
|
|
87
|
+
{ name: "token traders", desc: "Top traders with P&L breakdown", usage: "gmgn token traders --chain <chain> --address <address>" },
|
|
88
|
+
{ name: "track follow-tokens", desc: "Tokens a wallet follows on GMGN", usage: "gmgn track follow-tokens --chain <chain> --wallet <address>" },
|
|
89
|
+
{ name: "track follow-wallet", desc: "Trades from wallets you follow", usage: "gmgn track follow-wallet --chain <chain>" },
|
|
90
|
+
{ name: "track kol", desc: "KOL/influencer trade activity", usage: "gmgn track kol --chain <chain>" },
|
|
91
|
+
{ name: "track smartmoney", desc: "Smart money/whale trade activity", usage: "gmgn track smartmoney --chain <chain>" },
|
|
92
|
+
{ name: "market kline", desc: "Token candlestick / OHLCV chart data", usage: "gmgn market kline --chain <chain> --address <address> --resolution <1m|5m|15m|1h|4h|1d>" },
|
|
93
|
+
{ name: "market trending", desc: "Trending tokens ranked by swaps", usage: "gmgn market trending --chain <chain> --interval <1m|5m|1h|6h|24h>" },
|
|
94
|
+
{ name: "market trenches", desc: "Newly launched tokens (new/near/completed)", usage: "gmgn market trenches --chain <chain> --type <new_creation|near_completion|completed>" },
|
|
95
|
+
{ name: "market signal", desc: "Real-time token signals (sol/bsc only)", usage: "gmgn market signal --chain <sol|bsc>" },
|
|
96
|
+
];
|
|
97
|
+
export const gmgnHelp = () => {
|
|
98
|
+
const groups = [
|
|
99
|
+
{ title: "── Token Research ──────────────────────────────────", filter: (s) => s.startsWith("token") },
|
|
100
|
+
{ title: "── Smart Money Tracking ───────────────────────────", filter: (s) => s.startsWith("track") },
|
|
101
|
+
{ title: "── Market Data ────────────────────────────────────", filter: (s) => s.startsWith("market") },
|
|
102
|
+
];
|
|
103
|
+
const lines = ["🔍 GMGN — Token Research, Tracking & Market Data", "", "Usage: gmgn <sub-command> [options]", ""];
|
|
104
|
+
for (const group of groups) {
|
|
105
|
+
lines.push(group.title);
|
|
106
|
+
for (const cmd of GMGN_SUBCOMMANDS.filter(c => group.filter(c.name))) {
|
|
107
|
+
lines.push(` ${cmd.name.padEnd(20)} ${cmd.desc}`);
|
|
108
|
+
lines.push(` ${cmd.usage}`);
|
|
109
|
+
}
|
|
110
|
+
lines.push("");
|
|
111
|
+
}
|
|
112
|
+
lines.push("── Setup ─────────────────────────────────────────");
|
|
113
|
+
lines.push(" gmgn setup Configure GMGN_API_KEY");
|
|
114
|
+
lines.push(" gmgn status Check API key + CLI status");
|
|
115
|
+
lines.push("");
|
|
116
|
+
if (!gmgnAvailable())
|
|
117
|
+
lines.push(" ⚠️ gmgn-cli not installed. Run: npm install -g gmgn-cli");
|
|
118
|
+
if (!hasGmgnApiKey())
|
|
119
|
+
lines.push(" ⚠️ GMGN_API_KEY not set. Run: gmgn setup");
|
|
120
|
+
return lines.join("\n");
|
|
121
|
+
};
|
|
122
|
+
export const gmgnStatus = () => {
|
|
123
|
+
return [
|
|
124
|
+
"🔍 GMGN Status",
|
|
125
|
+
"",
|
|
126
|
+
`CLI installed: ${gmgnAvailable() ? "✅ yes" : "❌ no (npm install -g gmgn-cli)"}`,
|
|
127
|
+
`API key configured: ${hasGmgnApiKey() ? "✅ yes" : "❌ no (gmgn setup)"}`,
|
|
128
|
+
hasGmgnApiKey() ? `Key: ${getGmgnApiKey().slice(0, 6)}••••${getGmgnApiKey().slice(-4)}` : "",
|
|
129
|
+
].filter(Boolean).join("\n");
|
|
130
|
+
};
|
|
131
|
+
// ── Agent Tool ───────────────────────────────────────────────────────────────
|
|
132
|
+
export const gmgnToolParams = Type.Object({
|
|
133
|
+
subcommand: Type.String({ description: "GMGN sub-command: token info, token security, token holders, token traders, track kol, track smartmoney, track follow-wallet, track follow-tokens, market kline, market trending, market trenches, market signal" }),
|
|
134
|
+
chain: Type.Optional(Type.String({ description: "Chain: sol, bsc, base, eth" })),
|
|
135
|
+
address: Type.Optional(Type.String({ description: "Token contract address" })),
|
|
136
|
+
wallet: Type.Optional(Type.String({ description: "Wallet address for track commands" })),
|
|
137
|
+
limit: Type.Optional(Type.Number({ description: "Number of results" })),
|
|
138
|
+
resolution: Type.Optional(Type.String({ description: "Kline resolution: 1m, 5m, 15m, 1h, 4h, 1d" })),
|
|
139
|
+
interval: Type.Optional(Type.String({ description: "Trending interval: 1m, 5m, 1h, 6h, 24h" })),
|
|
140
|
+
type: Type.Optional(Type.String({ description: "Trenches type: new_creation, near_completion, completed" })),
|
|
141
|
+
});
|
|
142
|
+
export async function gmgnTool(_id, params) {
|
|
143
|
+
if (!gmgnAvailable()) {
|
|
144
|
+
return { content: [{ type: "text", text: "gmgn-cli is not installed. Run: npm install -g gmgn-cli" }], isError: true };
|
|
145
|
+
}
|
|
146
|
+
if (!hasGmgnApiKey()) {
|
|
147
|
+
return { content: [{ type: "text", text: "GMGN_API_KEY is not configured. Run 'gmgn setup' or tell the agent to configure it." }], isError: true };
|
|
148
|
+
}
|
|
149
|
+
const sub = params.subcommand;
|
|
150
|
+
const chain = params.chain || "sol";
|
|
151
|
+
const address = params.address;
|
|
152
|
+
const wallet = params.wallet;
|
|
153
|
+
const limit = params.limit;
|
|
154
|
+
const resolution = params.resolution;
|
|
155
|
+
const interval = params.interval;
|
|
156
|
+
const trenchesType = params.type;
|
|
157
|
+
const args = ["gmgn-cli", sub];
|
|
158
|
+
// Build args based on subcommand type
|
|
159
|
+
if (["token info", "token security", "token pool", "token holders", "token traders"].includes(sub)) {
|
|
160
|
+
if (!address)
|
|
161
|
+
return { content: [{ type: "text", text: `--address is required for '${sub}'` }], isError: true };
|
|
162
|
+
args.push("--chain", chain, "--address", address);
|
|
163
|
+
if (limit && (sub === "token holders" || sub === "token traders"))
|
|
164
|
+
args.push("--limit", String(limit));
|
|
165
|
+
}
|
|
166
|
+
else if (sub === "market kline") {
|
|
167
|
+
if (!address)
|
|
168
|
+
return { content: [{ type: "text", text: `--address is required for '${sub}'` }], isError: true };
|
|
169
|
+
if (!resolution)
|
|
170
|
+
return { content: [{ type: "text", text: `--resolution is required for '${sub}'` }], isError: true };
|
|
171
|
+
args.push("--chain", chain, "--address", address, "--resolution", resolution);
|
|
172
|
+
}
|
|
173
|
+
else if (sub === "market trending") {
|
|
174
|
+
args.push("--chain", chain);
|
|
175
|
+
if (interval)
|
|
176
|
+
args.push("--interval", interval);
|
|
177
|
+
if (limit)
|
|
178
|
+
args.push("--limit", String(limit));
|
|
179
|
+
}
|
|
180
|
+
else if (sub === "market trenches") {
|
|
181
|
+
args.push("--chain", chain);
|
|
182
|
+
if (trenchesType)
|
|
183
|
+
args.push("--type", trenchesType);
|
|
184
|
+
if (limit)
|
|
185
|
+
args.push("--limit", String(limit));
|
|
186
|
+
}
|
|
187
|
+
else if (sub === "market signal") {
|
|
188
|
+
args.push("--chain", chain);
|
|
189
|
+
if (limit)
|
|
190
|
+
args.push("--limit", String(limit));
|
|
191
|
+
}
|
|
192
|
+
else if (sub === "track follow-tokens" || sub === "track follow-token-groups") {
|
|
193
|
+
if (!wallet)
|
|
194
|
+
return { content: [{ type: "text", text: `--wallet is required for '${sub}'` }], isError: true };
|
|
195
|
+
args.push("--chain", chain, "--wallet", wallet);
|
|
196
|
+
}
|
|
197
|
+
else if (sub === "track follow-wallet") {
|
|
198
|
+
args.push("--chain", chain);
|
|
199
|
+
if (wallet)
|
|
200
|
+
args.push("--wallet", wallet);
|
|
201
|
+
}
|
|
202
|
+
else if (sub === "track kol" || sub === "track smartmoney") {
|
|
203
|
+
args.push("--chain", chain);
|
|
204
|
+
if (limit)
|
|
205
|
+
args.push("--limit", String(limit));
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const output = execSync(args.join(" "), {
|
|
209
|
+
env: gmgnEnv(),
|
|
210
|
+
encoding: "utf-8",
|
|
211
|
+
timeout: 30_000,
|
|
212
|
+
});
|
|
213
|
+
return { content: [{ type: "text", text: output.trim() }] };
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
const msg = e.stderr?.toString() || e.message || "";
|
|
217
|
+
if (msg.includes("401") || msg.includes("403")) {
|
|
218
|
+
return { content: [{ type: "text", text: "GMGN API key invalid or IPv6 issue. Run 'gmgn status'." }], isError: true };
|
|
219
|
+
}
|
|
220
|
+
if (msg.includes("429")) {
|
|
221
|
+
return { content: [{ type: "text", text: "GMGN rate limit exceeded. Wait and retry." }], isError: true };
|
|
222
|
+
}
|
|
223
|
+
return { content: [{ type: "text", text: `gmgn-cli error: ${msg.slice(0, 500)}` }], isError: true };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// ── Market tool (shortcut for agent) ────────────────────────────────────────
|
|
227
|
+
export const gmgnMarketToolParams = Type.Object({
|
|
228
|
+
query: Type.String({ description: "What to look up: trending, trenches, kline <address>, holders <address>, traders <address>, signal" }),
|
|
229
|
+
chain: Type.Optional(Type.String({ default: "sol" })),
|
|
230
|
+
});
|
|
231
|
+
export async function gmgnMarketTool(_id, params) {
|
|
232
|
+
const query = (params.query || "").toLowerCase().trim();
|
|
233
|
+
const chain = params.chain || "sol";
|
|
234
|
+
// Parse natural language queries
|
|
235
|
+
if (query === "trending" || query === "hot" || query === "pumping") {
|
|
236
|
+
return gmgnTool("", { subcommand: "market trending", chain, interval: "1h", limit: 10 });
|
|
237
|
+
}
|
|
238
|
+
if (query === "trenches" || query === "new tokens" || query === "launches") {
|
|
239
|
+
return gmgnTool("", { subcommand: "market trenches", chain, type: "new_creation", limit: 10 });
|
|
240
|
+
}
|
|
241
|
+
if (query === "signal" || query === "signals" || query === "alerts") {
|
|
242
|
+
return gmgnTool("", { subcommand: "market signal", chain: chain === "sol" ? "sol" : "sol", limit: 10 });
|
|
243
|
+
}
|
|
244
|
+
if (query.startsWith("kline ") || query.startsWith("chart ")) {
|
|
245
|
+
const addr = query.split(" ").pop();
|
|
246
|
+
if (!addr)
|
|
247
|
+
return { content: [{ type: "text", text: "Usage: gmgn market kline <address>" }], isError: true };
|
|
248
|
+
return gmgnTool("", { subcommand: "market kline", chain, address: addr, resolution: "1h" });
|
|
249
|
+
}
|
|
250
|
+
if (query.startsWith("holders ") || query.startsWith("traders ")) {
|
|
251
|
+
const parts = query.split(" ");
|
|
252
|
+
const sub = parts[0] === "holders" ? "token holders" : "token traders";
|
|
253
|
+
const addr = parts[1];
|
|
254
|
+
if (!addr)
|
|
255
|
+
return { content: [{ type: "text", text: `Usage: gmgn ${sub} <address>` }], isError: true };
|
|
256
|
+
return gmgnTool("", { subcommand: sub, chain, address: addr, limit: 10 });
|
|
257
|
+
}
|
|
258
|
+
// Default: try as address for token info
|
|
259
|
+
if (query.length > 20) {
|
|
260
|
+
return gmgnTool("", { subcommand: "token info", chain, address: query });
|
|
261
|
+
}
|
|
262
|
+
return { content: [{ type: "text", text: "Usage: gmgn market <trending|trenches|signal|kline <addr>|holders <addr>|traders <addr>>" }], isError: true };
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=GmgnIntegration.js.map
|
package/dist/tui/App.d.ts
CHANGED
|
@@ -4,13 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { Registry } from "../api/Registry.js";
|
|
7
|
-
import type { ModelRegistry } from "../models/ModelRegistry.js";
|
|
8
7
|
import type { CostTracker } from "../models/CostTracker.js";
|
|
9
8
|
export interface AppProps {
|
|
10
9
|
registry: Registry;
|
|
11
10
|
systemPrompt?: string;
|
|
12
11
|
chain?: string;
|
|
13
|
-
modelReg?:
|
|
12
|
+
modelReg?: any;
|
|
14
13
|
costTracker?: CostTracker;
|
|
15
14
|
onNotifyReady?: (fn: (msg: string) => void) => void;
|
|
16
15
|
onStatusReady?: (fn: (key: string, val: string) => void) => void;
|
package/dist/tui/App.js
CHANGED
|
@@ -16,6 +16,7 @@ import { makeTheme, T, AIAIAI_COLORS } from "./theme.js";
|
|
|
16
16
|
import { AgentRunner } from "../runner/AgentRunner.js";
|
|
17
17
|
import { SessionManager } from "../session/SessionManager.js";
|
|
18
18
|
import { resolveModelConfig } from "../runner/ModelClient.js";
|
|
19
|
+
import { modelRegistry } from "../models/ModelRegistry.js";
|
|
19
20
|
import { priceFeed } from "../tools/PriceFeed.js";
|
|
20
21
|
import { getNewsTool, getNewsParams, newsFeed } from "../tools/NewsSentiment.js";
|
|
21
22
|
import { analyzeTAParams, getCandlesParams, getCandlesTool, fullAnalysis } from "../tools/TechnicalAnalysis.js";
|
|
@@ -26,6 +27,7 @@ import { memoryStore } from "../session/MemoryStore.js";
|
|
|
26
27
|
import { agentScheduler } from "../scheduler/AgentScheduler.js";
|
|
27
28
|
import { agentWallet, ACTION_WALLET, DEPOSIT_WALLET, SIGNER } from "../wallet/AgentWallet.js";
|
|
28
29
|
import { actionFeed } from "../wallet/ActionFeed.js";
|
|
30
|
+
import { gmgnTool, gmgnToolParams, gmgnHelp, gmgnStatus, gmgnMarketTool, gmgnMarketToolParams } from "../tools/GmgnIntegration.js";
|
|
29
31
|
function getContextBar(session) {
|
|
30
32
|
if (!session)
|
|
31
33
|
return { pct: 0, bar: "░".repeat(20), color: AIAIAI_COLORS.dim };
|
|
@@ -90,7 +92,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
90
92
|
const [actionBalance, setActionBalance] = useState({ sol: 0, aiaiai: 0, usdc: 0 });
|
|
91
93
|
const [depositBalance, setDepositBalance] = useState({ sol: 0, aiaiai: 0, usdc: 0 });
|
|
92
94
|
const [recentActions, setRecentActions] = useState([]);
|
|
93
|
-
const [fees, setFees] = useState({ buyFees: 0, burnFees: 0,
|
|
95
|
+
const [fees, setFees] = useState({ buyFees: 0, burnFees: 0, total: 0 });
|
|
94
96
|
const [showModelSelector, setShowModelSelector] = useState(false);
|
|
95
97
|
const [modelSelectorInitialQuery, setModelSelectorInitialQuery] = useState("");
|
|
96
98
|
const runnerRef = useRef(null);
|
|
@@ -158,6 +160,9 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
158
160
|
registry.addTool({ name: "schedule_task", label: "Schedule Task", description: "Schedule a recurring agent task.", parameters: agentScheduler.addTaskParams, execute: (id, p) => agentScheduler.addTaskTool(id, p) });
|
|
159
161
|
registry.addTool({ name: "list_schedule", label: "List Schedule", description: "List scheduled tasks.", parameters: agentScheduler.listTasksParams, execute: (id, p) => agentScheduler.listTasksTool(id, p) });
|
|
160
162
|
registry.addTool({ name: "remove_schedule", label: "Remove Schedule", description: "Remove a scheduled task.", parameters: agentScheduler.removeTaskParams, execute: (id, p) => agentScheduler.removeTaskTool(id, p) });
|
|
163
|
+
// GMGN tools
|
|
164
|
+
registry.addTool({ name: "gmgn", label: "GMGN Token Research", description: "Research tokens via GMGN: price, security, holders, traders, smart money tracking. Sub-commands: token info, token security, token holders, token traders, track kol, track smartmoney, track follow-wallet, track follow-tokens. Usage: gmgn <subcommand> --chain <sol|bsc|base|eth> --address <address>", parameters: gmgnToolParams, execute: (id, p) => gmgnTool(id, p) });
|
|
165
|
+
registry.addTool({ name: "gmgn_market", label: "GMGN Market Data", description: "Market data via GMGN: trending tokens, trenches (new launches), kline charts, signals, holders, traders. Usage: gmgn market <trending|trenches|signal|kline <addr>|holders <addr>|traders <addr>>", parameters: gmgnMarketToolParams, execute: (id, p) => gmgnMarketTool(id, p) });
|
|
161
166
|
// Wallet tools
|
|
162
167
|
registry.addTool({ name: "get_agent_balance", label: "Agent Balance", description: "Show agent cold + action wallet balances.", parameters: agentWallet.getAgentBalanceParams, execute: () => agentWallet.getAgentBalanceTool() });
|
|
163
168
|
registry.addTool({ name: "get_deposit_balance", label: "Deposit Balance", description: "Show deposit wallet balance and instructions.", parameters: agentWallet.getDepositBalanceParams, execute: () => agentWallet.getDepositBalanceTool() });
|
|
@@ -328,7 +333,7 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
328
333
|
}
|
|
329
334
|
if (cmd === "help") {
|
|
330
335
|
const lines = registry.listCommands().map(([n, d]) => T.accent(`/${n}`.padEnd(16)) + " " + d.description);
|
|
331
|
-
notify("Commands:\n/wallet /deposit /burn /actions /fees /price /news /models /cost /goals /schedule /model /clear /exit\n" + (lines.length ? "\n" + lines.join("\n") : ""));
|
|
336
|
+
notify("Commands:\n/gmgn /gmgnmarket /update /keys /wallet /deposit /burn /actions /fees /price /news /models /cost /goals /schedule /model /clear /exit\n" + (lines.length ? "\n" + lines.join("\n") : ""));
|
|
332
337
|
return;
|
|
333
338
|
}
|
|
334
339
|
if (cmd === "price") {
|
|
@@ -456,6 +461,58 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
456
461
|
notify(T.error("No pending approval."));
|
|
457
462
|
return;
|
|
458
463
|
}
|
|
464
|
+
if (cmd === "gmgn" || cmd === "gmgnhelp") {
|
|
465
|
+
const gmgnArgs = rest.join(" ");
|
|
466
|
+
if (!gmgnArgs || gmgnArgs === "help" || gmgnArgs === "--help") {
|
|
467
|
+
notify(gmgnHelp());
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (gmgnArgs === "status") {
|
|
471
|
+
notify(gmgnStatus());
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
if (gmgnArgs.startsWith("setup") || gmgnArgs.startsWith("key")) {
|
|
475
|
+
notify("GMGN API key setup. Switch to CLI and run: aiaiaichain gmgn setup");
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
// Forward to gmgn tool
|
|
479
|
+
const result = await gmgnTool("", { subcommand: gmgnArgs.split(" ")[0], chain: "sol", ...Object.fromEntries(gmgnArgs.split(" ").slice(1).reduce((acc, val, i) => { if (i % 2 === 0)
|
|
480
|
+
acc.push([val]);
|
|
481
|
+
else
|
|
482
|
+
acc[acc.length - 1].push(val); return acc; }, []).map(([k, v]) => [k, v || true])) });
|
|
483
|
+
notify(result.content[0].text);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (cmd === "gmgnmarket" || cmd === "market") {
|
|
487
|
+
const query = rest.join(" ");
|
|
488
|
+
const result = await gmgnMarketTool("", { query: query || "trending", chain: "sol" });
|
|
489
|
+
notify(result.content[0].text);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
if (cmd === "update" || cmd === "upgrade") {
|
|
493
|
+
notify([
|
|
494
|
+
"🤖 AIAIAI Update",
|
|
495
|
+
"",
|
|
496
|
+
"Switch to CLI to check for updates:",
|
|
497
|
+
" aiaiai update",
|
|
498
|
+
"",
|
|
499
|
+
"This will check npm for the latest version",
|
|
500
|
+
"and install it if available.",
|
|
501
|
+
].join("\n"));
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if (cmd === "keys" || cmd === "providers") {
|
|
505
|
+
notify([
|
|
506
|
+
"🔑 AI Model Providers",
|
|
507
|
+
"",
|
|
508
|
+
"Switch to CLI to manage keys:", " aiaiaichain keys",
|
|
509
|
+
"",
|
|
510
|
+
"Or use /models to see available models.",
|
|
511
|
+
`\nProviders: ${modelRegistry.getProviderCount()} configured`,
|
|
512
|
+
`Models: ${modelRegistry.modelCount} available`,
|
|
513
|
+
].join("\n"));
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
459
516
|
if (cmd === "memory" && args.trim()) {
|
|
460
517
|
const results = memoryStore.search(args.trim(), 8);
|
|
461
518
|
if (results.length === 0) {
|
|
@@ -529,15 +586,16 @@ export function App({ registry, systemPrompt, chain: initialChain = "solana", mo
|
|
|
529
586
|
const statusLine = [costBadge, newsBadge, ...Object.values(statusBadges)].filter(Boolean).join(" ") || null;
|
|
530
587
|
const changeColor = (pct) => pct > 0 ? AIAIAI_COLORS.success : pct < 0 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
531
588
|
const SIDEBAR_W = 44;
|
|
589
|
+
const SIDEBAR_MIN = 38;
|
|
532
590
|
const actionIcon = (type) => type === "buy" ? "↗" : type === "burn" ? "🔥" : type === "deposit" ? "↓" : "→";
|
|
533
591
|
if (showModelSelector) {
|
|
534
592
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { model: modelName, chain: chain, price: priceBadge, toolRunning: toolRunning, connected: true, statusLine: statusLine }), _jsx(ModelSelector, { models: modelList, currentModelId: process.env.DEFAULT_MODEL ?? "", onSelect: handleModelSelect, onCancel: handleModelCancel, initialQuery: modelSelectorInitialQuery })] }));
|
|
535
593
|
}
|
|
536
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { model: modelName, chain: chain, price: priceBadge, toolRunning: toolRunning, connected: true, statusLine: statusLine }), _jsxs(Box, { flexDirection: "row", flexGrow: 1, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", width: SIDEBAR_W, flexShrink: 0, children: [_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.accent, paddingX: 1, children: [_jsx(Text, { bold: true, color: AIAIAI_COLORS.accent, children: "\uD83E\uDD16 $AIAIAI" }), aiaiPrice ? (_jsxs(_Fragment, { children: [_jsxs(Text, { color: changeColor(aiaiPrice.change), children: [aiaiPrice.priceUsd ? `$${parseFloat(aiaiPrice.priceUsd).toFixed(8)}` : "N/A", " ", aiaiPrice.change > 0 ? "▲" : aiaiPrice.change < 0 ? "▼" : "─", Math.abs(aiaiPrice.change).toFixed(1), "%"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["MCap: ", aiaiPrice.mcap ? `$${(aiaiPrice.mcap / 1000).toFixed(1)}k` : "N/A"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Liq: $", (aiaiPrice.liq / 1000).toFixed(1), "k"] })] })) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCBC Wallets" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Cold: ", coldBalance.sol.toFixed(2), " SOL"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" AIAIAI: ", coldBalance.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Action: ", actionBalance.sol.toFixed(2), " SOL"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" AIAIAI: ", actionBalance.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Deposit: ", depositBalance.usdc.toFixed(2), " USDC"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" SOL: ", depositBalance.sol.toFixed(2)] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\u26A1 Actions" }), recentActions.length > 0 ? recentActions.map((a, i) => {
|
|
594
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { model: modelName, chain: chain, price: priceBadge, toolRunning: toolRunning, connected: true, statusLine: statusLine }), _jsxs(Box, { flexDirection: "row", flexGrow: 1, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", width: SIDEBAR_W, minWidth: SIDEBAR_MIN, flexShrink: 0, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.accent, paddingX: 1, children: [_jsx(Text, { bold: true, color: AIAIAI_COLORS.accent, children: "\uD83E\uDD16 $AIAIAI" }), aiaiPrice ? (_jsxs(_Fragment, { children: [_jsxs(Text, { color: changeColor(aiaiPrice.change), children: [aiaiPrice.priceUsd ? `$${parseFloat(aiaiPrice.priceUsd).toFixed(8)}` : "N/A", " ", aiaiPrice.change > 0 ? "▲" : aiaiPrice.change < 0 ? "▼" : "─", Math.abs(aiaiPrice.change).toFixed(1), "%"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["MCap: ", aiaiPrice.mcap ? `$${(aiaiPrice.mcap / 1000).toFixed(1)}k` : "N/A"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Liq: $", (aiaiPrice.liq / 1000).toFixed(1), "k"] })] })) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Loading\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCBC Wallets" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Cold: ", coldBalance.sol.toFixed(2), " SOL"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" AIAIAI: ", coldBalance.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Action: ", actionBalance.sol.toFixed(2), " SOL"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" AIAIAI: ", actionBalance.aiaiai.toLocaleString(undefined, { maximumFractionDigits: 0 })] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Deposit: ", depositBalance.usdc.toFixed(2), " USDC"] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: [" SOL: ", depositBalance.sol.toFixed(2)] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\u26A1 Actions" }), recentActions.length > 0 ? recentActions.map((a, i) => {
|
|
537
595
|
const col = a.type === "buy" ? AIAIAI_COLORS.success : a.type === "burn" ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
538
596
|
const time = new Date(a.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
539
597
|
return _jsxs(Text, { color: col, children: [actionIcon(a.type), " ", time, " ", a.type, " ", a.amount.toLocaleString()] }, i);
|
|
540
|
-
}) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Waiting for actions\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCB5 Fees" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Buy:
|
|
598
|
+
}) : _jsx(Text, { color: AIAIAI_COLORS.muted, children: "Waiting for actions\u2026" })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCB5 Fees (0.5%)" }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Buy: $", fees.buyFees.toFixed(4)] }), _jsxs(Text, { color: AIAIAI_COLORS.muted, children: ["Burn: $", fees.burnFees.toFixed(4)] }), _jsxs(Text, { color: AIAIAI_COLORS.accent, bold: true, children: ["Total: $", fees.total.toFixed(4)] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: AIAIAI_COLORS.dim, paddingX: 1, marginTop: 1, children: [_jsx(Text, { bold: true, children: "\uD83D\uDCF0 News" }), sidebarNews.length > 0 ? sidebarNews.map((n, i) => {
|
|
541
599
|
const sentColor = n.sentiment > 0.3 ? AIAIAI_COLORS.success : n.sentiment < -0.3 ? AIAIAI_COLORS.error : AIAIAI_COLORS.muted;
|
|
542
600
|
const title = n.title.length > 30 ? n.title.slice(0, 28) + "…" : n.title;
|
|
543
601
|
return _jsx(Text, { color: sentColor, children: title }, i);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ActionFeed — reads agent wallet activity via public RPC.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Only tracks AIAIAI token transfers. Classifies as buy or burn.
|
|
4
|
+
* Fees are calculated as a percentage of each action.
|
|
5
5
|
*/
|
|
6
6
|
import type { ToolResult } from "../api/ExtensionAPI.js";
|
|
7
|
-
export type ActionType = "buy" | "burn"
|
|
7
|
+
export type ActionType = "buy" | "burn";
|
|
8
8
|
export interface AgentAction {
|
|
9
9
|
id: string;
|
|
10
10
|
type: ActionType;
|
|
@@ -13,13 +13,10 @@ export interface AgentAction {
|
|
|
13
13
|
usdValue: number;
|
|
14
14
|
timestamp: number;
|
|
15
15
|
signature: string;
|
|
16
|
-
from: string;
|
|
17
|
-
to: string;
|
|
18
16
|
}
|
|
19
17
|
export interface FeeTracker {
|
|
20
18
|
buyFees: number;
|
|
21
19
|
burnFees: number;
|
|
22
|
-
sellFees: number;
|
|
23
20
|
total: number;
|
|
24
21
|
}
|
|
25
22
|
export declare class ActionFeed {
|
|
@@ -28,12 +25,9 @@ export declare class ActionFeed {
|
|
|
28
25
|
private lastFetch;
|
|
29
26
|
private cacheDuration;
|
|
30
27
|
private fees;
|
|
31
|
-
private
|
|
32
|
-
private lastActionBalance;
|
|
28
|
+
private _price;
|
|
33
29
|
refresh(): Promise<void>;
|
|
34
30
|
private processTransaction;
|
|
35
|
-
private getPrice;
|
|
36
|
-
private _price;
|
|
37
31
|
setPrice(p: number): void;
|
|
38
32
|
getActions(): AgentAction[];
|
|
39
33
|
getRecentActions(limit?: number): AgentAction[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ActionFeed — reads agent wallet activity via public RPC.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Only tracks AIAIAI token transfers. Classifies as buy or burn.
|
|
4
|
+
* Fees are calculated as a percentage of each action.
|
|
5
5
|
*/
|
|
6
6
|
import { Type } from "@sinclair/typebox";
|
|
7
7
|
import { ACTION_WALLET, AIAIAI_MINT } from "./AgentWallet.js";
|
|
@@ -24,22 +24,19 @@ export class ActionFeed {
|
|
|
24
24
|
lastSignature = "";
|
|
25
25
|
lastFetch = 0;
|
|
26
26
|
cacheDuration = 60_000;
|
|
27
|
-
fees = { buyFees: 0, burnFees: 0,
|
|
28
|
-
|
|
29
|
-
lastActionBalance = 0;
|
|
27
|
+
fees = { buyFees: 0, burnFees: 0, total: 0 };
|
|
28
|
+
_price = 0.0004;
|
|
30
29
|
async refresh() {
|
|
31
30
|
if (Date.now() - this.lastFetch < this.cacheDuration)
|
|
32
31
|
return;
|
|
33
32
|
this.lastFetch = Date.now();
|
|
34
33
|
try {
|
|
35
|
-
// Get recent signatures for the action wallet
|
|
36
34
|
const signatures = await rpcCall("getSignaturesForAddress", [
|
|
37
35
|
ACTION_WALLET,
|
|
38
|
-
{ limit:
|
|
36
|
+
{ limit: 30 },
|
|
39
37
|
]);
|
|
40
38
|
if (!signatures || signatures.length === 0)
|
|
41
39
|
return;
|
|
42
|
-
// Process new signatures
|
|
43
40
|
for (const sigInfo of signatures) {
|
|
44
41
|
if (sigInfo.signature === this.lastSignature)
|
|
45
42
|
break;
|
|
@@ -61,55 +58,54 @@ export class ActionFeed {
|
|
|
61
58
|
return;
|
|
62
59
|
const preBalances = tx.meta.preTokenBalances ?? [];
|
|
63
60
|
const postBalances = tx.meta.postTokenBalances ?? [];
|
|
64
|
-
//
|
|
61
|
+
// Find AIAIAI balance changes for the action wallet
|
|
65
62
|
for (const post of postBalances) {
|
|
66
|
-
if (post.mint
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
type,
|
|
90
|
-
amount: Math.abs(diff),
|
|
91
|
-
token: "AIAIAI",
|
|
92
|
-
usdValue: Math.abs(diff) * (this.getPrice()),
|
|
93
|
-
timestamp: (tx.blockTime ?? 0) * 1000,
|
|
94
|
-
signature,
|
|
95
|
-
from: "",
|
|
96
|
-
to: "",
|
|
97
|
-
};
|
|
98
|
-
this.actions.unshift(action);
|
|
63
|
+
if (post.mint !== AIAIAI_MINT)
|
|
64
|
+
continue;
|
|
65
|
+
if (post.owner !== ACTION_WALLET)
|
|
66
|
+
continue;
|
|
67
|
+
const pre = preBalances.find((p) => p.accountIndex === post.accountIndex && p.mint === AIAIAI_MINT);
|
|
68
|
+
const preAmt = pre ? parseFloat(pre.uiTokenAmount?.uiAmount ?? "0") : 0;
|
|
69
|
+
const postAmt = parseFloat(post.uiTokenAmount?.uiAmount ?? "0");
|
|
70
|
+
const diff = postAmt - preAmt;
|
|
71
|
+
// Skip negligible changes
|
|
72
|
+
if (Math.abs(diff) < 0.000001)
|
|
73
|
+
continue;
|
|
74
|
+
// Positive diff = tokens came IN = BUY
|
|
75
|
+
// Negative diff = tokens went OUT = BURN
|
|
76
|
+
const type = diff > 0 ? "buy" : "burn";
|
|
77
|
+
const amount = Math.abs(diff);
|
|
78
|
+
const usdValue = amount * this._price;
|
|
79
|
+
// Fee: 0.5% of action value
|
|
80
|
+
const fee = usdValue * 0.005;
|
|
81
|
+
if (type === "buy") {
|
|
82
|
+
this.fees.buyFees += fee;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
this.fees.burnFees += fee;
|
|
99
86
|
}
|
|
87
|
+
this.fees.total = this.fees.buyFees + this.fees.burnFees;
|
|
88
|
+
const action = {
|
|
89
|
+
id: signature.slice(0, 8),
|
|
90
|
+
type,
|
|
91
|
+
amount,
|
|
92
|
+
token: "AIAIAI",
|
|
93
|
+
usdValue,
|
|
94
|
+
timestamp: (tx.blockTime ?? Math.floor(Date.now() / 1000)) * 1000,
|
|
95
|
+
signature,
|
|
96
|
+
};
|
|
97
|
+
this.actions.unshift(action);
|
|
100
98
|
}
|
|
101
|
-
// Keep only last 50
|
|
99
|
+
// Keep only last 50
|
|
102
100
|
if (this.actions.length > 50)
|
|
103
101
|
this.actions = this.actions.slice(0, 50);
|
|
104
102
|
}
|
|
105
103
|
catch { /* skip this tx */ }
|
|
106
104
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
setPrice(p) {
|
|
106
|
+
if (p > 0)
|
|
107
|
+
this._price = p;
|
|
110
108
|
}
|
|
111
|
-
_price = 0.0004;
|
|
112
|
-
setPrice(p) { this._price = p; }
|
|
113
109
|
getActions() {
|
|
114
110
|
return [...this.actions];
|
|
115
111
|
}
|
|
@@ -117,7 +113,7 @@ export class ActionFeed {
|
|
|
117
113
|
return this.actions.slice(0, limit);
|
|
118
114
|
}
|
|
119
115
|
getFees() {
|
|
120
|
-
return { ...this.fees
|
|
116
|
+
return { ...this.fees };
|
|
121
117
|
}
|
|
122
118
|
getActionSummary() {
|
|
123
119
|
const buys = this.actions.filter(a => a.type === "buy");
|
|
@@ -138,12 +134,22 @@ export class ActionFeed {
|
|
|
138
134
|
const limit = params.limit || 10;
|
|
139
135
|
const actions = this.getRecentActions(limit);
|
|
140
136
|
if (actions.length === 0) {
|
|
141
|
-
return {
|
|
137
|
+
return {
|
|
138
|
+
content: [{
|
|
139
|
+
type: "text",
|
|
140
|
+
text: [
|
|
141
|
+
"No agent buy/burn actions detected yet.",
|
|
142
|
+
"",
|
|
143
|
+
`Action wallet: ${ACTION_WALLET}`,
|
|
144
|
+
"The agent buys and burns $AIAIAI. Actions appear here when detected on-chain.",
|
|
145
|
+
].join("\n"),
|
|
146
|
+
}],
|
|
147
|
+
};
|
|
142
148
|
}
|
|
143
149
|
const lines = actions.map(a => {
|
|
144
|
-
const icon = a.type === "buy" ? "↗" :
|
|
150
|
+
const icon = a.type === "buy" ? "↗" : "🔥";
|
|
145
151
|
const time = new Date(a.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
146
|
-
return ` ${icon} ${time}
|
|
152
|
+
return ` ${icon} ${time} ${a.type.toUpperCase().padEnd(6)} ${a.amount.toLocaleString()} $AIAIAI ($${a.usdValue.toFixed(2)})`;
|
|
147
153
|
});
|
|
148
154
|
const summary = this.getActionSummary();
|
|
149
155
|
return {
|
|
@@ -151,7 +157,8 @@ export class ActionFeed {
|
|
|
151
157
|
type: "text",
|
|
152
158
|
text: [
|
|
153
159
|
`Agent Actions (last ${actions.length})`,
|
|
154
|
-
`Buys: ${summary.buys} (${summary.totalBought.toLocaleString()}
|
|
160
|
+
` Buys: ${summary.buys} (${summary.totalBought.toLocaleString()} $AIAIAI)`,
|
|
161
|
+
` Burns: ${summary.burns} (${summary.totalBurned.toLocaleString()} $AIAIAI)`,
|
|
155
162
|
``,
|
|
156
163
|
...lines,
|
|
157
164
|
].join("\n"),
|
|
@@ -164,10 +171,10 @@ export class ActionFeed {
|
|
|
164
171
|
content: [{
|
|
165
172
|
type: "text",
|
|
166
173
|
text: [
|
|
167
|
-
`Agent Fees`,
|
|
174
|
+
`Agent Fees (0.5% per action)`,
|
|
168
175
|
` Buy fees: $${f.buyFees.toFixed(4)}`,
|
|
169
176
|
` Burn fees: $${f.burnFees.toFixed(4)}`,
|
|
170
|
-
`
|
|
177
|
+
` ─────────────────────`,
|
|
171
178
|
` Total: $${f.total.toFixed(4)}`,
|
|
172
179
|
].join("\n"),
|
|
173
180
|
}],
|