solana-risk-engine 0.4.11 → 0.4.12
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/README.md +1 -1
- package/package.json +1 -1
- package/src/index.js +1 -1
- package/src/launch.js +24 -0
- package/src/risk.js +5 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ for (const l of r.lights) // the four traffic lights
|
|
|
28
28
|
| **Real concentration** | Largest accounts are traced to their owners. Pools and programs are separated from people, so `w1`/`w10` measure *people*, not AMM vaults. |
|
|
29
29
|
| **Inflated liquidity** | DexScreener values both sides of a pool at the pool's price. A pool with 4B of a token against 2 USDC shows "$4,000M liquidity". The engine recomputes it from the real side (USDC/USDT/SOL). It only flags extreme gaps (real side under 5%), because concentrated-liquidity pools are lopsided by nature. |
|
|
30
30
|
| **Unlocked LP** | % of LP locked/burned via RugCheck. It only counts for markets under 90 days old, so veteran tokens don't get false positives, and never while a token is on Pump.fun's bonding curve, where there is no LP to pull. |
|
|
31
|
-
| **Serial creators** | For markets under 7 days: how many other tokens the creator launched in the last 24 h (RugCheck). 2 or more is a warning, 5 or more a red flag. `recentLaunches(client, wallet)` counts the same on chain for the wallet that *paid* a launch, which catches farms that name a fresh creator each time. |
|
|
31
|
+
| **Serial creators** | For markets under 7 days: how many other tokens the creator launched in the last 24 h (RugCheck). 2 or more is a warning, 5 or more a red flag. `recentLaunches(client, wallet)` counts the same on chain for the wallet that *paid* a launch, which catches farms that name a fresh creator each time. When RugCheck has no report yet (it often answers 400 for tokens a few minutes old) the analysis falls back to `chainLaunches(client, mint)`: that count for the first signer of the token's creation transaction. |
|
|
32
32
|
| **Impostors** | Tokens using the symbol or name of a famous token (USDC, JUP, BONK, WIF, BTC, ETH...) with a different address. |
|
|
33
33
|
| **Real brands** | About 320 real companies with no token on Solana (Spotify, Nike, Zara, Ferrari...). A token called just that is a red flag and **HIGH**; the brand as one word of a longer name is a warning. Everyday words that are also brands (Shell, Target, Puma) are left out, and markets at least 30 days old with $100k+ liquidity are not checked. |
|
|
34
34
|
| **Known issuers and official tokens** | Stablecoins from Paxos (PYUSD, USDG), Circle (USDC, EURC) and Tether (USDT), tokenized shares from xStocks (all recognised by their control keys, taken from mints the issuer lists on its own pages) and official tokens from exchanges and Socios.com (bnSOL, bbSOL, cbBTC, club fan tokens..., by mint address) are shown as dependency on the issuer. Their large holders (treasury, custodians, exchanges) are shown but do not count as concentration, a 0% transfer fee is not a warning, and a tiny Solana pool is a warning, not a red flag. A short **trusted** list (Circle, Paxos, Tether, xStocks; `trustedIssuer()`) goes further: every light green, holders not checked. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solana-risk-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.12",
|
|
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/index.js
CHANGED
|
@@ -49,7 +49,7 @@ export { getTokens, getMint, getConc, getAuthKinds, isMint, accountKinds, kindOf
|
|
|
49
49
|
export { getMarkets, realLiq, impostorOf, INFLATED_RATIO, geckoLiquidity, geckoPair, brandOf, copiesOf, fakeLiquidity, FAKE_LIQ_RATIO } from "./markets.js";
|
|
50
50
|
export { rugcheckSummary, rugcheckReport, rugcheckGraph, netBalances, creatorLaunches, CREATOR_DEAD_MCAP } from "./rugcheck.js";
|
|
51
51
|
export { score, issuerOf, trustedIssuer, tokenType, buildLights, levelFrom, dataOnly, reasonsFrom, assess, SERIAL_WARN, SERIAL_BAD, HISTORY_MIN, HISTORY_WARN, HISTORY_BAD_MIN, HISTORY_BAD } from "./risk.js";
|
|
52
|
-
export { recentLaunches } from "./launch.js";
|
|
52
|
+
export { recentLaunches, chainLaunches } from "./launch.js";
|
|
53
53
|
export { analyzeToken, analyzeWallet, analyzeAddress, sortResults } from "./wallet.js";
|
|
54
54
|
export { holderBase, markBusy, onlyReceives, PASSIVE_SAMPLE, findFunder, findFunders, clusterByFunder, holderRows, creatorInfo, analyzeHolders,
|
|
55
55
|
perHour, sourceRates, groupList, EXCHANGE_PER_HOUR, YOUNG_DAYS, HOLD_GROUP_YOUNG, HOLD_GROUP_MATURE } from "./holders.js";
|
package/src/launch.js
CHANGED
|
@@ -42,3 +42,27 @@ export async function recentLaunches(client, wallet, opts) {
|
|
|
42
42
|
});
|
|
43
43
|
return { n: mints.length, mints: mints, scanned: day.length, more: sigs.length >= limit && day.length === sigs.length };
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The creator's recent launches read on chain, for when RugCheck does not know the token yet (it answers
|
|
48
|
+
* 400 "unable to generate report" for most tokens a few minutes old, 2026-11-02) or does not answer.
|
|
49
|
+
* The launcher is the first signer of the token's creation transaction: the wallet that paid, as in
|
|
50
|
+
* recentLaunches (farms name a fresh creator each time but pay from one wallet). Only new tokens: past
|
|
51
|
+
* 1000 transactions the creation is too far back to find cheaply.
|
|
52
|
+
* Same shape as creatorLaunches, without the history of older launches (that needs market caps):
|
|
53
|
+
* @returns {creator, day, total, old: 0, dead: 0, best: null, chain: true, more} or null
|
|
54
|
+
*/
|
|
55
|
+
export async function chainLaunches(client, mint, opts) {
|
|
56
|
+
opts = opts || {};
|
|
57
|
+
const sigs = (await client.rpc("getSignaturesForAddress", [mint, { limit: 1000 }])) || [];
|
|
58
|
+
if (!sigs.length || sigs.length >= 1000) return null;
|
|
59
|
+
const p = { encoding: "jsonParsed", maxSupportedTransactionVersion: 1, commitment: "confirmed" };
|
|
60
|
+
const tx = await client.rpc("getTransaction", [sigs[sigs.length - 1].signature, p]);
|
|
61
|
+
const keys = tx && tx.transaction && tx.transaction.message && tx.transaction.message.accountKeys;
|
|
62
|
+
const first = keys && keys.filter(function (k) { return k.signer; })[0];
|
|
63
|
+
const wallet = first && (first.pubkey || first);
|
|
64
|
+
if (!wallet || wallet === mint) return null;
|
|
65
|
+
const r = await recentLaunches(client, wallet, { now: opts.now, limit: opts.limit });
|
|
66
|
+
const day = r.mints.filter(function (m) { return m !== mint; }).length;
|
|
67
|
+
return { creator: wallet, day: day, total: day, old: 0, dead: 0, best: null, chain: true, more: r.more };
|
|
68
|
+
}
|
package/src/risk.js
CHANGED
|
@@ -4,6 +4,7 @@ import { short, sleep } from "./util.js";
|
|
|
4
4
|
import { getMint, getAuthKinds, getConc } from "./tokens.js";
|
|
5
5
|
import { impostorOf, brandOf, copiesOf, fakeLiquidity } from "./markets.js";
|
|
6
6
|
import { rugcheckSummary, creatorLaunches } from "./rugcheck.js";
|
|
7
|
+
import { chainLaunches } from "./launch.js";
|
|
7
8
|
|
|
8
9
|
// Every finding is a "signal": {code, s, data}. `s` is ok | na | warn | bad, `data` holds the numbers
|
|
9
10
|
// behind it. Turning signals into sentences is i18n's job (describe / explainLight), never the engine's.
|
|
@@ -264,7 +265,10 @@ export async function assess(client, mint, pair, opts) {
|
|
|
264
265
|
const rcPromise = pair ? rugcheckSummary(client, mint) : Promise.resolve(null); // in parallel with the RPC calls below
|
|
265
266
|
// Serial launching only matters while a token is new; RugCheck's full report is small (~10 KB) then
|
|
266
267
|
const fresh = pair && !opts.skipCreator && (!pair.pairCreatedAt || Date.now() - pair.pairCreatedAt < 7 * DAY);
|
|
267
|
-
|
|
268
|
+
// when RugCheck does not know the creator (new tokens often get a 400) the launches are counted on chain
|
|
269
|
+
const launchPromise = fresh ? creatorLaunches(client, mint).then(function (r) {
|
|
270
|
+
return r || chainLaunches(client, mint).catch(function (e) { if (isCancel(e)) throw e; return null; });
|
|
271
|
+
}) : Promise.resolve(null);
|
|
268
272
|
// Copies of the same name and ticker: one search per token, so only when asked (a single token, not a wallet)
|
|
269
273
|
const copiesPromise = pair && opts.copies && !OFFICIAL_TOKENS[mint] ? copiesOf(client, mint, pair).catch(function (e) { if (isCancel(e)) throw e; return null; }) : Promise.resolve(null);
|
|
270
274
|
let info = null, conc = null;
|