-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsmart-wallets.js
More file actions
103 lines (90 loc) · 3.61 KB
/
Copy pathsmart-wallets.js
File metadata and controls
103 lines (90 loc) · 3.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { log } from "./logger.js";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const WALLETS_PATH = path.join(__dirname, "smart-wallets.json");
function loadWallets() {
if (!fs.existsSync(WALLETS_PATH)) return { wallets: [] };
try {
return JSON.parse(fs.readFileSync(WALLETS_PATH, "utf8"));
} catch {
return { wallets: [] };
}
}
function saveWallets(data) {
fs.writeFileSync(WALLETS_PATH, JSON.stringify(data, null, 2));
}
const SOLANA_PUBKEY_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
export function addSmartWallet({ name, address, category = "alpha", type = "lp" }) {
if (!SOLANA_PUBKEY_RE.test(address)) {
return { success: false, error: "Invalid Solana address format" };
}
const data = loadWallets();
const existing = data.wallets.find((w) => w.address === address);
if (existing) {
return { success: false, error: `Already tracked as "${existing.name}"` };
}
data.wallets.push({ name, address, category, type, addedAt: new Date().toISOString() });
saveWallets(data);
log("smart_wallets", `Added wallet: ${name} (${category}, type=${type})`);
return { success: true, wallet: { name, address, category, type } };
}
export function removeSmartWallet({ address }) {
const data = loadWallets();
const wallet = data.wallets.find((w) => w.address === address);
if (!wallet) return { success: false, error: "Wallet not found" };
data.wallets = data.wallets.filter((w) => w.address !== address);
saveWallets(data);
log("smart_wallets", `Removed wallet: ${wallet.name}`);
return { success: true, removed: wallet.name };
}
export function listSmartWallets() {
const { wallets } = loadWallets();
return { total: wallets.length, wallets };
}
// Cache wallet positions for 5 minutes to avoid hammering RPC
const _cache = new Map(); // address -> { positions, fetchedAt }
const CACHE_TTL = 5 * 60 * 1000;
export async function checkSmartWalletsOnPool({ pool_address }) {
const { wallets: allWallets } = loadWallets();
// Only check LP-type wallets — holder wallets don't have positions
const wallets = allWallets.filter((w) => !w.type || w.type === "lp");
if (wallets.length === 0) {
return {
pool: pool_address,
tracked_wallets: 0,
in_pool: [],
confidence_boost: false,
signal: "No smart wallets tracked yet — neutral signal",
};
}
const { getWalletPositions } = await import("./tools/dlmm.js");
const results = await Promise.all(
wallets.map(async (wallet) => {
try {
const cached = _cache.get(wallet.address);
if (cached && Date.now() - cached.fetchedAt < CACHE_TTL) {
return { wallet, positions: cached.positions };
}
const { positions } = await getWalletPositions({ wallet_address: wallet.address });
_cache.set(wallet.address, { positions: positions || [], fetchedAt: Date.now() });
return { wallet, positions: positions || [] };
} catch {
return { wallet, positions: [] };
}
})
);
const inPool = results
.filter((r) => r.positions.some((p) => p.pool === pool_address))
.map((r) => ({ name: r.wallet.name, category: r.wallet.category, address: r.wallet.address }));
return {
pool: pool_address,
tracked_wallets: wallets.length,
in_pool: inPool,
confidence_boost: inPool.length > 0,
signal: inPool.length > 0
? `${inPool.length}/${wallets.length} smart wallet(s) are in this pool: ${inPool.map((w) => w.name).join(", ")} — STRONG signal`
: `0/${wallets.length} smart wallets in this pool — neutral, rely on fundamentals`,
};
}