solana-risk-engine 0.3.2 → 0.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solana-risk-engine",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Explainable risk analysis for Solana tokens and wallets: creator control, real holder concentration, inflated liquidity, impostor tokens, funder clusters and PnL. Zero dependencies, runs in Node and the browser.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/i18n/en.js CHANGED
@@ -37,6 +37,7 @@ export default {
37
37
  CTL_ONLY_PROGRAMS: () => "Only program-controlled authorities (see dependency)",
38
38
  CTL_NONE: () => "No active authorities: nobody can mint more or freeze",
39
39
  CTL_SERIAL_CREATOR: (d) => "The creator launched " + d.n + " other tokens in the last 24 h (per RugCheck): a serial launcher, who usually drops each token as soon as buyers arrive",
40
+ CTL_CREATOR_HISTORY: (d) => "Of the " + d.old + " tokens the creator launched before (over 24 h ago), " + d.dead + " stayed under $10,000 market cap (per RugCheck): their launches rarely take off",
40
41
  IMPOSTOR: (d) => "Impersonates " + d.symbol + ": the real " + d.symbol + " has another address (" + short(d.real) + "). It is a copy, almost certainly a scam",
41
42
 
42
43
  CON_SKIPPED: () => "Holder concentration not checked",
package/src/i18n/es.js CHANGED
@@ -40,6 +40,7 @@ export default {
40
40
  CTL_ONLY_PROGRAMS: () => "Solo hay autoridades controladas por un programa (ver dependencia)",
41
41
  CTL_NONE: () => "Sin autoridades activas: nadie puede crear más ni congelar",
42
42
  CTL_SERIAL_CREATOR: (d) => "El creador ha lanzado " + d.n + " tokens más en las últimas 24 h (según RugCheck): es un lanzador en serie, que suele abandonar cada token en cuanto llegan compradores",
43
+ CTL_CREATOR_HISTORY: (d) => "De los " + d.old + " tokens que el creador lanzó antes (hace más de 24 h), " + d.dead + " se quedaron por debajo de 10.000 $ de capitalización (según RugCheck): sus lanzamientos no suelen despegar",
43
44
  IMPOSTOR: (d) => "Se hace pasar por " + d.symbol + ": el " + d.symbol + " de verdad tiene otra dirección (" + short(d.real) + "). Es una copia, casi seguro una estafa",
44
45
 
45
46
  CON_SKIPPED: () => "Sin comprobar la concentración de holders",
package/src/index.js CHANGED
@@ -45,8 +45,8 @@ export * from "./constants.js";
45
45
  export { isOnCurve, onCurve, b58bytes } from "./curve.js";
46
46
  export { getTokens, getMint, getConc, getAuthKinds, isMint, accountKinds, kindOf } from "./tokens.js";
47
47
  export { getMarkets, realLiq, impostorOf, INFLATED_RATIO, geckoLiquidity, geckoPair } from "./markets.js";
48
- export { rugcheckSummary, rugcheckReport, rugcheckGraph, netBalances, creatorLaunches } from "./rugcheck.js";
49
- export { score, tokenType, buildLights, levelFrom, dataOnly, reasonsFrom, assess, SERIAL_WARN, SERIAL_BAD } from "./risk.js";
48
+ export { rugcheckSummary, rugcheckReport, rugcheckGraph, netBalances, creatorLaunches, CREATOR_DEAD_MCAP } from "./rugcheck.js";
49
+ export { score, tokenType, buildLights, levelFrom, dataOnly, reasonsFrom, assess, SERIAL_WARN, SERIAL_BAD, HISTORY_MIN, HISTORY_WARN, HISTORY_BAD_MIN, HISTORY_BAD } from "./risk.js";
50
50
  export { recentLaunches } from "./launch.js";
51
51
  export { analyzeToken, analyzeWallet, analyzeAddress, sortResults } from "./wallet.js";
52
52
  export { holderBase, markBusy, findFunder, findFunders, clusterByFunder, holderRows, creatorInfo, analyzeHolders } from "./holders.js";
package/src/risk.js CHANGED
@@ -16,6 +16,8 @@ function sig(code, s, data) {
16
16
  const DAY = 86400000;
17
17
  // Other tokens launched by the same creator in the last 24 h that make it a serial launcher
18
18
  export const SERIAL_WARN = 2, SERIAL_BAD = 5;
19
+ // Creator track record: at least HISTORY_MIN older tokens and this share of them never took off
20
+ export const HISTORY_MIN = 3, HISTORY_WARN = 0.8, HISTORY_BAD_MIN = 10, HISTORY_BAD = 0.9;
19
21
 
20
22
  /* ---------- old point score: only for tokens without a market, to spot spam ---------- */
21
23
  export function score(info) {
@@ -73,7 +75,7 @@ function mkLight(k, items, okSignal) {
73
75
  * @param value USD value the analysed wallet holds (0 when checking a token alone)
74
76
  * @param type tokenType(info, pair)
75
77
  * @param rc RugCheck summary or null
76
- * @param launches creatorLaunches result or null: other tokens the creator launched in the last 24 h
78
+ * @param launches creatorLaunches result or null: what else the creator launched (last 24 h, and how older ones did)
77
79
  * @returns [control, concentration, market, dependency]
78
80
  */
79
81
  export function buildLights(info, conc, pair, value, type, rc, now, launches) {
@@ -95,6 +97,11 @@ export function buildLights(info, conc, pair, value, type, rc, now, launches) {
95
97
  // Launch farms put out a token every few minutes and drop each one as soon as buyers arrive: in a
96
98
  // 100-token Pump.fun sample one wallet launched 7 copies of "GMS" in 10 minutes
97
99
  if (launches && launches.day >= SERIAL_WARN) it.push(sig("CTL_SERIAL_CREATOR", launches.day >= SERIAL_BAD ? "bad" : "warn", { n: launches.day, creator: launches.creator }));
100
+ // Older launches that went nowhere: farms that spread their tokens over days escape the 24 h count
101
+ if (launches && launches.old >= HISTORY_MIN && launches.dead / launches.old >= HISTORY_WARN) {
102
+ const bad = launches.old >= HISTORY_BAD_MIN && launches.dead / launches.old >= HISTORY_BAD;
103
+ it.push(sig("CTL_CREATOR_HISTORY", bad ? "bad" : "warn", { old: launches.old, dead: launches.dead, best: launches.best, creator: launches.creator }));
104
+ }
98
105
  const control = mkLight("control", it, sig((info && (info.mintAuth || info.freezeAuth)) ? "CTL_ONLY_PROGRAMS" : "CTL_NONE", "ok"));
99
106
 
100
107
  // 2) concentration in people (pools and programs do not count)
package/src/rugcheck.js CHANGED
@@ -32,16 +32,27 @@ export function rugcheckReport(client, mint) {
32
32
  return client.softJson(RUGCHECK + mint + "/report", 10000);
33
33
  }
34
34
 
35
- // How many other tokens the creator launched in the last 24 h. Only for new tokens: their full report
35
+ // A creator token older than a day still under this market cap never took off: a Pump.fun token
36
+ // starts at ~28 SOL (~$2,800 at $100/SOL). In a 43-token farm (2026-10-27) all sat at $2,800-4,400
37
+ export const CREATOR_DEAD_MCAP = 10000;
38
+
39
+ // What else the creator launched: how many in the last 24 h and, of the older ones RugCheck has a
40
+ // market cap for, how many stayed under CREATOR_DEAD_MCAP. Only for new tokens: their full report
36
41
  // is ~10 KB (the 700 KB ones are old tokens with many markets and holders).
37
- // @returns {creator, day, total} or null when RugCheck does not answer or does not know the creator
42
+ // @returns {creator, day, total, old, dead, best} or null when RugCheck does not answer or does not know the creator
38
43
  export async function creatorLaunches(client, mint, now) {
39
44
  const rc = await rugcheckReport(client, mint);
40
45
  if (!rc || !rc.creator) return null;
41
46
  now = now || Date.now();
42
47
  const others = (rc.creatorTokens || []).filter(function (t) { return t && t.mint !== mint; });
43
48
  const day = others.filter(function (t) { const at = Date.parse(t.createdAt); return at && now - at < 86400000; }).length;
44
- return { creator: rc.creator, day: day, total: others.length };
49
+ const old = others.filter(function (t) {
50
+ const at = Date.parse(t.createdAt);
51
+ return at && now - at >= 86400000 && typeof t.marketCap === "number" && isFinite(t.marketCap);
52
+ });
53
+ const dead = old.filter(function (t) { return t.marketCap < CREATOR_DEAD_MCAP; }).length;
54
+ const best = old.reduce(function (m, t) { return Math.max(m, t.marketCap); }, 0);
55
+ return { creator: rc.creator, day: day, total: others.length, old: old.length, dead: dead, best: old.length ? best : null };
45
56
  }
46
57
 
47
58
  // The report only gives each network's size; this endpoint gives its wallets and holdings