@helium/sus 0.6.2-next.34
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/LICENSE +203 -0
- package/README.md +462 -0
- package/lib/cjs/index.js +699 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/esm/src/index.js +670 -0
- package/lib/esm/src/index.js.map +1 -0
- package/lib/esm/tsconfig.esm.tsbuildinfo +1 -0
- package/lib/types/src/index.d.ts +126 -0
- package/lib/types/src/index.d.ts.map +1 -0
- package/package.json +48 -0
package/lib/cjs/index.js
ADDED
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.fetchMetadatas = exports.getMetadataId = exports.getDetailedWritableAccountsWithoutTM = exports.isInsufficientBal = exports.sus = void 0;
|
|
16
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
17
|
+
const idl_1 = require("@coral-xyz/anchor/dist/cjs/idl");
|
|
18
|
+
const bytes_1 = require("@coral-xyz/anchor/dist/cjs/utils/bytes");
|
|
19
|
+
const mpl_bubblegum_1 = require("@metaplex-foundation/mpl-bubblegum");
|
|
20
|
+
const mpl_token_metadata_1 = require("@metaplex-foundation/mpl-token-metadata");
|
|
21
|
+
const sha256_1 = require("@noble/hashes/sha256");
|
|
22
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
23
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
24
|
+
const axios_1 = __importDefault(require("axios"));
|
|
25
|
+
const pako_1 = require("pako");
|
|
26
|
+
const BUBBLEGUM_PROGRAM_ID = new web3_js_1.PublicKey("BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY");
|
|
27
|
+
const ACCOUNT_COMPRESSION_PROGRAM_ID = new web3_js_1.PublicKey("cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK");
|
|
28
|
+
function getAccountKeys({ connection, transaction, }) {
|
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
const addressLookupTableAccounts = [];
|
|
31
|
+
const { addressTableLookups } = transaction.message;
|
|
32
|
+
if (addressTableLookups.length > 0) {
|
|
33
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
34
|
+
for (const addressTableLookup of addressTableLookups) {
|
|
35
|
+
// eslint-disable-next-line no-await-in-loop
|
|
36
|
+
const result = yield (connection === null || connection === void 0 ? void 0 : connection.getAddressLookupTable(addressTableLookup.accountKey));
|
|
37
|
+
if (result === null || result === void 0 ? void 0 : result.value) {
|
|
38
|
+
addressLookupTableAccounts.push(result === null || result === void 0 ? void 0 : result.value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return transaction.message.getAccountKeys({
|
|
43
|
+
addressLookupTableAccounts,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function getMultipleAccounts({ connection, keys, }) {
|
|
48
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
const batchSize = 100;
|
|
50
|
+
const batches = Math.ceil(keys.length / batchSize);
|
|
51
|
+
const results = [];
|
|
52
|
+
for (let i = 0; i < batches; i++) {
|
|
53
|
+
const batchKeys = keys.slice(i * batchSize, (i + 1) * batchSize);
|
|
54
|
+
const batchResults = yield connection.getMultipleAccountsInfo(batchKeys);
|
|
55
|
+
results.push(...batchResults);
|
|
56
|
+
}
|
|
57
|
+
return results;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function sus({ connection, wallet, serializedTransactions,
|
|
61
|
+
/// CNFT specific params
|
|
62
|
+
checkCNfts = false, extraSearchAssetParams, cNfts,
|
|
63
|
+
// Cluster for explorer
|
|
64
|
+
cluster = "mainnet-beta", accountBlacklist, }) {
|
|
65
|
+
var _a, _b;
|
|
66
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
67
|
+
let assets = cNfts;
|
|
68
|
+
if (checkCNfts) {
|
|
69
|
+
if (!assets) {
|
|
70
|
+
const assetsResponse = yield axios_1.default.post(connection.rpcEndpoint, {
|
|
71
|
+
jsonrpc: "2.0",
|
|
72
|
+
method: "searchAssets",
|
|
73
|
+
id: "get-assets-op-1",
|
|
74
|
+
params: Object.assign({ page: 1,
|
|
75
|
+
// limit to checking 200 assets
|
|
76
|
+
limit: 200, compressed: true, ownerAddress: wallet.toBase58() }, extraSearchAssetParams),
|
|
77
|
+
headers: {
|
|
78
|
+
"Cache-Control": "no-cache",
|
|
79
|
+
Pragma: "no-cache",
|
|
80
|
+
Expires: "0",
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
assets = (_a = assetsResponse.data.result) === null || _a === void 0 ? void 0 : _a.items;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const warningsByTx = serializedTransactions.map(() => []);
|
|
87
|
+
const transactions = serializedTransactions.map((t) => web3_js_1.VersionedTransaction.deserialize(t));
|
|
88
|
+
const accountKeysByTx = yield Promise.all(transactions.map((transaction) => getAccountKeys({ connection, transaction })));
|
|
89
|
+
const simulationAccountsByTx = accountKeysByTx.map((accountKeys, txIndex) => [
|
|
90
|
+
...new Set(accountKeys.staticAccountKeys
|
|
91
|
+
.filter((_, index) => transactions[txIndex].message.isAccountWritable(index))
|
|
92
|
+
.concat(accountKeys.accountKeysFromLookups
|
|
93
|
+
? // Only writable accounts will contribute to balance changes
|
|
94
|
+
accountKeys.accountKeysFromLookups.writable
|
|
95
|
+
: [])),
|
|
96
|
+
].filter((a) => !(accountBlacklist === null || accountBlacklist === void 0 ? void 0 : accountBlacklist.has(a.toBase58()))));
|
|
97
|
+
const allAccounts = [...new Set(simulationAccountsByTx.flat())];
|
|
98
|
+
const fetchedAccounts = yield getMultipleAccounts({
|
|
99
|
+
connection,
|
|
100
|
+
keys: allAccounts,
|
|
101
|
+
});
|
|
102
|
+
const fetchedAccountsByAddr = fetchedAccounts.reduce((acc, account, index) => {
|
|
103
|
+
acc[allAccounts[index].toBase58()] = account;
|
|
104
|
+
return acc;
|
|
105
|
+
}, {});
|
|
106
|
+
let { blockhash } = yield (connection === null || connection === void 0 ? void 0 : connection.getLatestBlockhash("finalized"));
|
|
107
|
+
const simulatedTxs = [];
|
|
108
|
+
// Linearly simulate txs so as not to hit rate limits
|
|
109
|
+
for (const [index, transaction] of transactions.entries()) {
|
|
110
|
+
let simulatedTxn = null;
|
|
111
|
+
let tries = 0;
|
|
112
|
+
// Retry until we stop getting blockhashNotFound
|
|
113
|
+
blockhashLoop: while (true) {
|
|
114
|
+
transaction.message.recentBlockhash = blockhash;
|
|
115
|
+
simulatedTxn = yield connection.simulateTransaction(transaction, {
|
|
116
|
+
accounts: {
|
|
117
|
+
encoding: "base64",
|
|
118
|
+
addresses: ((_b = simulationAccountsByTx[index]) === null || _b === void 0 ? void 0 : _b.map((account) => account.toBase58())) || [],
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
if (isBlockhashNotFound(simulatedTxn)) {
|
|
122
|
+
({ blockhash } = yield (connection === null || connection === void 0 ? void 0 : connection.getLatestBlockhash("finalized")));
|
|
123
|
+
tries++;
|
|
124
|
+
if (tries >= 5) {
|
|
125
|
+
simulatedTxs.push(simulatedTxn);
|
|
126
|
+
break blockhashLoop;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
simulatedTxs.push(simulatedTxn);
|
|
131
|
+
break blockhashLoop;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const fullAccountsByTxn = simulationAccountsByTx.map((simulationAccounts, transactionIndex) => {
|
|
136
|
+
const simulatedTxn = simulatedTxs[transactionIndex];
|
|
137
|
+
return simulationAccounts.map((account, index) => {
|
|
138
|
+
var _a;
|
|
139
|
+
const post = (_a = simulatedTxn.value.accounts) === null || _a === void 0 ? void 0 : _a[index];
|
|
140
|
+
return {
|
|
141
|
+
address: account,
|
|
142
|
+
post: post
|
|
143
|
+
? Object.assign(Object.assign({}, post), { owner: new web3_js_1.PublicKey(post.owner), data: Buffer.from(post.data[0], post.data[1]) }) : undefined,
|
|
144
|
+
pre: fetchedAccountsByAddr[account.toBase58()],
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
const instructionProgramIds = transactions
|
|
149
|
+
.flatMap((transaction, index) => transaction.message.compiledInstructions.map((ix) => accountKeysByTx[index].get(ix.programIdIndex) || null))
|
|
150
|
+
.filter(truthy);
|
|
151
|
+
const programKeys = fullAccountsByTxn
|
|
152
|
+
.flat()
|
|
153
|
+
.map((acc) => { var _a; return ((_a = acc === null || acc === void 0 ? void 0 : acc.pre) === null || _a === void 0 ? void 0 : _a.owner) || (acc.post ? new web3_js_1.PublicKey(acc.post.owner) : null); })
|
|
154
|
+
.concat(...instructionProgramIds)
|
|
155
|
+
.filter(truthy);
|
|
156
|
+
const idlKeys = programKeys.map(getIdlKey);
|
|
157
|
+
const idls = (yield getMultipleAccounts({ connection, keys: idlKeys }))
|
|
158
|
+
.map((acc, index) => {
|
|
159
|
+
if (acc) {
|
|
160
|
+
return {
|
|
161
|
+
program: programKeys[index],
|
|
162
|
+
idl: decodeIdl(acc),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
.filter(truthy)
|
|
167
|
+
.reduce((acc, { program, idl }) => {
|
|
168
|
+
if (idl) {
|
|
169
|
+
acc[program.toBase58()] = idl;
|
|
170
|
+
}
|
|
171
|
+
return acc;
|
|
172
|
+
}, {});
|
|
173
|
+
const writableAccountsByTxRaw = fullAccountsByTxn.map((accounts) => getDetailedWritableAccountsWithoutTM({
|
|
174
|
+
accounts,
|
|
175
|
+
idls,
|
|
176
|
+
}));
|
|
177
|
+
const tokens = [
|
|
178
|
+
...new Set(writableAccountsByTxRaw.flatMap((w) => w.tokens).map((t) => t.toBase58())),
|
|
179
|
+
].map((t) => new web3_js_1.PublicKey(t));
|
|
180
|
+
const metadatas = (yield fetchMetadatas(connection, tokens)).reduce((acc, m, index) => {
|
|
181
|
+
if (m) {
|
|
182
|
+
acc[tokens[index].toBase58()] = m;
|
|
183
|
+
}
|
|
184
|
+
return acc;
|
|
185
|
+
}, {});
|
|
186
|
+
const writableAccountsByTx = writableAccountsByTxRaw.map(({ withoutMetadata }, index) => {
|
|
187
|
+
const writableAccounts = withoutMetadata.map((acc) => {
|
|
188
|
+
var _a, _b;
|
|
189
|
+
let name = acc.name;
|
|
190
|
+
let metadata;
|
|
191
|
+
// Attempt to take last known type
|
|
192
|
+
const type = (acc.pre.type !== "Unknown" && acc.pre.type) ||
|
|
193
|
+
(acc.post.type !== "Unknown" && acc.post.type) ||
|
|
194
|
+
"Unknown";
|
|
195
|
+
// If token, get the name based on the metadata
|
|
196
|
+
if (type === "Mint") {
|
|
197
|
+
metadata = metadatas[acc.address.toBase58()];
|
|
198
|
+
if (metadata) {
|
|
199
|
+
name = `${metadata.symbol} Mint`;
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
name = `Unknown Mint`;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else if (type === "TokenAccount") {
|
|
206
|
+
metadata =
|
|
207
|
+
metadatas[(((_a = acc.pre.parsed) === null || _a === void 0 ? void 0 : _a.mint) || ((_b = acc.post.parsed) === null || _b === void 0 ? void 0 : _b.mint)).toBase58()];
|
|
208
|
+
if (metadata) {
|
|
209
|
+
name = `${metadata.symbol} Token Account`;
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
name = `Unknown Token Account`;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return Object.assign(Object.assign({}, acc), { name,
|
|
216
|
+
metadata });
|
|
217
|
+
});
|
|
218
|
+
writableAccounts.forEach((acc) => {
|
|
219
|
+
var _a, _b, _c, _d;
|
|
220
|
+
if (!acc.changedInSimulation) {
|
|
221
|
+
warningsByTx[index].push({
|
|
222
|
+
severity: "warning",
|
|
223
|
+
shortMessage: "Unchanged",
|
|
224
|
+
message: "Account did not change in simulation but was labeled as writable. The behavior of the transaction may differ from the simulation.",
|
|
225
|
+
account: acc.address,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
// Catch malicious sol ownwer change
|
|
229
|
+
const sysProg = new web3_js_1.PublicKey("11111111111111111111111111111111");
|
|
230
|
+
const postOwner = ((_a = acc.post.account) === null || _a === void 0 ? void 0 : _a.owner) || sysProg;
|
|
231
|
+
const preOwner = ((_b = acc.pre.account) === null || _b === void 0 ? void 0 : _b.owner) || sysProg;
|
|
232
|
+
const accountOwnerChanged = !preOwner.equals(postOwner);
|
|
233
|
+
if (acc.name === "Native SOL Account" && acc.owner && acc.owner.equals(wallet) && accountOwnerChanged) {
|
|
234
|
+
warningsByTx[index].push({
|
|
235
|
+
severity: "critical",
|
|
236
|
+
shortMessage: "Owner Changed",
|
|
237
|
+
message: `The owner of ${acc.name} changed to ${(_d = (_c = acc.post.parsed) === null || _c === void 0 ? void 0 : _c.owner) === null || _d === void 0 ? void 0 : _d.toBase58()}. This gives that wallet full custody of these tokens.`,
|
|
238
|
+
account: acc.address,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
return writableAccounts;
|
|
243
|
+
});
|
|
244
|
+
const instructionsByTx = yield Promise.all(transactions.map((transaction, index) => __awaiter(this, void 0, void 0, function* () {
|
|
245
|
+
const instructions = parseInstructions({
|
|
246
|
+
idls,
|
|
247
|
+
instructions: transaction.message.compiledInstructions.map((ix) => ({
|
|
248
|
+
data: Buffer.from(ix.data),
|
|
249
|
+
programId: accountKeysByTx[index].get(ix.programIdIndex),
|
|
250
|
+
accounts: ix.accountKeyIndexes.map((ix) => ({
|
|
251
|
+
pubkey: accountKeysByTx[index].get(ix),
|
|
252
|
+
isSigner: transaction.message.isAccountSigner(ix),
|
|
253
|
+
isWritable: transaction.message.isAccountWritable(ix),
|
|
254
|
+
})),
|
|
255
|
+
})),
|
|
256
|
+
});
|
|
257
|
+
if (instructions.some((ix) => { var _a; return ((_a = ix.parsed) === null || _a === void 0 ? void 0 : _a.name) === "ledgerTransferPositionV0"; })) {
|
|
258
|
+
warningsByTx[index].push({
|
|
259
|
+
severity: "critical",
|
|
260
|
+
shortMessage: "Theft of Locked HNT",
|
|
261
|
+
message: "This transaction is attempting to steal your locked HNT positions",
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
if (instructions.some((ix) => {
|
|
265
|
+
var _a, _b;
|
|
266
|
+
return ((_a = ix.parsed) === null || _a === void 0 ? void 0 : _a.name) === "updateDestinationV0" ||
|
|
267
|
+
((_b = ix.parsed) === null || _b === void 0 ? void 0 : _b.name) === "updateCompressionDestinationV0";
|
|
268
|
+
})) {
|
|
269
|
+
warningsByTx[index].push({
|
|
270
|
+
severity: "warning",
|
|
271
|
+
shortMessage: "Rewards Destination Changed",
|
|
272
|
+
message: "This transaction will change the destination wallet of your mining rewards",
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
if ((yield Promise.all(instructions.map((ix) => isBurnHotspot(connection, ix, assets)))).some((isBurn) => isBurn)) {
|
|
276
|
+
warningsByTx[index].push({
|
|
277
|
+
severity: "critical",
|
|
278
|
+
shortMessage: "Hotspot Destroyed",
|
|
279
|
+
message: "This transaction will brick your Hotspot!",
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return instructions;
|
|
283
|
+
})));
|
|
284
|
+
const results = [];
|
|
285
|
+
for (const [index, simulatedTxn] of simulatedTxs.entries()) {
|
|
286
|
+
const warnings = warningsByTx[index];
|
|
287
|
+
const instructions = instructionsByTx[index];
|
|
288
|
+
const writableAccounts = writableAccountsByTx[index];
|
|
289
|
+
const transaction = transactions[index];
|
|
290
|
+
const message = Buffer.from(transaction.message.serialize()).toString("base64");
|
|
291
|
+
const explorerLink = `https://explorer.solana.com/tx/inspector?cluster=${cluster}&message=${encodeURIComponent(message)}`;
|
|
292
|
+
const logs = simulatedTxn.value.logs;
|
|
293
|
+
let result;
|
|
294
|
+
if (simulatedTxn === null || simulatedTxn === void 0 ? void 0 : simulatedTxn.value.err) {
|
|
295
|
+
warnings.push({
|
|
296
|
+
severity: "critical",
|
|
297
|
+
shortMessage: "Simulation Failed",
|
|
298
|
+
message: "Transaction failed in simulation",
|
|
299
|
+
});
|
|
300
|
+
result = {
|
|
301
|
+
instructions,
|
|
302
|
+
error: simulatedTxn.value.err,
|
|
303
|
+
logs,
|
|
304
|
+
solFee: 0,
|
|
305
|
+
priorityFee: 0,
|
|
306
|
+
insufficientFunds: isInsufficientBal(simulatedTxn === null || simulatedTxn === void 0 ? void 0 : simulatedTxn.value.err),
|
|
307
|
+
explorerLink,
|
|
308
|
+
balanceChanges: [],
|
|
309
|
+
possibleCNftChanges: [],
|
|
310
|
+
writableAccounts,
|
|
311
|
+
rawSimulation: simulatedTxn.value,
|
|
312
|
+
warnings,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
let solFee = ((transaction === null || transaction === void 0 ? void 0 : transaction.signatures.length) || 1) * 5000;
|
|
317
|
+
let priorityFee = 0;
|
|
318
|
+
const fee = (yield (connection === null || connection === void 0 ? void 0 : connection.getFeeForMessage(transaction.message, "confirmed")))
|
|
319
|
+
.value || solFee;
|
|
320
|
+
priorityFee = fee - solFee;
|
|
321
|
+
const balanceChanges = writableAccounts
|
|
322
|
+
.map((acc) => {
|
|
323
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
324
|
+
const type = (acc.pre.type !== "Unknown" && acc.pre.type) ||
|
|
325
|
+
(acc.post.type !== "Unknown" && acc.post.type);
|
|
326
|
+
switch (type) {
|
|
327
|
+
case "TokenAccount":
|
|
328
|
+
if (((_a = acc.post.parsed) === null || _a === void 0 ? void 0 : _a.delegate) && !((_b = acc.pre.parsed) === null || _b === void 0 ? void 0 : _b.delegate)) {
|
|
329
|
+
warnings.push({
|
|
330
|
+
severity: "warning",
|
|
331
|
+
shortMessage: "Withdraw Authority Given",
|
|
332
|
+
message: `Delegation was taken on ${acc.name}. This gives permission to withdraw tokens without the owner's permission.`,
|
|
333
|
+
account: acc.address,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
if (acc.post.parsed &&
|
|
337
|
+
acc.pre.parsed &&
|
|
338
|
+
!acc.post.parsed.owner.equals(acc.pre.parsed.owner)) {
|
|
339
|
+
warnings.push({
|
|
340
|
+
severity: "warning",
|
|
341
|
+
shortMessage: "Owner Changed",
|
|
342
|
+
message: `The owner of ${acc.name} changed to ${(_d = (_c = acc.post.parsed) === null || _c === void 0 ? void 0 : _c.owner) === null || _d === void 0 ? void 0 : _d.toBase58()}. This gives that wallet full custody of these tokens.`,
|
|
343
|
+
account: acc.address,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
owner: ((_e = acc.post.parsed) === null || _e === void 0 ? void 0 : _e.owner) || ((_f = acc.pre.parsed) === null || _f === void 0 ? void 0 : _f.owner),
|
|
348
|
+
address: acc.address,
|
|
349
|
+
amount: (((_g = acc.post.parsed) === null || _g === void 0 ? void 0 : _g.amount) || BigInt(0)) -
|
|
350
|
+
(((_h = acc.pre.parsed) === null || _h === void 0 ? void 0 : _h.amount) || BigInt(0)),
|
|
351
|
+
metadata: acc.metadata,
|
|
352
|
+
};
|
|
353
|
+
case "NativeAccount":
|
|
354
|
+
return {
|
|
355
|
+
owner: acc.address,
|
|
356
|
+
address: acc.address,
|
|
357
|
+
amount: BigInt((((_j = acc.post.account) === null || _j === void 0 ? void 0 : _j.lamports) || 0) -
|
|
358
|
+
(((_k = acc.pre.account) === null || _k === void 0 ? void 0 : _k.lamports) || 0)),
|
|
359
|
+
metadata: {
|
|
360
|
+
mint: spl_token_1.NATIVE_MINT,
|
|
361
|
+
decimals: 9,
|
|
362
|
+
name: "SOL",
|
|
363
|
+
symbol: "SOL",
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
default:
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
})
|
|
370
|
+
.filter(truthy);
|
|
371
|
+
// Don't count new mints being created, as this might flag on candymachine txs
|
|
372
|
+
if (balanceChanges.filter((b) => b.owner.equals(wallet) && fetchedAccountsByAddr[b.address.toBase58()]).length >= 3) {
|
|
373
|
+
warnings.push({
|
|
374
|
+
severity: "warning",
|
|
375
|
+
shortMessage: "3+ Token Accounts",
|
|
376
|
+
message: "3 or more token accounts are impacted by this transaction. Any token account listed as writable can be emptied by the transaction, is this okay?",
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
let possibleCNftChanges = [];
|
|
380
|
+
if (checkCNfts) {
|
|
381
|
+
const possibleMerkles = new Set(writableAccounts
|
|
382
|
+
.filter((acc) => acc.name === "Merkle Tree")
|
|
383
|
+
.map((a) => a.address.toBase58()));
|
|
384
|
+
possibleCNftChanges = (assets || []).filter((item) => item.compression.tree && possibleMerkles.has(item.compression.tree));
|
|
385
|
+
}
|
|
386
|
+
result = {
|
|
387
|
+
instructions,
|
|
388
|
+
logs,
|
|
389
|
+
solFee,
|
|
390
|
+
priorityFee,
|
|
391
|
+
insufficientFunds: false,
|
|
392
|
+
explorerLink,
|
|
393
|
+
balanceChanges,
|
|
394
|
+
possibleCNftChanges,
|
|
395
|
+
writableAccounts,
|
|
396
|
+
rawSimulation: simulatedTxn.value,
|
|
397
|
+
warnings,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
results.push(result);
|
|
401
|
+
}
|
|
402
|
+
return results;
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
exports.sus = sus;
|
|
406
|
+
function isBlockhashNotFound(simulatedTxn) {
|
|
407
|
+
var _a;
|
|
408
|
+
return ((_a = simulatedTxn === null || simulatedTxn === void 0 ? void 0 : simulatedTxn.value.err) === null || _a === void 0 ? void 0 : _a.toString()) === "BlockhashNotFound";
|
|
409
|
+
}
|
|
410
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
411
|
+
function isInsufficientBal(e) {
|
|
412
|
+
var _a, _b;
|
|
413
|
+
return (e.toString().includes("Insufficient Balance") ||
|
|
414
|
+
e.toString().includes('"Custom":1') ||
|
|
415
|
+
((_b = (_a = e.InstructionError) === null || _a === void 0 ? void 0 : _a[1]) === null || _b === void 0 ? void 0 : _b.Custom) === 1);
|
|
416
|
+
}
|
|
417
|
+
exports.isInsufficientBal = isInsufficientBal;
|
|
418
|
+
function parseInstructions({ idls, instructions, }) {
|
|
419
|
+
return instructions.map((ix) => {
|
|
420
|
+
const idl = idls[ix.programId.toBase58()];
|
|
421
|
+
if (idl) {
|
|
422
|
+
try {
|
|
423
|
+
const coder = new anchor_1.BorshInstructionCoder(idl);
|
|
424
|
+
const parsed = coder.decode(ix.data, "base58");
|
|
425
|
+
if (parsed) {
|
|
426
|
+
const formatted = coder.format(parsed, ix.accounts);
|
|
427
|
+
if (formatted) {
|
|
428
|
+
return {
|
|
429
|
+
parsed: {
|
|
430
|
+
name: parsed.name,
|
|
431
|
+
programName: idl.name,
|
|
432
|
+
data: parsed.data,
|
|
433
|
+
accounts: formatted.accounts,
|
|
434
|
+
},
|
|
435
|
+
raw: ix,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
catch (e) {
|
|
441
|
+
// Ignore, not a valid ix
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return { raw: ix };
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
function getIdlKey(programId) {
|
|
448
|
+
const base = web3_js_1.PublicKey.findProgramAddressSync([], programId)[0];
|
|
449
|
+
const buffer = Buffer.concat([
|
|
450
|
+
base.toBuffer(),
|
|
451
|
+
Buffer.from("anchor:idl"),
|
|
452
|
+
programId.toBuffer(),
|
|
453
|
+
]);
|
|
454
|
+
const publicKeyBytes = (0, sha256_1.sha256)(buffer);
|
|
455
|
+
return new web3_js_1.PublicKey(publicKeyBytes);
|
|
456
|
+
}
|
|
457
|
+
function decodeIdl(account) {
|
|
458
|
+
try {
|
|
459
|
+
const idlData = (0, idl_1.decodeIdlAccount)(Buffer.from(account.data.subarray(8)));
|
|
460
|
+
const inflatedIdl = (0, pako_1.inflate)(idlData.data);
|
|
461
|
+
return JSON.parse(bytes_1.utf8.decode(inflatedIdl));
|
|
462
|
+
}
|
|
463
|
+
catch (e) {
|
|
464
|
+
// Ignore, not a valid IDL
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function getDetailedWritableAccountsWithoutTM({ accounts, idls, }) {
|
|
468
|
+
const uniqueTokens = new Set();
|
|
469
|
+
const withoutMetadata = accounts.map(({ address, pre, post }) => {
|
|
470
|
+
let name = "Unknown";
|
|
471
|
+
let type = "Unknown";
|
|
472
|
+
let preParsed = null;
|
|
473
|
+
let postParsed = null;
|
|
474
|
+
let accountOwner = undefined;
|
|
475
|
+
const postData = post && post.data;
|
|
476
|
+
const postAccount = post && postData
|
|
477
|
+
? {
|
|
478
|
+
executable: post.executable,
|
|
479
|
+
owner: new web3_js_1.PublicKey(post.owner),
|
|
480
|
+
lamports: post.lamports,
|
|
481
|
+
data: postData,
|
|
482
|
+
rentEpoch: post.rentEpoch,
|
|
483
|
+
}
|
|
484
|
+
: null;
|
|
485
|
+
const owner = (pre === null || pre === void 0 ? void 0 : pre.owner) || (post ? new web3_js_1.PublicKey(post.owner) : null);
|
|
486
|
+
switch (owner === null || owner === void 0 ? void 0 : owner.toBase58()) {
|
|
487
|
+
case ACCOUNT_COMPRESSION_PROGRAM_ID.toBase58():
|
|
488
|
+
name = "Merkle Tree";
|
|
489
|
+
type = "MerkleTree";
|
|
490
|
+
break;
|
|
491
|
+
case web3_js_1.SystemProgram.programId.toBase58():
|
|
492
|
+
name = "Native SOL Account";
|
|
493
|
+
type = "NativeAccount";
|
|
494
|
+
accountOwner = address;
|
|
495
|
+
break;
|
|
496
|
+
case spl_token_1.TOKEN_2022_PROGRAM_ID.toBase58():
|
|
497
|
+
({ parsed: preParsed, type } = decodeTokenStruct(address, pre, spl_token_1.TOKEN_2022_PROGRAM_ID) || { type });
|
|
498
|
+
({ parsed: postParsed, type } = decodeTokenStruct(address, postAccount, spl_token_1.TOKEN_2022_PROGRAM_ID) || { type });
|
|
499
|
+
break;
|
|
500
|
+
case spl_token_1.TOKEN_PROGRAM_ID.toBase58():
|
|
501
|
+
({ parsed: preParsed, type } = decodeTokenStruct(address, pre, spl_token_1.TOKEN_PROGRAM_ID) || { type });
|
|
502
|
+
({ parsed: postParsed, type } = decodeTokenStruct(address, postAccount, spl_token_1.TOKEN_PROGRAM_ID) || { type });
|
|
503
|
+
break;
|
|
504
|
+
default:
|
|
505
|
+
if (owner) {
|
|
506
|
+
const idl = idls[owner.toBase58()];
|
|
507
|
+
if (idl) {
|
|
508
|
+
const decodedPre = decodeIdlStruct(idl, pre);
|
|
509
|
+
const decodedPost = decodeIdlStruct(idl, postAccount);
|
|
510
|
+
preParsed = decodedPre === null || decodedPre === void 0 ? void 0 : decodedPre.parsed;
|
|
511
|
+
postParsed = decodedPost === null || decodedPost === void 0 ? void 0 : decodedPost.parsed;
|
|
512
|
+
type =
|
|
513
|
+
((decodedPre === null || decodedPre === void 0 ? void 0 : decodedPre.type) !== "Unknown" && (decodedPre === null || decodedPre === void 0 ? void 0 : decodedPre.type)) ||
|
|
514
|
+
((decodedPost === null || decodedPost === void 0 ? void 0 : decodedPost.type) !== "Unknown" && (decodedPost === null || decodedPost === void 0 ? void 0 : decodedPost.type)) ||
|
|
515
|
+
"Unknown";
|
|
516
|
+
name = type;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
// If token, get the name based on the metadata
|
|
522
|
+
if (new Set([
|
|
523
|
+
spl_token_1.TOKEN_2022_PROGRAM_ID.toBase58(),
|
|
524
|
+
spl_token_1.TOKEN_PROGRAM_ID.toBase58(),
|
|
525
|
+
]).has((owner === null || owner === void 0 ? void 0 : owner.toBase58()) || "")) {
|
|
526
|
+
if (type === "Mint") {
|
|
527
|
+
uniqueTokens.add(address.toBase58());
|
|
528
|
+
}
|
|
529
|
+
else if (type === "TokenAccount") {
|
|
530
|
+
const mint = ((preParsed === null || preParsed === void 0 ? void 0 : preParsed.mint) || (postParsed === null || postParsed === void 0 ? void 0 : postParsed.mint)).toBase58();
|
|
531
|
+
accountOwner = (preParsed === null || preParsed === void 0 ? void 0 : preParsed.owner) || (postParsed === null || postParsed === void 0 ? void 0 : postParsed.owner);
|
|
532
|
+
uniqueTokens.add(mint);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
address,
|
|
537
|
+
name,
|
|
538
|
+
owner: accountOwner,
|
|
539
|
+
pre: {
|
|
540
|
+
type,
|
|
541
|
+
account: pre || null,
|
|
542
|
+
parsed: preParsed,
|
|
543
|
+
},
|
|
544
|
+
post: {
|
|
545
|
+
type,
|
|
546
|
+
account: post || null,
|
|
547
|
+
parsed: postParsed,
|
|
548
|
+
},
|
|
549
|
+
changedInSimulation: pre && postData
|
|
550
|
+
? !pre.data.equals(postData) ||
|
|
551
|
+
pre.lamports != post.lamports ||
|
|
552
|
+
!pre.owner.equals(new web3_js_1.PublicKey(post.owner))
|
|
553
|
+
: true,
|
|
554
|
+
};
|
|
555
|
+
});
|
|
556
|
+
const tokens = [...uniqueTokens].map((t) => new web3_js_1.PublicKey(t));
|
|
557
|
+
return {
|
|
558
|
+
withoutMetadata,
|
|
559
|
+
tokens,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
exports.getDetailedWritableAccountsWithoutTM = getDetailedWritableAccountsWithoutTM;
|
|
563
|
+
function decodeIdlStruct(idl, account) {
|
|
564
|
+
var _a, _b;
|
|
565
|
+
if (!account) {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
try {
|
|
569
|
+
const coder = new anchor_1.BorshAccountsCoder(idl);
|
|
570
|
+
const descriminator = account.data.slice(0, 8);
|
|
571
|
+
const type = (_b = (_a = idl.accounts) === null || _a === void 0 ? void 0 : _a.find((account) => anchor_1.BorshAccountsCoder.accountDiscriminator(account.name).equals(descriminator))) === null || _b === void 0 ? void 0 : _b.name;
|
|
572
|
+
if (type) {
|
|
573
|
+
return {
|
|
574
|
+
type,
|
|
575
|
+
parsed: coder.decode(type, account.data),
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
catch (e) {
|
|
580
|
+
// Ignore, not a valid account
|
|
581
|
+
}
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
function decodeTokenStruct(address, account, programId) {
|
|
585
|
+
if (!account) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
try {
|
|
589
|
+
return {
|
|
590
|
+
type: "TokenAccount",
|
|
591
|
+
parsed: (0, spl_token_1.unpackAccount)(address, account, programId),
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
catch (e) {
|
|
595
|
+
// Not an account
|
|
596
|
+
}
|
|
597
|
+
try {
|
|
598
|
+
return {
|
|
599
|
+
type: "Mint",
|
|
600
|
+
parsed: (0, spl_token_1.unpackMint)(address, account, programId),
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
catch (e) {
|
|
604
|
+
// Not an account
|
|
605
|
+
}
|
|
606
|
+
try {
|
|
607
|
+
return {
|
|
608
|
+
type: "Multisig",
|
|
609
|
+
parsed: (0, spl_token_1.unpackMultisig)(address, account, programId),
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
catch (e) {
|
|
613
|
+
// Not an account
|
|
614
|
+
}
|
|
615
|
+
return null;
|
|
616
|
+
}
|
|
617
|
+
function getMetadataId(mint) {
|
|
618
|
+
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("metadata", "utf-8"), mpl_token_metadata_1.PROGRAM_ID.toBuffer(), mint.toBuffer()], mpl_token_metadata_1.PROGRAM_ID)[0];
|
|
619
|
+
}
|
|
620
|
+
exports.getMetadataId = getMetadataId;
|
|
621
|
+
function fetchMetadatas(connection, tokens) {
|
|
622
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
623
|
+
const metadatas = tokens.map(getMetadataId);
|
|
624
|
+
const all = yield getMultipleAccounts({
|
|
625
|
+
keys: [...metadatas, ...tokens],
|
|
626
|
+
connection,
|
|
627
|
+
});
|
|
628
|
+
const metadataAccounts = all.slice(0, metadatas.length);
|
|
629
|
+
const mintAccounts = all.slice(metadatas.length, metadatas.length * 2);
|
|
630
|
+
return metadataAccounts.map((acc, index) => {
|
|
631
|
+
try {
|
|
632
|
+
const mint = (0, spl_token_1.unpackMint)(tokens[index], mintAccounts[index]);
|
|
633
|
+
if (acc) {
|
|
634
|
+
const collectable = mpl_token_metadata_1.Metadata.fromAccountInfo(acc)[0];
|
|
635
|
+
return {
|
|
636
|
+
name: collectable.data.name.replace(/\0/g, ""),
|
|
637
|
+
symbol: collectable.data.symbol.replace(/\0/g, ""),
|
|
638
|
+
uri: collectable.data.uri.replace(/\0/g, ""),
|
|
639
|
+
decimals: mint.decimals,
|
|
640
|
+
mint: tokens[index],
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
mint: tokens[index],
|
|
645
|
+
decimals: mint.decimals,
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
catch (e) {
|
|
649
|
+
// Ignore, not a valid mint
|
|
650
|
+
}
|
|
651
|
+
return null;
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
exports.fetchMetadatas = fetchMetadatas;
|
|
656
|
+
const truthy = (value) => !!value;
|
|
657
|
+
function isBurnHotspot(connection, ix, assets) {
|
|
658
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
659
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
660
|
+
if (ix.raw.programId.equals(BUBBLEGUM_PROGRAM_ID) &&
|
|
661
|
+
((_a = ix.parsed) === null || _a === void 0 ? void 0 : _a.name) === "burn") {
|
|
662
|
+
const tree = (_c = (_b = ix.parsed) === null || _b === void 0 ? void 0 : _b.accounts.find((acc) => acc.name === "Merkle Tree")) === null || _c === void 0 ? void 0 : _c.pubkey;
|
|
663
|
+
if (tree) {
|
|
664
|
+
const index = (_e = (_d = ix.parsed) === null || _d === void 0 ? void 0 : _d.data) === null || _e === void 0 ? void 0 : _e.index;
|
|
665
|
+
const assetId = yield (0, mpl_bubblegum_1.getLeafAssetId)(tree, new anchor_1.BN(index));
|
|
666
|
+
let asset;
|
|
667
|
+
if (assets) {
|
|
668
|
+
asset = assets.find(a => a.id === assetId.toBase58());
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
const assetResponse = yield axios_1.default.post(connection.rpcEndpoint, {
|
|
672
|
+
jsonrpc: "2.0",
|
|
673
|
+
method: "getAsset",
|
|
674
|
+
id: "get-asset-op-1",
|
|
675
|
+
params: {
|
|
676
|
+
id: assetId.toBase58(),
|
|
677
|
+
},
|
|
678
|
+
headers: {
|
|
679
|
+
"Cache-Control": "no-cache",
|
|
680
|
+
Pragma: "no-cache",
|
|
681
|
+
Expires: "0",
|
|
682
|
+
},
|
|
683
|
+
});
|
|
684
|
+
asset = assetResponse.data.result;
|
|
685
|
+
}
|
|
686
|
+
return (asset &&
|
|
687
|
+
((_f = asset.creators[0]) === null || _f === void 0 ? void 0 : _f.address) == HELIUM_ENTITY_CREATOR.toBase58() &&
|
|
688
|
+
((_g = asset.creators[0]) === null || _g === void 0 ? void 0 : _g.verified));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return false;
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
const DAO = web3_js_1.PublicKey.findProgramAddressSync([
|
|
695
|
+
Buffer.from("dao", "utf-8"),
|
|
696
|
+
new web3_js_1.PublicKey("hntyVP6YFm1Hg25TN9WGLqM12b8TQmcknKrdu1oxWux").toBuffer(),
|
|
697
|
+
], new web3_js_1.PublicKey("hdaoVTCqhfHHo75XdAMxBKdUqvq1i5bF23sisBqVgGR"))[0];
|
|
698
|
+
const HELIUM_ENTITY_CREATOR = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("entity_creator", "utf-8"), DAO.toBuffer()], new web3_js_1.PublicKey("hemjuPXBpNvggtaUnN1MwT3wrdhttKEfosTcc2P9Pg8"))[0];
|
|
699
|
+
//# sourceMappingURL=index.js.map
|