brainblast 0.6.3 → 0.7.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.
@@ -0,0 +1,176 @@
1
+ import {
2
+ buildTrustGraph
3
+ } from "./chunk-SVSVVW6U.js";
4
+
5
+ // src/score.ts
6
+ var W_AUTHORITY = 35;
7
+ var W_VERIFIED = 25;
8
+ var W_AUDITS = 20;
9
+ var W_CURATION = 10;
10
+ var W_PARITY = 10;
11
+ function scoreAuthority(ua) {
12
+ let points;
13
+ let detail;
14
+ switch (ua.kind) {
15
+ case "renounced":
16
+ points = W_AUTHORITY;
17
+ detail = "Upgrade authority renounced \u2014 program code is frozen forever.";
18
+ break;
19
+ case "dao":
20
+ points = 30;
21
+ detail = "Upgrade authority held by a governance program (DAO).";
22
+ break;
23
+ case "multisig":
24
+ points = 26;
25
+ detail = "Upgrade authority held by a multisig.";
26
+ break;
27
+ case "unknown":
28
+ if (ua.address) {
29
+ points = 12;
30
+ detail = `Upgrade authority present but unclassified (${ua.address}). Could be a single hot key.`;
31
+ } else {
32
+ points = 10;
33
+ detail = "Upgrade authority could not be determined.";
34
+ }
35
+ break;
36
+ case "single-key":
37
+ default:
38
+ points = 8;
39
+ detail = "Upgrade authority is a single key \u2014 it can replace the program's code at any time.";
40
+ break;
41
+ }
42
+ return { name: "Upgrade authority", weight: W_AUTHORITY, points, detail };
43
+ }
44
+ function scoreVerified(vb) {
45
+ let points;
46
+ let detail;
47
+ switch (vb.state) {
48
+ case "verified":
49
+ points = W_VERIFIED;
50
+ detail = `On-chain bytecode matches a published source build (${vb.registryUrl}).`;
51
+ break;
52
+ case "unverified":
53
+ points = 2;
54
+ detail = "Not present in any verified-build registry we trust.";
55
+ break;
56
+ case "unknown":
57
+ default:
58
+ points = 8;
59
+ detail = "Verified-build status not checked.";
60
+ break;
61
+ }
62
+ return { name: "Verified build", weight: W_VERIFIED, points, detail };
63
+ }
64
+ function scoreAudits(audits) {
65
+ let points = 0;
66
+ let detail = "No audits on record.";
67
+ if (audits.length >= 2) {
68
+ points = W_AUDITS;
69
+ detail = `${audits.length} audits on record (${audits.map((a) => a.firm).join(", ")}).`;
70
+ } else if (audits.length === 1) {
71
+ points = 16;
72
+ detail = `One audit on record (${audits[0].firm}, ${audits[0].date}).`;
73
+ }
74
+ return { name: "Audits", weight: W_AUDITS, points, detail };
75
+ }
76
+ function scoreCuration(program) {
77
+ const curated = program.upgradeAuthority.source === "directory" || !!program.provenance?.directoryFile;
78
+ return {
79
+ name: "Curation",
80
+ weight: W_CURATION,
81
+ points: curated ? W_CURATION : 0,
82
+ detail: curated ? "Listed in the curated program directory (known entity)." : "Not in the curated directory \u2014 identity is not independently corroborated."
83
+ };
84
+ }
85
+ function scoreParity(parity) {
86
+ let points;
87
+ let detail;
88
+ switch (parity.mainnet) {
89
+ case "present":
90
+ points = W_PARITY;
91
+ detail = "Deployed on mainnet as expected.";
92
+ break;
93
+ case "unknown":
94
+ points = 5;
95
+ detail = "Cross-cluster parity not checked.";
96
+ break;
97
+ default:
98
+ points = 0;
99
+ detail = `Mainnet deployment is '${parity.mainnet}' \u2014 possible devnet-only or address mismatch.`;
100
+ break;
101
+ }
102
+ return { name: "Cluster parity", weight: W_PARITY, points, detail };
103
+ }
104
+ function gradeForScore(score) {
105
+ if (score >= 90) return "A";
106
+ if (score >= 75) return "B";
107
+ if (score >= 60) return "C";
108
+ if (score >= 40) return "D";
109
+ return "F";
110
+ }
111
+ function scoreFromProgram(program) {
112
+ const factors = [
113
+ scoreAuthority(program.upgradeAuthority),
114
+ scoreVerified(program.verifiedBuild),
115
+ scoreAudits(program.audits ?? []),
116
+ scoreCuration(program),
117
+ scoreParity(program.parity ?? { mainnet: "unknown", devnet: "unknown" })
118
+ ];
119
+ const score = factors.reduce((s, f) => s + f.points, 0);
120
+ const grade = gradeForScore(score);
121
+ return {
122
+ programId: program.programId,
123
+ resolved: true,
124
+ score,
125
+ grade,
126
+ factors,
127
+ program,
128
+ summary: `${program.name} \u2014 grade ${grade} (${score}/100). ` + factors.find((f) => f.name === "Upgrade authority").detail
129
+ };
130
+ }
131
+ async function scoreProgram(programId, opts = {}) {
132
+ const graph = await buildTrustGraph([programId], opts);
133
+ const program = graph.programs.find((p) => p.programId === programId);
134
+ if (!program) {
135
+ const reason = graph.unresolved.find((u) => u.programId === programId)?.reason ?? "not resolved";
136
+ return {
137
+ programId,
138
+ resolved: false,
139
+ score: null,
140
+ grade: "unrated",
141
+ factors: [],
142
+ summary: `Could not resolve program ${programId}: ${reason}`,
143
+ unresolvedReason: reason
144
+ };
145
+ }
146
+ return scoreFromProgram(program);
147
+ }
148
+ function renderScoreText(s) {
149
+ if (!s.resolved) {
150
+ return `Trust score [unrated]
151
+ ${s.summary}`;
152
+ }
153
+ const lines = [];
154
+ lines.push(`Trust score [${s.grade}] ${s.score}/100`);
155
+ lines.push(` Program: ${s.program?.name ?? s.programId}`);
156
+ lines.push(` Address: ${s.programId}`);
157
+ lines.push("");
158
+ lines.push(" Factors:");
159
+ for (const f of s.factors) {
160
+ lines.push(` ${f.points}/${f.weight} ${f.name} \u2014 ${f.detail}`);
161
+ }
162
+ return lines.join("\n");
163
+ }
164
+ function gradeAtLeast(grade, min) {
165
+ const order = { F: 0, D: 1, C: 2, B: 3, A: 4 };
166
+ if (grade === "unrated") return false;
167
+ return order[grade] >= order[min];
168
+ }
169
+
170
+ export {
171
+ gradeForScore,
172
+ scoreFromProgram,
173
+ scoreProgram,
174
+ renderScoreText,
175
+ gradeAtLeast
176
+ };
@@ -0,0 +1,61 @@
1
+ // src/trustGraph/base58.ts
2
+ var ALPHA = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
3
+ var MAP = {};
4
+ for (let i = 0; i < ALPHA.length; i++) MAP[ALPHA[i]] = i;
5
+ function base58Encode(bytes) {
6
+ let zeros = 0;
7
+ while (zeros < bytes.length && bytes[zeros] === 0) zeros++;
8
+ const buf = Array.from(bytes);
9
+ const out = [];
10
+ let start = zeros;
11
+ while (start < buf.length) {
12
+ let rem = 0;
13
+ for (let i = start; i < buf.length; i++) {
14
+ const acc = rem * 256 + buf[i];
15
+ buf[i] = Math.floor(acc / 58);
16
+ rem = acc % 58;
17
+ }
18
+ out.push(rem);
19
+ if (buf[start] === 0) start++;
20
+ }
21
+ let s = "";
22
+ for (let i = 0; i < zeros; i++) s += "1";
23
+ for (let i = out.length - 1; i >= 0; i--) s += ALPHA[out[i]];
24
+ return s;
25
+ }
26
+ function base58Decode(s) {
27
+ let zeros = 0;
28
+ while (zeros < s.length && s[zeros] === "1") zeros++;
29
+ const buf = [];
30
+ for (let i = zeros; i < s.length; i++) {
31
+ const v = MAP[s[i]];
32
+ if (v === void 0) throw new Error(`base58: invalid char '${s[i]}' at ${i}`);
33
+ let carry = v;
34
+ for (let j = 0; j < buf.length; j++) {
35
+ const acc = buf[j] * 58 + carry;
36
+ buf[j] = acc & 255;
37
+ carry = acc >>> 8;
38
+ }
39
+ while (carry > 0) {
40
+ buf.push(carry & 255);
41
+ carry >>>= 8;
42
+ }
43
+ }
44
+ const out = new Uint8Array(zeros + buf.length);
45
+ for (let i = 0; i < buf.length; i++) out[zeros + buf.length - 1 - i] = buf[i];
46
+ return out;
47
+ }
48
+ function isValidSolanaAddress(s) {
49
+ if (typeof s !== "string" || s.length < 32 || s.length > 44) return false;
50
+ try {
51
+ return base58Decode(s).length === 32;
52
+ } catch {
53
+ return false;
54
+ }
55
+ }
56
+
57
+ export {
58
+ base58Encode,
59
+ base58Decode,
60
+ isValidSolanaAddress
61
+ };
@@ -0,0 +1,79 @@
1
+ import {
2
+ CANONICAL_BY_MINT,
3
+ CANONICAL_MINTS
4
+ } from "./chunk-2XJORJPQ.js";
5
+
6
+ // src/tokenRegistry.ts
7
+ async function jupiterLookup(mint, baseUrl) {
8
+ const url = `${baseUrl ?? "https://tokens.jup.ag"}/token/${mint}`;
9
+ try {
10
+ const res = await fetch(url, { signal: AbortSignal.timeout(1e4) });
11
+ if (res.status === 404) return null;
12
+ if (!res.ok) return null;
13
+ return await res.json();
14
+ } catch {
15
+ return null;
16
+ }
17
+ }
18
+ function detectImpersonation(resolvedSymbol) {
19
+ if (!resolvedSymbol) return { impersonation: false };
20
+ const upper = resolvedSymbol.toUpperCase();
21
+ const canonical = CANONICAL_MINTS[upper];
22
+ if (!canonical) return { impersonation: false };
23
+ return { impersonation: true, canonicalMint: canonical.mint };
24
+ }
25
+ async function verifyTokenIdentity(mint, opts = {}) {
26
+ const bundled = CANONICAL_BY_MINT[mint];
27
+ if (bundled) {
28
+ const expectMismatch2 = opts.expectSymbol ? opts.expectSymbol.toUpperCase() !== bundled.symbol.toUpperCase() : void 0;
29
+ return {
30
+ mint,
31
+ status: "verified-canonical",
32
+ symbol: bundled.symbol,
33
+ name: bundled.name,
34
+ source: "bundled",
35
+ impersonation: false,
36
+ expectMismatch: expectMismatch2 ?? void 0
37
+ };
38
+ }
39
+ if (opts.offline) {
40
+ const { impersonation: impersonation2, canonicalMint: canonicalMint2 } = detectImpersonation(opts.claimedSymbol);
41
+ return {
42
+ mint,
43
+ status: "unverified",
44
+ source: "none",
45
+ impersonation: impersonation2,
46
+ canonicalMint: canonicalMint2,
47
+ detail: "offline mode \u2014 Jupiter lookup skipped"
48
+ };
49
+ }
50
+ const jup = await jupiterLookup(mint, opts.baseUrl);
51
+ if (!jup) {
52
+ const { impersonation: impersonation2, canonicalMint: canonicalMint2 } = detectImpersonation(opts.claimedSymbol);
53
+ return {
54
+ mint,
55
+ status: "unknown",
56
+ source: "none",
57
+ impersonation: impersonation2,
58
+ canonicalMint: canonicalMint2
59
+ };
60
+ }
61
+ const resolvedSymbol = jup.symbol ?? opts.claimedSymbol;
62
+ const { impersonation, canonicalMint } = detectImpersonation(resolvedSymbol);
63
+ const expectMismatch = opts.expectSymbol ? opts.expectSymbol.toUpperCase() !== (resolvedSymbol ?? "").toUpperCase() : void 0;
64
+ const status = impersonation ? "unverified" : "verified";
65
+ return {
66
+ mint,
67
+ status,
68
+ symbol: jup.symbol,
69
+ name: jup.name,
70
+ source: "jupiter",
71
+ impersonation,
72
+ canonicalMint,
73
+ expectMismatch: expectMismatch ?? void 0
74
+ };
75
+ }
76
+
77
+ export {
78
+ verifyTokenIdentity
79
+ };
@@ -0,0 +1,148 @@
1
+ import {
2
+ analyzeToken
3
+ } from "./chunk-FQA5BYWW.js";
4
+ import {
5
+ verifyTokenIdentity
6
+ } from "./chunk-VI2JBH2T.js";
7
+ import {
8
+ getAccountInfo
9
+ } from "./chunk-XSVQSK53.js";
10
+ import {
11
+ base58Encode
12
+ } from "./chunk-VG5FMOLW.js";
13
+
14
+ // src/pumpCheck.ts
15
+ function readU32LE(d, o) {
16
+ return d[o] | d[o + 1] << 8 | d[o + 2] << 16 | d[o + 3] << 24;
17
+ }
18
+ function readU64LE(d, o) {
19
+ let v = 0n;
20
+ for (let i = 7; i >= 0; i--) v = v << 8n | BigInt(d[o + i]);
21
+ return v;
22
+ }
23
+ function parseMintAccount(data) {
24
+ if (data.length < 82) throw new Error(`not an SPL mint account (got ${data.length} bytes, need \u226582)`);
25
+ const mintAuthOption = readU32LE(data, 0);
26
+ const freezeAuthOption = readU32LE(data, 46);
27
+ return {
28
+ mintAuthorityRevoked: mintAuthOption === 0,
29
+ freezeAuthorityRevoked: freezeAuthOption === 0,
30
+ mintAuthority: mintAuthOption === 1 ? base58Encode(data.subarray(4, 36)) : null,
31
+ freezeAuthority: freezeAuthOption === 1 ? base58Encode(data.subarray(50, 82)) : null,
32
+ supply: readU64LE(data, 36).toString(),
33
+ decimals: data[44],
34
+ isInitialized: data[45] === 1
35
+ };
36
+ }
37
+ function verdictFrom(checks) {
38
+ if (checks.some((c) => c.status === "fail")) return "NO-GO";
39
+ if (checks.some((c) => c.status === "warn")) return "CAUTION";
40
+ return "GO";
41
+ }
42
+ async function pumpPreflight(mint, opts = {}) {
43
+ const failOnRisk = opts.failOnRisk ?? 70;
44
+ const checks = [];
45
+ const report = { mint, verdict: "GO", checks };
46
+ try {
47
+ const acct = await getAccountInfo(mint, opts);
48
+ if (!acct) {
49
+ checks.push({ id: "mint-exists", label: "Mint account exists", status: "fail", detail: "No account found at this address on the selected cluster." });
50
+ } else {
51
+ const info = parseMintAccount(acct.data);
52
+ report.mintInfo = info;
53
+ checks.push({
54
+ id: "mint-authority-revoked",
55
+ label: "Mint authority revoked",
56
+ status: info.mintAuthorityRevoked ? "pass" : "fail",
57
+ detail: info.mintAuthorityRevoked ? "Mint authority is revoked \u2014 total supply is fixed." : `Mint authority is LIVE (${info.mintAuthority}). The holder can mint unlimited new supply and dilute every holder.`
58
+ });
59
+ checks.push({
60
+ id: "freeze-authority-revoked",
61
+ label: "Freeze authority revoked",
62
+ status: info.freezeAuthorityRevoked ? "pass" : "warn",
63
+ detail: info.freezeAuthorityRevoked ? "Freeze authority is revoked \u2014 token accounts cannot be frozen." : `Freeze authority is LIVE (${info.freezeAuthority}). The holder can freeze any user's token account.`
64
+ });
65
+ }
66
+ } catch (e) {
67
+ checks.push({ id: "mint-read", label: "Read mint account", status: "skip", detail: `Could not read mint account: ${e?.message ?? String(e)}` });
68
+ }
69
+ try {
70
+ const identity = await verifyTokenIdentity(mint, { baseUrl: opts.jupBaseUrl, offline: opts.offline });
71
+ report.identity = identity;
72
+ checks.push({
73
+ id: "identity",
74
+ label: "Token identity",
75
+ status: identity.impersonation ? "fail" : "pass",
76
+ detail: identity.impersonation ? `Impersonation: claims symbol '${identity.symbol}' but the canonical mint is ${identity.canonicalMint}.` : `Identity: ${identity.status}${identity.symbol ? ` (${identity.symbol})` : ""}.`
77
+ });
78
+ } catch (e) {
79
+ checks.push({ id: "identity", label: "Token identity", status: "skip", detail: `Identity check failed: ${e?.message ?? String(e)}` });
80
+ }
81
+ if (!opts.offline) {
82
+ const outcome = await analyzeToken(mint, { apiKey: opts.apiKey, baseUrl: opts.ricoBaseUrl });
83
+ if (outcome.ok) {
84
+ const q = outcome.result;
85
+ report.quality = q;
86
+ checks.push({
87
+ id: "risk-score",
88
+ label: "Rico risk score",
89
+ status: q.riskScore >= failOnRisk ? "fail" : q.riskScore >= 40 ? "warn" : "pass",
90
+ detail: `Rico risk score ${q.riskScore}/100 (threshold ${failOnRisk}).`
91
+ });
92
+ checks.push({
93
+ id: "snipers",
94
+ label: "Sniper activity",
95
+ status: q.snipersDetected ? "warn" : "pass",
96
+ detail: q.snipersDetected ? `Snipers detected (${(q.sniperPct * 100).toFixed(1)}% of holders).` : "No snipers detected."
97
+ });
98
+ checks.push({
99
+ id: "bundle-clusters",
100
+ label: "Bundle clusters",
101
+ status: q.bundleClustersDetected ? "warn" : "pass",
102
+ detail: q.bundleClustersDetected ? "Bundle launch clusters detected." : "No bundle clusters detected."
103
+ });
104
+ checks.push({
105
+ id: "holders",
106
+ label: "Holder distribution",
107
+ status: q.totalHolders < 50 ? "warn" : "pass",
108
+ detail: `${q.totalHolders} holders${q.cabalCount ? `, ${q.cabalCount} cabal wallet(s)` : ""}.`
109
+ });
110
+ if (q.deployerFlags.length) {
111
+ checks.push({
112
+ id: "deployer-flags",
113
+ label: "Deployer flags",
114
+ status: "warn",
115
+ detail: `Deployer flags: ${q.deployerFlags.join(", ")}.`
116
+ });
117
+ }
118
+ } else {
119
+ checks.push({
120
+ id: "rico-scan",
121
+ label: "Rico Maps scan",
122
+ status: "skip",
123
+ detail: `Quality scan skipped (${outcome.kind}): ${outcome.error}.`
124
+ });
125
+ }
126
+ } else {
127
+ checks.push({ id: "rico-scan", label: "Rico Maps scan", status: "skip", detail: "Offline mode \u2014 quality scan skipped." });
128
+ }
129
+ report.verdict = verdictFrom(checks);
130
+ return report;
131
+ }
132
+ var STATUS_ICON = { pass: "\u2713", warn: "\u26A0", fail: "\u2717", skip: "\xB7" };
133
+ function renderPreflightText(r) {
134
+ const lines = [];
135
+ lines.push(`Launch pre-flight [${r.verdict}] ${r.mint}`);
136
+ if (r.mintInfo) lines.push(` Supply: ${r.mintInfo.supply} Decimals: ${r.mintInfo.decimals}`);
137
+ lines.push("");
138
+ for (const c of r.checks) {
139
+ lines.push(` ${STATUS_ICON[c.status]} ${c.label} \u2014 ${c.detail}`);
140
+ }
141
+ return lines.join("\n");
142
+ }
143
+
144
+ export {
145
+ parseMintAccount,
146
+ pumpPreflight,
147
+ renderPreflightText
148
+ };
@@ -0,0 +1,100 @@
1
+ import {
2
+ base58Encode,
3
+ isValidSolanaAddress
4
+ } from "./chunk-VG5FMOLW.js";
5
+
6
+ // src/trustGraph/rpc.ts
7
+ var BPF_UPGRADEABLE_LOADER = "BPFLoaderUpgradeab1e11111111111111111111111";
8
+ var BPF_LOADER_2 = "BPFLoader2111111111111111111111111111111111";
9
+ var NATIVE_LOADER = "NativeLoader1111111111111111111111111111111";
10
+ var DEFAULT_RPC = "https://api.mainnet-beta.solana.com";
11
+ async function rpc(method, params, opts) {
12
+ const url = opts.rpcUrl ?? DEFAULT_RPC;
13
+ const fetchImpl = opts.fetchImpl ?? fetch;
14
+ const ac = new AbortController();
15
+ const t = setTimeout(() => ac.abort(), opts.timeoutMs ?? 1e4);
16
+ try {
17
+ const res = await fetchImpl(url, {
18
+ method: "POST",
19
+ headers: { "content-type": "application/json" },
20
+ body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
21
+ signal: ac.signal
22
+ });
23
+ if (!res.ok) throw new Error(`rpc ${method}: HTTP ${res.status}`);
24
+ const body = await res.json();
25
+ if (body.error) throw new Error(`rpc ${method}: ${body.error.message}`);
26
+ if (body.result === void 0) throw new Error(`rpc ${method}: empty result`);
27
+ return body.result;
28
+ } finally {
29
+ clearTimeout(t);
30
+ }
31
+ }
32
+ async function getAccountInfo(address, opts = {}) {
33
+ if (!isValidSolanaAddress(address)) throw new Error(`invalid Solana address: ${address}`);
34
+ const result = await rpc(
35
+ "getAccountInfo",
36
+ [address, { encoding: "base64", commitment: "confirmed" }],
37
+ opts
38
+ );
39
+ if (!result || !result.value) return null;
40
+ const v = result.value;
41
+ const [b64] = v.data;
42
+ return {
43
+ owner: v.owner,
44
+ data: Buffer.from(b64, "base64"),
45
+ executable: v.executable,
46
+ lamports: v.lamports
47
+ };
48
+ }
49
+ async function probeUpgradeAuthority(programId, opts = {}) {
50
+ const acct = await getAccountInfo(programId, opts);
51
+ if (!acct) {
52
+ return {
53
+ kind: "unknown",
54
+ address: null,
55
+ source: "rpc",
56
+ checkedAt: (/* @__PURE__ */ new Date()).toISOString()
57
+ };
58
+ }
59
+ if (acct.owner === BPF_LOADER_2 || acct.owner === NATIVE_LOADER) {
60
+ return {
61
+ kind: "renounced",
62
+ address: null,
63
+ source: "rpc",
64
+ checkedAt: (/* @__PURE__ */ new Date()).toISOString()
65
+ };
66
+ }
67
+ if (acct.owner !== BPF_UPGRADEABLE_LOADER) {
68
+ throw new Error(
69
+ `program ${programId} is owned by ${acct.owner}, not a known loader; not a deployed program?`
70
+ );
71
+ }
72
+ if (acct.data.length < 36) throw new Error(`program account too small: ${acct.data.length}`);
73
+ const tag = acct.data[0] | acct.data[1] << 8 | acct.data[2] << 16 | acct.data[3] << 24;
74
+ if (tag !== 2) throw new Error(`expected Program (tag=2) state, got tag=${tag}`);
75
+ const programDataAddr = base58Encode(acct.data.subarray(4, 36));
76
+ const pd = await getAccountInfo(programDataAddr, opts);
77
+ if (!pd) {
78
+ throw new Error(`program ${programId} ProgramData ${programDataAddr} not found`);
79
+ }
80
+ if (pd.data.length < 45) throw new Error(`ProgramData account too small: ${pd.data.length}`);
81
+ const pdTag = pd.data[0] | pd.data[1] << 8 | pd.data[2] << 16 | pd.data[3] << 24;
82
+ if (pdTag !== 3) throw new Error(`expected ProgramData (tag=3), got tag=${pdTag}`);
83
+ const optionTag = pd.data[12];
84
+ const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
85
+ if (optionTag === 0) {
86
+ return { kind: "renounced", address: null, source: "rpc", checkedAt };
87
+ }
88
+ if (optionTag !== 1) throw new Error(`unexpected Option tag in ProgramData: ${optionTag}`);
89
+ const authority = base58Encode(pd.data.subarray(13, 45));
90
+ return { kind: "unknown", address: authority, source: "rpc", checkedAt };
91
+ }
92
+
93
+ export {
94
+ BPF_UPGRADEABLE_LOADER,
95
+ BPF_LOADER_2,
96
+ NATIVE_LOADER,
97
+ DEFAULT_RPC,
98
+ getAccountInfo,
99
+ probeUpgradeAuthority
100
+ };