tx-indexer 0.4.0 → 0.5.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/README.md +36 -3
- package/dist/{classification.types-DlJe6bDZ.d.ts → classification.types-Cn9IGtEC.d.ts} +13 -12
- package/dist/{client-yGDWPKKf.d.ts → client-iLW2_DnL.d.ts} +39 -3
- package/dist/client.d.ts +2 -2
- package/dist/client.js +140 -14
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +141 -15
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,8 +28,42 @@ const txs = await indexer.getTransactions("YourWalletAddress...", {
|
|
|
28
28
|
filterSpam: true
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
// Get single transaction
|
|
32
|
-
const tx = await indexer.getTransaction("signature..."
|
|
31
|
+
// Get single transaction (no wallet required)
|
|
32
|
+
const tx = await indexer.getTransaction("signature...");
|
|
33
|
+
|
|
34
|
+
// Classification includes sender/receiver
|
|
35
|
+
console.log(tx.classification.primaryType); // "transfer", "swap", "nft_mint", etc.
|
|
36
|
+
console.log(tx.classification.sender); // sender address
|
|
37
|
+
console.log(tx.classification.receiver); // receiver address
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Transaction Types
|
|
41
|
+
|
|
42
|
+
- `transfer` - Wallet-to-wallet transfers
|
|
43
|
+
- `swap` - Token exchanges (pattern-based detection works with any DEX, higher confidence for known protocols like Jupiter, Raydium, Orca)
|
|
44
|
+
- `nft_mint` - NFT minting (Metaplex, Candy Machine, Bubblegum)
|
|
45
|
+
- `stake_deposit` - SOL staking deposits
|
|
46
|
+
- `stake_withdraw` - SOL staking withdrawals
|
|
47
|
+
- `bridge_in` - Receiving from bridge (Wormhole, deBridge, Allbridge)
|
|
48
|
+
- `bridge_out` - Sending to bridge
|
|
49
|
+
- `airdrop` - Token distributions
|
|
50
|
+
- `fee_only` - Transactions with only network fees
|
|
51
|
+
|
|
52
|
+
## Frontend Integration
|
|
53
|
+
|
|
54
|
+
Classification is wallet-agnostic. Determine perspective in your frontend:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
const { classification } = await indexer.getTransaction(signature);
|
|
58
|
+
const connectedWallet = wallet?.address;
|
|
59
|
+
|
|
60
|
+
if (connectedWallet === classification.sender) {
|
|
61
|
+
// "You sent..."
|
|
62
|
+
} else if (connectedWallet === classification.receiver) {
|
|
63
|
+
// "You received..."
|
|
64
|
+
} else {
|
|
65
|
+
// "Address X sent to Address Y"
|
|
66
|
+
}
|
|
33
67
|
```
|
|
34
68
|
|
|
35
69
|
## Bundle Size
|
|
@@ -78,4 +112,3 @@ bun run size:why
|
|
|
78
112
|
## License
|
|
79
113
|
|
|
80
114
|
MIT
|
|
81
|
-
|
|
@@ -8,11 +8,11 @@ declare const TxDirectionSchema: z.ZodEnum<{
|
|
|
8
8
|
neutral: "neutral";
|
|
9
9
|
}>;
|
|
10
10
|
declare const TxPrimaryTypeSchema: z.ZodEnum<{
|
|
11
|
-
|
|
12
|
-
swap: "swap";
|
|
11
|
+
nft_mint: "nft_mint";
|
|
13
12
|
nft_purchase: "nft_purchase";
|
|
14
13
|
nft_sale: "nft_sale";
|
|
15
|
-
|
|
14
|
+
transfer: "transfer";
|
|
15
|
+
swap: "swap";
|
|
16
16
|
stake_deposit: "stake_deposit";
|
|
17
17
|
stake_withdraw: "stake_withdraw";
|
|
18
18
|
token_deposit: "token_deposit";
|
|
@@ -50,6 +50,7 @@ declare const RawTransactionSchema: z.ZodObject<{
|
|
|
50
50
|
signature: z.ZodCustom<Signature, Signature>;
|
|
51
51
|
slot: z.ZodUnion<readonly [z.ZodNumber, z.ZodBigInt]>;
|
|
52
52
|
blockTime: z.ZodNullable<z.ZodUnion<readonly [z.ZodNumber, z.ZodBigInt]>>;
|
|
53
|
+
fee: z.ZodOptional<z.ZodNumber>;
|
|
53
54
|
err: z.ZodNullable<z.ZodAny>;
|
|
54
55
|
programIds: z.ZodArray<z.ZodString>;
|
|
55
56
|
protocol: z.ZodNullable<z.ZodObject<{
|
|
@@ -91,11 +92,11 @@ declare const TxLegSideSchema: z.ZodEnum<{
|
|
|
91
92
|
credit: "credit";
|
|
92
93
|
}>;
|
|
93
94
|
declare const TxLegRoleSchema: z.ZodEnum<{
|
|
94
|
-
unknown: "unknown";
|
|
95
95
|
reward: "reward";
|
|
96
|
+
unknown: "unknown";
|
|
97
|
+
fee: "fee";
|
|
96
98
|
sent: "sent";
|
|
97
99
|
received: "received";
|
|
98
|
-
fee: "fee";
|
|
99
100
|
protocol_deposit: "protocol_deposit";
|
|
100
101
|
protocol_withdraw: "protocol_withdraw";
|
|
101
102
|
principal: "principal";
|
|
@@ -143,11 +144,11 @@ declare const TxLegSchema: z.ZodObject<{
|
|
|
143
144
|
} | undefined;
|
|
144
145
|
}>;
|
|
145
146
|
role: z.ZodEnum<{
|
|
146
|
-
unknown: "unknown";
|
|
147
147
|
reward: "reward";
|
|
148
|
+
unknown: "unknown";
|
|
149
|
+
fee: "fee";
|
|
148
150
|
sent: "sent";
|
|
149
151
|
received: "received";
|
|
150
|
-
fee: "fee";
|
|
151
152
|
protocol_deposit: "protocol_deposit";
|
|
152
153
|
protocol_withdraw: "protocol_withdraw";
|
|
153
154
|
principal: "principal";
|
|
@@ -165,11 +166,11 @@ type TxLeg = z.infer<typeof TxLegSchema>;
|
|
|
165
166
|
|
|
166
167
|
declare const TransactionClassificationSchema: z.ZodObject<{
|
|
167
168
|
primaryType: z.ZodEnum<{
|
|
168
|
-
|
|
169
|
-
swap: "swap";
|
|
169
|
+
nft_mint: "nft_mint";
|
|
170
170
|
nft_purchase: "nft_purchase";
|
|
171
171
|
nft_sale: "nft_sale";
|
|
172
|
-
|
|
172
|
+
transfer: "transfer";
|
|
173
|
+
swap: "swap";
|
|
173
174
|
stake_deposit: "stake_deposit";
|
|
174
175
|
stake_withdraw: "stake_withdraw";
|
|
175
176
|
token_deposit: "token_deposit";
|
|
@@ -187,12 +188,12 @@ declare const TransactionClassificationSchema: z.ZodObject<{
|
|
|
187
188
|
receiver: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
188
189
|
counterparty: z.ZodNullable<z.ZodObject<{
|
|
189
190
|
type: z.ZodEnum<{
|
|
190
|
-
unknown: "unknown";
|
|
191
|
-
protocol: "protocol";
|
|
192
191
|
person: "person";
|
|
193
192
|
merchant: "merchant";
|
|
194
193
|
exchange: "exchange";
|
|
194
|
+
protocol: "protocol";
|
|
195
195
|
own_wallet: "own_wallet";
|
|
196
|
+
unknown: "unknown";
|
|
196
197
|
}>;
|
|
197
198
|
address: z.ZodString;
|
|
198
199
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Rpc, GetBalanceApi, GetTokenAccountsByOwnerApi, GetSignaturesForAddressApi, GetTransactionApi, RpcSubscriptions, Address, Signature } from '@solana/kit';
|
|
2
|
-
import { R as RawTransaction, T as TxLeg, a as TransactionClassification } from './classification.types-
|
|
2
|
+
import { R as RawTransaction, T as TxLeg, a as TransactionClassification } from './classification.types-Cn9IGtEC.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Union type of all RPC APIs used by the transaction indexer.
|
|
@@ -152,6 +152,36 @@ interface TokenAccountBalance {
|
|
|
152
152
|
*/
|
|
153
153
|
declare function fetchWalletBalance(rpc: Rpc<GetBalanceApi & GetTokenAccountsByOwnerApi>, walletAddress: Address, tokenMints?: readonly string[]): Promise<WalletBalance>;
|
|
154
154
|
|
|
155
|
+
interface NftMetadata {
|
|
156
|
+
mint: string;
|
|
157
|
+
name: string;
|
|
158
|
+
symbol: string;
|
|
159
|
+
image: string;
|
|
160
|
+
cdnImage?: string;
|
|
161
|
+
description?: string;
|
|
162
|
+
collection?: string;
|
|
163
|
+
attributes?: Array<{
|
|
164
|
+
trait_type: string;
|
|
165
|
+
value: string;
|
|
166
|
+
}>;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Fetches NFT metadata from Helius DAS API.
|
|
170
|
+
*
|
|
171
|
+
* @param rpcUrl - Helius RPC endpoint URL
|
|
172
|
+
* @param mintAddress - NFT mint address
|
|
173
|
+
* @returns NFT metadata including name, image, collection, and attributes, or null if not found
|
|
174
|
+
*/
|
|
175
|
+
declare function fetchNftMetadata(rpcUrl: string, mintAddress: string): Promise<NftMetadata | null>;
|
|
176
|
+
/**
|
|
177
|
+
* Fetches NFT metadata for multiple mints in parallel.
|
|
178
|
+
*
|
|
179
|
+
* @param rpcUrl - Helius RPC endpoint URL
|
|
180
|
+
* @param mintAddresses - Array of NFT mint addresses
|
|
181
|
+
* @returns Map of mint address to NFT metadata (only includes successful fetches)
|
|
182
|
+
*/
|
|
183
|
+
declare function fetchNftMetadataBatch(rpcUrl: string, mintAddresses: string[]): Promise<Map<string, NftMetadata>>;
|
|
184
|
+
|
|
155
185
|
type TxIndexerOptions = {
|
|
156
186
|
rpcUrl: string;
|
|
157
187
|
wsUrl?: string;
|
|
@@ -164,6 +194,10 @@ interface GetTransactionsOptions {
|
|
|
164
194
|
until?: Signature;
|
|
165
195
|
filterSpam?: boolean;
|
|
166
196
|
spamConfig?: SpamFilterConfig;
|
|
197
|
+
enrichNftMetadata?: boolean;
|
|
198
|
+
}
|
|
199
|
+
interface GetTransactionOptions {
|
|
200
|
+
enrichNftMetadata?: boolean;
|
|
167
201
|
}
|
|
168
202
|
interface ClassifiedTransaction {
|
|
169
203
|
tx: RawTransaction;
|
|
@@ -174,9 +208,11 @@ interface TxIndexer {
|
|
|
174
208
|
rpc: ReturnType<typeof createSolanaClient>["rpc"];
|
|
175
209
|
getBalance(walletAddress: Address, tokenMints?: readonly string[]): Promise<WalletBalance>;
|
|
176
210
|
getTransactions(walletAddress: Address, options?: GetTransactionsOptions): Promise<ClassifiedTransaction[]>;
|
|
177
|
-
getTransaction(signature: Signature): Promise<ClassifiedTransaction | null>;
|
|
211
|
+
getTransaction(signature: Signature, options?: GetTransactionOptions): Promise<ClassifiedTransaction | null>;
|
|
178
212
|
getRawTransaction(signature: Signature): Promise<RawTransaction | null>;
|
|
213
|
+
getNftMetadata(mintAddress: string): Promise<NftMetadata | null>;
|
|
214
|
+
getNftMetadataBatch(mintAddresses: string[]): Promise<Map<string, NftMetadata>>;
|
|
179
215
|
}
|
|
180
216
|
declare function createIndexer(options: TxIndexerOptions): TxIndexer;
|
|
181
217
|
|
|
182
|
-
export { type ClassifiedTransaction as C, type FetchTransactionsConfig as F, type GetTransactionsOptions as G, type IndexerRpcApi as I, type SolanaClient as S, type TxIndexer as T, type WalletBalance as W, type TxIndexerOptions as a,
|
|
218
|
+
export { type ClassifiedTransaction as C, type FetchTransactionsConfig as F, type GetTransactionsOptions as G, type IndexerRpcApi as I, type NftMetadata as N, type SolanaClient as S, type TxIndexer as T, type WalletBalance as W, type TxIndexerOptions as a, type GetTransactionOptions as b, createIndexer as c, createSolanaClient as d, parseSignature as e, fetchWalletBalance as f, type TokenAccountBalance as g, fetchWalletSignatures as h, fetchTransaction as i, fetchTransactionsBatch as j, isSpamTransaction as k, filterSpamTransactions as l, type SpamFilterConfig as m, fetchNftMetadata as n, fetchNftMetadataBatch as o, parseAddress as p, transactionToLegs as t };
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import '@solana/kit';
|
|
2
|
-
export { C as ClassifiedTransaction, F as FetchTransactionsConfig, G as GetTransactionsOptions, T as TxIndexer, a as TxIndexerOptions, c as createIndexer } from './client-
|
|
3
|
-
import './classification.types-
|
|
2
|
+
export { C as ClassifiedTransaction, F as FetchTransactionsConfig, b as GetTransactionOptions, G as GetTransactionsOptions, T as TxIndexer, a as TxIndexerOptions, c as createIndexer } from './client-iLW2_DnL.js';
|
|
3
|
+
import './classification.types-Cn9IGtEC.js';
|
|
4
4
|
import 'zod';
|
package/dist/client.js
CHANGED
|
@@ -416,6 +416,7 @@ async function fetchTransaction(rpc, signature2, commitment = "confirmed") {
|
|
|
416
416
|
signature: signature2,
|
|
417
417
|
slot: response.slot,
|
|
418
418
|
blockTime: response.blockTime,
|
|
419
|
+
fee: Number(response.meta?.fee ?? 0),
|
|
419
420
|
err: response.meta?.err ?? null,
|
|
420
421
|
programIds: extractProgramIds(response.transaction),
|
|
421
422
|
protocol: null,
|
|
@@ -705,7 +706,7 @@ var TransferClassifier = class {
|
|
|
705
706
|
sender,
|
|
706
707
|
receiver,
|
|
707
708
|
counterparty: {
|
|
708
|
-
type: "
|
|
709
|
+
type: "unknown",
|
|
709
710
|
address: receiver,
|
|
710
711
|
name: `${receiver.slice(0, 8)}...`
|
|
711
712
|
},
|
|
@@ -876,19 +877,19 @@ var SwapClassifier = class {
|
|
|
876
877
|
priority = 80;
|
|
877
878
|
classify(context) {
|
|
878
879
|
const { legs, tx } = context;
|
|
879
|
-
const hasDexProtocol = isDexProtocolById(tx.protocol?.id);
|
|
880
|
-
if (!hasDexProtocol) {
|
|
881
|
-
return null;
|
|
882
|
-
}
|
|
883
880
|
const feeLeg = legs.find(
|
|
884
881
|
(leg) => leg.role === "fee" && leg.side === "debit"
|
|
885
882
|
);
|
|
886
883
|
const initiator = feeLeg?.accountId.replace("external:", "") ?? null;
|
|
884
|
+
if (!initiator) {
|
|
885
|
+
return null;
|
|
886
|
+
}
|
|
887
|
+
const initiatorAccountId = `external:${initiator}`;
|
|
887
888
|
const tokensOut = legs.filter(
|
|
888
|
-
(leg) => leg.accountId
|
|
889
|
+
(leg) => leg.accountId === initiatorAccountId && leg.side === "debit" && (leg.role === "sent" || leg.role === "protocol_deposit")
|
|
889
890
|
);
|
|
890
891
|
const tokensIn = legs.filter(
|
|
891
|
-
(leg) => leg.accountId
|
|
892
|
+
(leg) => leg.accountId === initiatorAccountId && leg.side === "credit" && (leg.role === "received" || leg.role === "protocol_withdraw")
|
|
892
893
|
);
|
|
893
894
|
if (tokensOut.length === 0 || tokensIn.length === 0) {
|
|
894
895
|
return null;
|
|
@@ -898,6 +899,8 @@ var SwapClassifier = class {
|
|
|
898
899
|
if (tokenOut.amount.token.symbol === tokenIn.amount.token.symbol) {
|
|
899
900
|
return null;
|
|
900
901
|
}
|
|
902
|
+
const hasDexProtocol = isDexProtocolById(tx.protocol?.id);
|
|
903
|
+
const confidence = hasDexProtocol ? 0.95 : 0.75;
|
|
901
904
|
return {
|
|
902
905
|
primaryType: "swap",
|
|
903
906
|
primaryAmount: tokenOut.amount,
|
|
@@ -905,7 +908,7 @@ var SwapClassifier = class {
|
|
|
905
908
|
sender: initiator,
|
|
906
909
|
receiver: initiator,
|
|
907
910
|
counterparty: null,
|
|
908
|
-
confidence
|
|
911
|
+
confidence,
|
|
909
912
|
isRelevant: true,
|
|
910
913
|
metadata: {
|
|
911
914
|
swap_type: "token_to_token",
|
|
@@ -1037,7 +1040,7 @@ var SolanaPayClassifier = class {
|
|
|
1037
1040
|
counterparty: receiver ? {
|
|
1038
1041
|
address: receiver,
|
|
1039
1042
|
name: memo.merchant ?? void 0,
|
|
1040
|
-
type: memo.merchant ? "merchant" : "
|
|
1043
|
+
type: memo.merchant ? "merchant" : "unknown"
|
|
1041
1044
|
} : null,
|
|
1042
1045
|
confidence: 0.98,
|
|
1043
1046
|
isRelevant: true,
|
|
@@ -1314,8 +1317,81 @@ function filterSpamTransactions(transactions, config) {
|
|
|
1314
1317
|
);
|
|
1315
1318
|
}
|
|
1316
1319
|
|
|
1320
|
+
// src/nft.ts
|
|
1321
|
+
async function fetchNftMetadata(rpcUrl, mintAddress) {
|
|
1322
|
+
const response = await fetch(rpcUrl, {
|
|
1323
|
+
method: "POST",
|
|
1324
|
+
headers: { "Content-Type": "application/json" },
|
|
1325
|
+
body: JSON.stringify({
|
|
1326
|
+
jsonrpc: "2.0",
|
|
1327
|
+
id: "get-asset",
|
|
1328
|
+
method: "getAsset",
|
|
1329
|
+
params: { id: mintAddress }
|
|
1330
|
+
})
|
|
1331
|
+
});
|
|
1332
|
+
const data = await response.json();
|
|
1333
|
+
if (data.error || !data.result?.content?.metadata) {
|
|
1334
|
+
return null;
|
|
1335
|
+
}
|
|
1336
|
+
const { result } = data;
|
|
1337
|
+
const { content, grouping } = result;
|
|
1338
|
+
return {
|
|
1339
|
+
mint: mintAddress,
|
|
1340
|
+
name: content.metadata.name,
|
|
1341
|
+
symbol: content.metadata.symbol,
|
|
1342
|
+
image: content.links.image ?? content.files?.[0]?.uri ?? "",
|
|
1343
|
+
cdnImage: content.files?.[0]?.cdn_uri,
|
|
1344
|
+
description: content.metadata.description,
|
|
1345
|
+
collection: grouping?.find((g) => g.group_key === "collection")?.group_value,
|
|
1346
|
+
attributes: content.metadata.attributes
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
async function fetchNftMetadataBatch(rpcUrl, mintAddresses) {
|
|
1350
|
+
const results = await Promise.all(
|
|
1351
|
+
mintAddresses.map((mint) => fetchNftMetadata(rpcUrl, mint))
|
|
1352
|
+
);
|
|
1353
|
+
const map = /* @__PURE__ */ new Map();
|
|
1354
|
+
results.forEach((metadata, index) => {
|
|
1355
|
+
if (metadata) {
|
|
1356
|
+
map.set(mintAddresses[index], metadata);
|
|
1357
|
+
}
|
|
1358
|
+
});
|
|
1359
|
+
return map;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1317
1362
|
// src/client.ts
|
|
1363
|
+
var NFT_TRANSACTION_TYPES = ["nft_mint", "nft_purchase", "nft_sale"];
|
|
1364
|
+
async function enrichNftClassification(rpcUrl, classified) {
|
|
1365
|
+
const { classification } = classified;
|
|
1366
|
+
if (!NFT_TRANSACTION_TYPES.includes(classification.primaryType)) {
|
|
1367
|
+
return classified;
|
|
1368
|
+
}
|
|
1369
|
+
const nftMint = classification.metadata?.nft_mint;
|
|
1370
|
+
if (!nftMint) {
|
|
1371
|
+
return classified;
|
|
1372
|
+
}
|
|
1373
|
+
const nftData = await fetchNftMetadata(rpcUrl, nftMint);
|
|
1374
|
+
if (!nftData) {
|
|
1375
|
+
return classified;
|
|
1376
|
+
}
|
|
1377
|
+
return {
|
|
1378
|
+
...classified,
|
|
1379
|
+
classification: {
|
|
1380
|
+
...classification,
|
|
1381
|
+
metadata: {
|
|
1382
|
+
...classification.metadata,
|
|
1383
|
+
nft_name: nftData.name,
|
|
1384
|
+
nft_image: nftData.image,
|
|
1385
|
+
nft_cdn_image: nftData.cdnImage,
|
|
1386
|
+
nft_collection: nftData.collection,
|
|
1387
|
+
nft_symbol: nftData.symbol,
|
|
1388
|
+
nft_attributes: nftData.attributes
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1318
1393
|
function createIndexer(options) {
|
|
1394
|
+
const rpcUrl = "client" in options ? "" : options.rpcUrl;
|
|
1319
1395
|
const client = "client" in options ? options.client : createSolanaClient(options.rpcUrl, options.wsUrl);
|
|
1320
1396
|
return {
|
|
1321
1397
|
rpc: client.rpc,
|
|
@@ -1323,7 +1399,39 @@ function createIndexer(options) {
|
|
|
1323
1399
|
return fetchWalletBalance(client.rpc, walletAddress, tokenMints);
|
|
1324
1400
|
},
|
|
1325
1401
|
async getTransactions(walletAddress, options2 = {}) {
|
|
1326
|
-
const { limit = 10, before, until, filterSpam = true, spamConfig } = options2;
|
|
1402
|
+
const { limit = 10, before, until, filterSpam = true, spamConfig, enrichNftMetadata = true } = options2;
|
|
1403
|
+
async function enrichBatch(transactions) {
|
|
1404
|
+
if (!enrichNftMetadata || !rpcUrl) {
|
|
1405
|
+
return transactions;
|
|
1406
|
+
}
|
|
1407
|
+
const nftMints = transactions.filter((t) => NFT_TRANSACTION_TYPES.includes(t.classification.primaryType)).map((t) => t.classification.metadata?.nft_mint).filter(Boolean);
|
|
1408
|
+
if (nftMints.length === 0) {
|
|
1409
|
+
return transactions;
|
|
1410
|
+
}
|
|
1411
|
+
const nftMetadataMap = await fetchNftMetadataBatch(rpcUrl, nftMints);
|
|
1412
|
+
return transactions.map((t) => {
|
|
1413
|
+
const nftMint = t.classification.metadata?.nft_mint;
|
|
1414
|
+
if (!nftMint || !nftMetadataMap.has(nftMint)) {
|
|
1415
|
+
return t;
|
|
1416
|
+
}
|
|
1417
|
+
const nftData = nftMetadataMap.get(nftMint);
|
|
1418
|
+
return {
|
|
1419
|
+
...t,
|
|
1420
|
+
classification: {
|
|
1421
|
+
...t.classification,
|
|
1422
|
+
metadata: {
|
|
1423
|
+
...t.classification.metadata,
|
|
1424
|
+
nft_name: nftData.name,
|
|
1425
|
+
nft_image: nftData.image,
|
|
1426
|
+
nft_cdn_image: nftData.cdnImage,
|
|
1427
|
+
nft_collection: nftData.collection,
|
|
1428
|
+
nft_symbol: nftData.symbol,
|
|
1429
|
+
nft_attributes: nftData.attributes
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1327
1435
|
if (!filterSpam) {
|
|
1328
1436
|
const signatures = await fetchWalletSignatures(client.rpc, walletAddress, {
|
|
1329
1437
|
limit,
|
|
@@ -1346,7 +1454,7 @@ function createIndexer(options) {
|
|
|
1346
1454
|
const classification = classifyTransaction(legs, tx);
|
|
1347
1455
|
return { tx, classification, legs };
|
|
1348
1456
|
});
|
|
1349
|
-
return classified;
|
|
1457
|
+
return enrichBatch(classified);
|
|
1350
1458
|
}
|
|
1351
1459
|
const accumulated = [];
|
|
1352
1460
|
let currentBefore = before;
|
|
@@ -1385,9 +1493,11 @@ function createIndexer(options) {
|
|
|
1385
1493
|
break;
|
|
1386
1494
|
}
|
|
1387
1495
|
}
|
|
1388
|
-
|
|
1496
|
+
const result = accumulated.slice(0, limit);
|
|
1497
|
+
return enrichBatch(result);
|
|
1389
1498
|
},
|
|
1390
|
-
async getTransaction(signature2) {
|
|
1499
|
+
async getTransaction(signature2, options2 = {}) {
|
|
1500
|
+
const { enrichNftMetadata = true } = options2;
|
|
1391
1501
|
const tx = await fetchTransaction(client.rpc, signature2);
|
|
1392
1502
|
if (!tx) {
|
|
1393
1503
|
return null;
|
|
@@ -1395,10 +1505,26 @@ function createIndexer(options) {
|
|
|
1395
1505
|
tx.protocol = detectProtocol(tx.programIds);
|
|
1396
1506
|
const legs = transactionToLegs(tx);
|
|
1397
1507
|
const classification = classifyTransaction(legs, tx);
|
|
1398
|
-
|
|
1508
|
+
let classified = { tx, classification, legs };
|
|
1509
|
+
if (enrichNftMetadata && rpcUrl) {
|
|
1510
|
+
classified = await enrichNftClassification(rpcUrl, classified);
|
|
1511
|
+
}
|
|
1512
|
+
return classified;
|
|
1399
1513
|
},
|
|
1400
1514
|
async getRawTransaction(signature2) {
|
|
1401
1515
|
return fetchTransaction(client.rpc, signature2);
|
|
1516
|
+
},
|
|
1517
|
+
async getNftMetadata(mintAddress) {
|
|
1518
|
+
if (!rpcUrl) {
|
|
1519
|
+
throw new Error("getNftMetadata requires rpcUrl to be set");
|
|
1520
|
+
}
|
|
1521
|
+
return fetchNftMetadata(rpcUrl, mintAddress);
|
|
1522
|
+
},
|
|
1523
|
+
async getNftMetadataBatch(mintAddresses) {
|
|
1524
|
+
if (!rpcUrl) {
|
|
1525
|
+
throw new Error("getNftMetadataBatch requires rpcUrl to be set");
|
|
1526
|
+
}
|
|
1527
|
+
return fetchNftMetadataBatch(rpcUrl, mintAddresses);
|
|
1402
1528
|
}
|
|
1403
1529
|
};
|
|
1404
1530
|
}
|