@aiaiaichain/agent 0.1.4 → 0.1.6
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 -9
- package/dist/core/ChainConfig.js +1 -1
- package/dist/core/SystemMonitor.d.ts +1 -4
- package/dist/core/SystemMonitor.js +20 -46
- package/dist/index.d.ts +10 -0
- package/dist/index.js +11 -0
- package/dist/models/ModelRegistry.js +12 -4
- package/dist/runner/AgentRunner.d.ts +2 -0
- package/dist/runner/AgentRunner.js +18 -1
- package/dist/runner/ModelClient.js +109 -48
- package/dist/session/SessionManager.d.ts +1 -0
- package/dist/session/SessionManager.js +8 -2
- package/dist/session/SessionStore.js +8 -3
- package/dist/tools/CrossTools.js +13 -5
- package/dist/tools/MarketSentiment.js +22 -13
- package/dist/tools/NewsSentiment.js +9 -3
- package/dist/tools/PriceFeed.js +11 -4
- package/dist/tools/TechnicalAnalysis.js +2 -1
- package/dist/tools/TokenCalendar.d.ts +24 -0
- package/dist/tools/TokenCalendar.js +81 -0
- package/dist/tools/TokenSecurityScanner.d.ts +22 -0
- package/dist/tools/TokenSecurityScanner.js +102 -0
- package/dist/tools/TransactionSim.d.ts +17 -0
- package/dist/tools/TransactionSim.js +78 -0
- package/dist/tui/App.js +145 -23
- package/dist/tui/REPL.js +2 -2
- package/dist/tui/Sparkline.d.ts +21 -0
- package/dist/tui/Sparkline.js +44 -0
- package/dist/tui/ThemePresets.d.ts +25 -0
- package/dist/tui/ThemePresets.js +117 -0
- package/dist/util/clipboard.d.ts +9 -0
- package/dist/util/clipboard.js +26 -0
- package/dist/util/commandSuggest.d.ts +7 -0
- package/dist/util/commandSuggest.js +44 -0
- package/dist/util/confirmation.d.ts +6 -0
- package/dist/util/confirmation.js +16 -0
- package/dist/util/errorHandler.d.ts +3 -0
- package/dist/util/errorHandler.js +28 -0
- package/dist/util/logger.d.ts +11 -0
- package/dist/util/logger.js +43 -0
- package/dist/util/processManager.d.ts +5 -0
- package/dist/util/processManager.js +39 -0
- package/dist/util/resilientFetch.d.ts +21 -0
- package/dist/util/resilientFetch.js +94 -0
- package/dist/util/responseCache.d.ts +27 -0
- package/dist/util/responseCache.js +54 -0
- package/dist/util/safeLog.d.ts +4 -5
- package/dist/util/safeLog.js +68 -30
- package/dist/util/scheduler.d.ts +14 -0
- package/dist/util/scheduler.js +75 -0
- package/dist/util/webhooks.d.ts +9 -0
- package/dist/util/webhooks.js +75 -0
- package/dist/wallet/ActionFeed.d.ts +3 -2
- package/dist/wallet/ActionFeed.js +97 -76
- package/dist/wallet/AgentWallet.d.ts +6 -2
- package/dist/wallet/AgentWallet.js +59 -27
- package/dist/wallet/ProfitTracker.d.ts +30 -0
- package/dist/wallet/ProfitTracker.js +93 -0
- package/package.json +2 -2
package/dist/util/safeLog.js
CHANGED
|
@@ -1,38 +1,76 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* safeLog —
|
|
2
|
+
* src/util/safeLog.ts — Intercepts console output, masks secrets, routes to file
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
import { existsSync, appendFileSync, mkdirSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
const AIAIAI_HOME = process.env.AIAIAI_HOME ?? join(homedir(), '.aiaiai');
|
|
8
|
+
const LOG_DIR = join(AIAIAI_HOME, '.logs');
|
|
9
|
+
const LOG_FILE = join(LOG_DIR, 'agent.log');
|
|
10
|
+
// Patterns that indicate sensitive values
|
|
11
|
+
const SENSITIVE_PATTERNS = [
|
|
12
|
+
/sk-[a-zA-Z0-9]{20,}/g, // API keys
|
|
13
|
+
/[A-Za-z0-9]{32,}/g, // Hex keys
|
|
14
|
+
/Bearer\s+[A-Za-z0-9\-._~+/]+/g, // Bearer tokens
|
|
15
|
+
/api[_-]?key["\s:=]+["']?[^\s"']+/gi, // Key assignments
|
|
16
|
+
/secret["\s:=]+["']?[^\s"']+/gi, // Secrets
|
|
17
|
+
];
|
|
18
|
+
const SENSITIVE_ENV_NAMES = [
|
|
19
|
+
'OPENROUTER_API_KEY', 'ANTHROPIC_API_KEY', 'CRYPTOPANIC_API_KEY',
|
|
20
|
+
'GMGN_API_KEY', 'SOLANA_RPC_URL', 'HELIUS_API_KEY', 'PRIVY_KEY',
|
|
21
|
+
];
|
|
22
|
+
const REDACTED = '[REDACTED]';
|
|
23
|
+
function maskSensitive(text) {
|
|
24
|
+
let masked = text;
|
|
25
|
+
for (const pattern of SENSITIVE_PATTERNS) {
|
|
26
|
+
masked = masked.replace(pattern, (match) => {
|
|
27
|
+
// Keep first 4 chars for identification
|
|
28
|
+
if (match.length > 8)
|
|
29
|
+
return match.slice(0, 4) + '…' + REDACTED;
|
|
30
|
+
return REDACTED;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return masked;
|
|
34
|
+
}
|
|
35
|
+
function ensureLogDir() {
|
|
36
|
+
if (!existsSync(LOG_DIR)) {
|
|
37
|
+
mkdirSync(LOG_DIR, { recursive: true, mode: 0o700 });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function writeToFile(text) {
|
|
41
|
+
try {
|
|
42
|
+
ensureLogDir();
|
|
43
|
+
appendFileSync(LOG_FILE, text + '\n', { encoding: 'utf-8' });
|
|
14
44
|
}
|
|
45
|
+
catch { /* non-fatal — prevents logging failures from crashing agent */ }
|
|
15
46
|
}
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
|
|
47
|
+
export function wireNotify(fn) {
|
|
48
|
+
// Wire to notification callback if provided
|
|
49
|
+
if (fn) {
|
|
50
|
+
// placeholder for notification integration
|
|
20
51
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
process.stderr.write(m + "\n");
|
|
30
|
-
queue.length = 0;
|
|
31
|
-
}
|
|
32
|
-
scheduled = false;
|
|
33
|
-
});
|
|
34
|
-
}
|
|
52
|
+
}
|
|
53
|
+
export function safeLog(...args) {
|
|
54
|
+
const raw = args.map(a => typeof a === 'string' ? a : String(a)).join(' ');
|
|
55
|
+
const masked = maskSensitive(raw);
|
|
56
|
+
const timestamp = new Date().toISOString();
|
|
57
|
+
const line = `[${timestamp}] ${masked}`;
|
|
58
|
+
try {
|
|
59
|
+
writeToFile(line);
|
|
35
60
|
}
|
|
61
|
+
catch { /* non-fatal */ }
|
|
62
|
+
process.stdout.write(line + '\n');
|
|
63
|
+
}
|
|
64
|
+
// Override console methods
|
|
65
|
+
export function installSafeLogging() {
|
|
66
|
+
console.log = (...args) => safeLog(...args);
|
|
67
|
+
console.warn = (...args) => {
|
|
68
|
+
const masked = maskSensitive(args.map(String).join(' '));
|
|
69
|
+
process.stderr.write(`[WARN] ${masked}\n`);
|
|
70
|
+
};
|
|
71
|
+
console.error = (...args) => {
|
|
72
|
+
const masked = maskSensitive(args.map(String).join(' '));
|
|
73
|
+
process.stderr.write(`[ERROR] ${masked}\n`);
|
|
74
|
+
};
|
|
36
75
|
}
|
|
37
|
-
export const safeLog = safeLogFn;
|
|
38
76
|
//# sourceMappingURL=safeLog.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/util/scheduler.ts — Centralized interval scheduler with pause/resume
|
|
3
|
+
*/
|
|
4
|
+
export declare function addTask(name: string, intervalMs: number, fn: () => void): void;
|
|
5
|
+
export declare function removeTask(name: string): void;
|
|
6
|
+
export declare function pauseTask(name: string): void;
|
|
7
|
+
export declare function resumeTask(name: string): void;
|
|
8
|
+
export declare function pauseAll(): void;
|
|
9
|
+
export declare function resumeAll(): void;
|
|
10
|
+
/**
|
|
11
|
+
* Start the scheduler loop. Returns a stop function.
|
|
12
|
+
*/
|
|
13
|
+
export declare function startScheduler(): () => void;
|
|
14
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/util/scheduler.ts — Centralized interval scheduler with pause/resume
|
|
3
|
+
*/
|
|
4
|
+
const MAX_TASKS = 50;
|
|
5
|
+
const tasks = [];
|
|
6
|
+
let running = false;
|
|
7
|
+
export function addTask(name, intervalMs, fn) {
|
|
8
|
+
// Replace existing task with same name
|
|
9
|
+
const existing = tasks.findIndex(t => t.name === name);
|
|
10
|
+
if (existing >= 0) {
|
|
11
|
+
tasks[existing] = { name, intervalMs, fn, lastRun: 0, paused: false };
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
if (tasks.length >= MAX_TASKS) {
|
|
15
|
+
// Evict oldest non-paused task
|
|
16
|
+
const evictIdx = tasks.findIndex(t => !t.paused);
|
|
17
|
+
if (evictIdx >= 0)
|
|
18
|
+
tasks.splice(evictIdx, 1);
|
|
19
|
+
else
|
|
20
|
+
return; // All tasks paused, refuse to add
|
|
21
|
+
}
|
|
22
|
+
tasks.push({ name, intervalMs, fn, lastRun: 0, paused: false });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function removeTask(name) {
|
|
26
|
+
const idx = tasks.findIndex(t => t.name === name);
|
|
27
|
+
if (idx >= 0)
|
|
28
|
+
tasks.splice(idx, 1);
|
|
29
|
+
}
|
|
30
|
+
export function pauseTask(name) {
|
|
31
|
+
const task = tasks.find(t => t.name === name);
|
|
32
|
+
if (task)
|
|
33
|
+
task.paused = true;
|
|
34
|
+
}
|
|
35
|
+
export function resumeTask(name) {
|
|
36
|
+
const task = tasks.find(t => t.name === name);
|
|
37
|
+
if (task)
|
|
38
|
+
task.paused = false;
|
|
39
|
+
}
|
|
40
|
+
export function pauseAll() {
|
|
41
|
+
tasks.forEach(t => (t.paused = true));
|
|
42
|
+
}
|
|
43
|
+
export function resumeAll() {
|
|
44
|
+
tasks.forEach(t => (t.paused = false));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Start the scheduler loop. Returns a stop function.
|
|
48
|
+
*/
|
|
49
|
+
export function startScheduler() {
|
|
50
|
+
if (running)
|
|
51
|
+
return () => { };
|
|
52
|
+
running = true;
|
|
53
|
+
const tick = () => {
|
|
54
|
+
if (!running)
|
|
55
|
+
return;
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
for (const task of tasks) {
|
|
58
|
+
if (task.paused)
|
|
59
|
+
continue;
|
|
60
|
+
if (now - task.lastRun >= task.intervalMs) {
|
|
61
|
+
task.lastRun = now;
|
|
62
|
+
try {
|
|
63
|
+
task.fn();
|
|
64
|
+
}
|
|
65
|
+
catch { /* task errors don't crash scheduler */ }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
setTimeout(tick, 1000);
|
|
69
|
+
};
|
|
70
|
+
tick();
|
|
71
|
+
return () => {
|
|
72
|
+
running = false;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/util/webhooks.ts — Discord & Telegram notification webhooks.
|
|
3
|
+
* Configure via ~/.aiaiai/.env with WEBHOOK_DISCORD and/or WEBHOOK_TELEGRAM.
|
|
4
|
+
*/
|
|
5
|
+
export declare function sendDiscord(message: string): Promise<boolean>;
|
|
6
|
+
export declare function sendTelegram(message: string): Promise<boolean>;
|
|
7
|
+
export declare function notify(message: string): Promise<void>;
|
|
8
|
+
export declare function hasWebhooks(): boolean;
|
|
9
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/util/webhooks.ts — Discord & Telegram notification webhooks.
|
|
3
|
+
* Configure via ~/.aiaiai/.env with WEBHOOK_DISCORD and/or WEBHOOK_TELEGRAM.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { homedir } from "node:os";
|
|
8
|
+
import { logger } from "./logger.js";
|
|
9
|
+
const AIAIAI_HOME = process.env.AIAIAI_HOME ?? join(homedir(), ".aiaiai");
|
|
10
|
+
function getWebhookUrl(service) {
|
|
11
|
+
const envFile = join(AIAIAI_HOME, ".env");
|
|
12
|
+
if (!existsSync(envFile))
|
|
13
|
+
return null;
|
|
14
|
+
const content = readFileSync(envFile, "utf-8");
|
|
15
|
+
const key = service === "discord" ? "WEBHOOK_DISCORD" : "WEBHOOK_TELEGRAM";
|
|
16
|
+
const match = new RegExp(`^${key}=(.+)$`, "m").exec(content);
|
|
17
|
+
return match?.[1] ?? null;
|
|
18
|
+
}
|
|
19
|
+
export async function sendDiscord(message) {
|
|
20
|
+
const url = getWebhookUrl("discord");
|
|
21
|
+
if (!url)
|
|
22
|
+
return false;
|
|
23
|
+
try {
|
|
24
|
+
const res = await fetch(url, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: { "Content-Type": "application/json" },
|
|
27
|
+
body: JSON.stringify({ content: `🤖 **AIAIAI Agent**\n${message}` }),
|
|
28
|
+
});
|
|
29
|
+
return res.ok;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
logger.warn("Webhooks", "Discord send failed", { error: error.message });
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function sendTelegram(message) {
|
|
37
|
+
const url = getWebhookUrl("telegram");
|
|
38
|
+
if (!url)
|
|
39
|
+
return false;
|
|
40
|
+
// Telegram Bot API format: https://api.telegram.org/bot<TOKEN>/sendMessage
|
|
41
|
+
try {
|
|
42
|
+
const res = await fetch(url, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: { "Content-Type": "application/json" },
|
|
45
|
+
body: JSON.stringify({
|
|
46
|
+
chat_id: extractTelegramChatId(url),
|
|
47
|
+
text: `🤖 AIAIAI Agent\n${message}`,
|
|
48
|
+
parse_mode: "HTML",
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
return res.ok;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
logger.warn("Webhooks", "Telegram send failed", { error: error.message });
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function extractTelegramChatId(url) {
|
|
59
|
+
// URL format: https://api.telegram.org/bot<TOKEN>/sendMessage?chat_id=<ID>
|
|
60
|
+
const match = /chat_id=([^&]+)/.exec(url);
|
|
61
|
+
return match?.[1] ?? "";
|
|
62
|
+
}
|
|
63
|
+
export async function notify(message) {
|
|
64
|
+
const [discordOk, telegramOk] = await Promise.all([
|
|
65
|
+
sendDiscord(message),
|
|
66
|
+
sendTelegram(message),
|
|
67
|
+
]);
|
|
68
|
+
if (!discordOk && !telegramOk) {
|
|
69
|
+
logger.debug("Webhooks", "No webhooks configured or all failed");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export function hasWebhooks() {
|
|
73
|
+
return !!(getWebhookUrl("discord") || getWebhookUrl("telegram"));
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ActionFeed — reads agent wallet activity via public RPC.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Parses Pump.fun Buy + BurnChecked events from inner instructions.
|
|
4
|
+
* Checks the ACTION wallet (GmFrDZT...) for transaction signatures.
|
|
5
|
+
* Fees are calculated as 0.5% of each action's USD value.
|
|
5
6
|
*/
|
|
6
7
|
import type { ToolResult } from "../api/ExtensionAPI.js";
|
|
7
8
|
export type ActionType = "buy" | "burn";
|
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ActionFeed — reads agent wallet activity via public RPC.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Parses Pump.fun Buy + BurnChecked events from inner instructions.
|
|
4
|
+
* Checks the ACTION wallet (GmFrDZT...) for transaction signatures.
|
|
5
|
+
* Fees are calculated as 0.5% of each action's USD value.
|
|
5
6
|
*/
|
|
6
7
|
import { Type } from "@sinclair/typebox";
|
|
7
8
|
import { ACTION_WALLET, AIAIAI_MINT } from "./AgentWallet.js";
|
|
9
|
+
import { resilientFetch } from "../util/resilientFetch.js";
|
|
10
|
+
import { logger } from "../util/logger.js";
|
|
8
11
|
function getRpcUrl() {
|
|
9
12
|
return process.env.SOLANA_RPC_URL || "https://api.mainnet-beta.solana.com";
|
|
10
13
|
}
|
|
11
14
|
async function rpcCall(method, params = []) {
|
|
12
|
-
const response = await
|
|
15
|
+
const response = await resilientFetch(getRpcUrl(), {
|
|
16
|
+
timeout: 15_000,
|
|
17
|
+
retries: 2,
|
|
13
18
|
method: "POST",
|
|
14
19
|
headers: { "Content-Type": "application/json" },
|
|
15
20
|
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
16
21
|
});
|
|
17
22
|
const data = await response.json();
|
|
18
23
|
if (data.error)
|
|
19
|
-
throw new Error(data.error.message);
|
|
24
|
+
throw new Error(data.error.message || 'Unknown RPC error');
|
|
20
25
|
return data.result;
|
|
21
26
|
}
|
|
22
27
|
export class ActionFeed {
|
|
@@ -46,7 +51,9 @@ export class ActionFeed {
|
|
|
46
51
|
this.lastSignature = signatures[0].signature;
|
|
47
52
|
}
|
|
48
53
|
}
|
|
49
|
-
catch {
|
|
54
|
+
catch (error) {
|
|
55
|
+
logger.debug('ActionFeed', 'refresh failed', { error: error.message });
|
|
56
|
+
}
|
|
50
57
|
}
|
|
51
58
|
async processTransaction(signature) {
|
|
52
59
|
try {
|
|
@@ -56,71 +63,91 @@ export class ActionFeed {
|
|
|
56
63
|
]);
|
|
57
64
|
if (!tx || !tx.meta)
|
|
58
65
|
return;
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// Fee: 0.5% of action value
|
|
80
|
-
const fee = usdValue * 0.005;
|
|
81
|
-
if (type === "buy") {
|
|
82
|
-
this.fees.buyFees += fee;
|
|
66
|
+
// Parse inner instructions for Pump.fun Buy + BurnChecked on AIAIAI
|
|
67
|
+
const innerInstructions = tx.meta.innerInstructions ?? [];
|
|
68
|
+
let buyAmount = 0;
|
|
69
|
+
let burnAmount = 0;
|
|
70
|
+
for (const inner of innerInstructions) {
|
|
71
|
+
for (const ix of (inner.instructions ?? [])) {
|
|
72
|
+
// Pump.fun Buy: transferChecked of AIAIAI from pool to hot wallet
|
|
73
|
+
if (ix.parsed?.type === "transferChecked" &&
|
|
74
|
+
ix.parsed.info?.mint === AIAIAI_MINT) {
|
|
75
|
+
const amt = parseFloat(ix.parsed.info?.tokenAmount?.uiAmount ?? "0");
|
|
76
|
+
if (amt > 1)
|
|
77
|
+
buyAmount += amt;
|
|
78
|
+
}
|
|
79
|
+
// Burn: burnChecked of AIAIAI
|
|
80
|
+
if (ix.parsed?.type === "burnChecked" &&
|
|
81
|
+
ix.parsed.info?.mint === AIAIAI_MINT) {
|
|
82
|
+
const amt = parseFloat(ix.parsed.info?.tokenAmount?.uiAmount ?? "0");
|
|
83
|
+
if (amt > 0)
|
|
84
|
+
burnAmount += amt;
|
|
85
|
+
}
|
|
83
86
|
}
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
}
|
|
88
|
+
// Fallback: scan token balance changes for any AIAIAI account
|
|
89
|
+
if (buyAmount === 0 && burnAmount === 0) {
|
|
90
|
+
const preBalances = tx.meta.preTokenBalances ?? [];
|
|
91
|
+
const postBalances = tx.meta.postTokenBalances ?? [];
|
|
92
|
+
for (const post of postBalances) {
|
|
93
|
+
if (post.mint !== AIAIAI_MINT)
|
|
94
|
+
continue;
|
|
95
|
+
const pre = preBalances.find((p) => p.accountIndex === post.accountIndex && p.mint === AIAIAI_MINT);
|
|
96
|
+
const preAmt = pre ? parseFloat(pre.uiTokenAmount?.uiAmount ?? "0") : 0;
|
|
97
|
+
const postAmt = parseFloat(post.uiTokenAmount?.uiAmount ?? "0");
|
|
98
|
+
const diff = postAmt - preAmt;
|
|
99
|
+
if (Math.abs(diff) < 0.000001)
|
|
100
|
+
continue;
|
|
101
|
+
if (diff > 0)
|
|
102
|
+
buyAmount += diff;
|
|
103
|
+
else
|
|
104
|
+
burnAmount += Math.abs(diff);
|
|
86
105
|
}
|
|
106
|
+
}
|
|
107
|
+
// Record
|
|
108
|
+
if (buyAmount > 0) {
|
|
109
|
+
const usdValue = buyAmount * this._price;
|
|
110
|
+
const fee = usdValue * 0.005;
|
|
111
|
+
this.fees.buyFees += fee;
|
|
87
112
|
this.fees.total = this.fees.buyFees + this.fees.burnFees;
|
|
88
|
-
|
|
89
|
-
id: signature.slice(0,
|
|
90
|
-
type,
|
|
91
|
-
amount,
|
|
92
|
-
token: "AIAIAI",
|
|
93
|
-
usdValue,
|
|
113
|
+
this.actions.unshift({
|
|
114
|
+
id: signature.slice(0, 10),
|
|
115
|
+
type: "buy", amount: buyAmount, token: "AIAIAI", usdValue,
|
|
94
116
|
timestamp: (tx.blockTime ?? Math.floor(Date.now() / 1000)) * 1000,
|
|
95
117
|
signature,
|
|
96
|
-
};
|
|
97
|
-
this.actions.unshift(action);
|
|
118
|
+
});
|
|
98
119
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
120
|
+
if (burnAmount > 0) {
|
|
121
|
+
const usdValue = burnAmount * this._price;
|
|
122
|
+
const fee = usdValue * 0.005;
|
|
123
|
+
this.fees.burnFees += fee;
|
|
124
|
+
this.fees.total = this.fees.buyFees + this.fees.burnFees;
|
|
125
|
+
this.actions.unshift({
|
|
126
|
+
id: signature.slice(0, 10) + "-b",
|
|
127
|
+
type: "burn", amount: burnAmount, token: "AIAIAI", usdValue,
|
|
128
|
+
timestamp: (tx.blockTime ?? Math.floor(Date.now() / 1000)) * 1000,
|
|
129
|
+
signature,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
if (this.actions.length > 100)
|
|
133
|
+
this.actions = this.actions.slice(0, 100);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
logger.debug('ActionFeed', 'Failed to parse tx', { error: error.message });
|
|
102
137
|
}
|
|
103
|
-
catch { /* skip this tx */ }
|
|
104
138
|
}
|
|
105
139
|
setPrice(p) {
|
|
106
140
|
if (p > 0)
|
|
107
141
|
this._price = p;
|
|
108
142
|
}
|
|
109
|
-
getActions() {
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
getRecentActions(limit = 10) {
|
|
113
|
-
return this.actions.slice(0, limit);
|
|
114
|
-
}
|
|
115
|
-
getFees() {
|
|
116
|
-
return { ...this.fees };
|
|
117
|
-
}
|
|
143
|
+
getActions() { return [...this.actions]; }
|
|
144
|
+
getRecentActions(limit = 10) { return this.actions.slice(0, limit); }
|
|
145
|
+
getFees() { return { ...this.fees }; }
|
|
118
146
|
getActionSummary() {
|
|
119
147
|
const buys = this.actions.filter(a => a.type === "buy");
|
|
120
148
|
const burns = this.actions.filter(a => a.type === "burn");
|
|
121
149
|
return {
|
|
122
|
-
buys: buys.length,
|
|
123
|
-
burns: burns.length,
|
|
150
|
+
buys: buys.length, burns: burns.length,
|
|
124
151
|
totalBought: buys.reduce((s, a) => s + a.amount, 0),
|
|
125
152
|
totalBurned: burns.reduce((s, a) => s + a.amount, 0),
|
|
126
153
|
};
|
|
@@ -138,32 +165,26 @@ export class ActionFeed {
|
|
|
138
165
|
content: [{
|
|
139
166
|
type: "text",
|
|
140
167
|
text: [
|
|
141
|
-
"No
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
"The agent buys and burns $AIAIAI. Actions appear here when detected on-chain.",
|
|
168
|
+
"No buy/burn actions detected yet.",
|
|
169
|
+
`Action wallet: ${ACTION_WALLET.slice(0, 8)}…${ACTION_WALLET.slice(-6)}`,
|
|
170
|
+
"Actions appear when the agent buys and burns $AIAIAI via Pump.fun.",
|
|
145
171
|
].join("\n"),
|
|
146
172
|
}],
|
|
147
173
|
};
|
|
148
174
|
}
|
|
149
|
-
const lines = actions.map(a => {
|
|
150
|
-
const icon = a.type === "buy" ? "↗" : "🔥";
|
|
151
|
-
const time = new Date(a.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
152
|
-
return ` ${icon} ${time} ${a.type.toUpperCase().padEnd(6)} ${a.amount.toLocaleString()} $AIAIAI ($${a.usdValue.toFixed(2)})`;
|
|
153
|
-
});
|
|
154
175
|
const summary = this.getActionSummary();
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
};
|
|
176
|
+
const lines = [
|
|
177
|
+
`🔄 Recent Actions (${actions.length})`,
|
|
178
|
+
` Buys: ${summary.buys} (${summary.totalBought.toLocaleString()} $AIAIAI)`,
|
|
179
|
+
` Burns: ${summary.burns} (${summary.totalBurned.toLocaleString()} $AIAIAI)`,
|
|
180
|
+
"",
|
|
181
|
+
...actions.map(a => {
|
|
182
|
+
const icon = a.type === "buy" ? "↗" : "🔥";
|
|
183
|
+
const time = new Date(a.timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
184
|
+
return ` ${icon} ${time} ${a.type.toUpperCase().padEnd(4)} ${a.amount.toLocaleString()} $AIAIAI ($${a.usdValue.toFixed(2)})`;
|
|
185
|
+
}),
|
|
186
|
+
];
|
|
187
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
167
188
|
}
|
|
168
189
|
async getFeesTool() {
|
|
169
190
|
const f = this.getFees();
|
|
@@ -171,7 +192,7 @@ export class ActionFeed {
|
|
|
171
192
|
content: [{
|
|
172
193
|
type: "text",
|
|
173
194
|
text: [
|
|
174
|
-
|
|
195
|
+
`💵 Agent Fees (0.5% per action)`,
|
|
175
196
|
` Buy fees: $${f.buyFees.toFixed(4)}`,
|
|
176
197
|
` Burn fees: $${f.burnFees.toFixed(4)}`,
|
|
177
198
|
` ─────────────────────`,
|
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
* Signer: GmFrDZT2cdrqykgTikVdXbe8EtCgzUDM9VsDhQnwsUsG (authority)
|
|
9
9
|
*/
|
|
10
10
|
import type { ToolResult } from "../api/ExtensionAPI.js";
|
|
11
|
-
export declare const
|
|
12
|
-
export declare const
|
|
11
|
+
export declare const ACTION_WALLET = "GmFrDZT2cdrqykgTikVdXbe8EtCgzUDM9VsDhQnwsUsG";
|
|
12
|
+
export declare const HOT_WALLET = "BygDYM1ZXLQNC1HXLhnd1rHZ7E5XjioqT3vPjJFfjnU2";
|
|
13
13
|
export declare const DEPOSIT_WALLET = "FBMDYpG9WXKy4SgxuATQdB2sCyzHsJWPrEr45z3TgL2e";
|
|
14
|
+
export declare const COLD_WALLET = "A11iZoqEt6hU7HyggqC67ee4AtYmaJjwKCvJLerJRV2J";
|
|
14
15
|
export declare const SIGNER = "GmFrDZT2cdrqykgTikVdXbe8EtCgzUDM9VsDhQnwsUsG";
|
|
15
16
|
export declare const AIAIAI_MINT = "AVPJS61gZmWKtaEpb7qYPKo8Fk2xQUsayYQxPiPMpump";
|
|
16
17
|
export declare const AIAIAI_TOKEN = "AVPJS61gZmWKtaEpb7qYPKo8Fk2xQUsayYQxPiPMpump";
|
|
@@ -23,6 +24,7 @@ export interface WalletBalance {
|
|
|
23
24
|
export interface AgentWallets {
|
|
24
25
|
cold: WalletBalance;
|
|
25
26
|
action: WalletBalance;
|
|
27
|
+
hot: WalletBalance;
|
|
26
28
|
deposit: WalletBalance;
|
|
27
29
|
}
|
|
28
30
|
export declare class AgentWallet {
|
|
@@ -33,6 +35,8 @@ export declare class AgentWallet {
|
|
|
33
35
|
getColdWallet(): Promise<WalletBalance>;
|
|
34
36
|
getActionWallet(): Promise<WalletBalance>;
|
|
35
37
|
getDepositWallet(): Promise<WalletBalance>;
|
|
38
|
+
getSolPrice(): Promise<number>;
|
|
39
|
+
getHotWallet(): Promise<WalletBalance>;
|
|
36
40
|
getAgentBalanceParams: import("@sinclair/typebox").TObject<{}>;
|
|
37
41
|
getDepositBalanceParams: import("@sinclair/typebox").TObject<{}>;
|
|
38
42
|
getAgentBalanceTool(): Promise<ToolResult>;
|