tx-indexer 0.4.1 → 0.5.1
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 +13 -1
- package/dist/client.js +45 -24
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.js +45 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,10 +37,22 @@ console.log(tx.classification.sender); // sender address
|
|
|
37
37
|
console.log(tx.classification.receiver); // receiver address
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
## RPC Compatibility
|
|
41
|
+
|
|
42
|
+
The SDK works with any Solana RPC for core features (transactions, balances, classification).
|
|
43
|
+
|
|
44
|
+
NFT metadata enrichment requires a DAS-compatible RPC (Helius, Triton, etc.). If using a standard RPC, disable it:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const txs = await indexer.getTransactions(address, {
|
|
48
|
+
enrichNftMetadata: false
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
40
52
|
## Transaction Types
|
|
41
53
|
|
|
42
54
|
- `transfer` - Wallet-to-wallet transfers
|
|
43
|
-
- `swap` - Token exchanges (Jupiter, Raydium, Orca)
|
|
55
|
+
- `swap` - Token exchanges (pattern-based detection works with any DEX, higher confidence for known protocols like Jupiter, Raydium, Orca)
|
|
44
56
|
- `nft_mint` - NFT minting (Metaplex, Candy Machine, Bubblegum)
|
|
45
57
|
- `stake_deposit` - SOL staking deposits
|
|
46
58
|
- `stake_withdraw` - SOL staking withdrawals
|
package/dist/client.js
CHANGED
|
@@ -706,7 +706,7 @@ var TransferClassifier = class {
|
|
|
706
706
|
sender,
|
|
707
707
|
receiver,
|
|
708
708
|
counterparty: {
|
|
709
|
-
type: "
|
|
709
|
+
type: "unknown",
|
|
710
710
|
address: receiver,
|
|
711
711
|
name: `${receiver.slice(0, 8)}...`
|
|
712
712
|
},
|
|
@@ -876,37 +876,56 @@ var SwapClassifier = class {
|
|
|
876
876
|
name = "swap";
|
|
877
877
|
priority = 80;
|
|
878
878
|
classify(context) {
|
|
879
|
-
const { legs, tx } = context;
|
|
880
|
-
const hasDexProtocol = isDexProtocolById(tx.protocol?.id);
|
|
881
|
-
if (!hasDexProtocol) {
|
|
882
|
-
return null;
|
|
883
|
-
}
|
|
879
|
+
const { legs, tx, walletAddress } = context;
|
|
884
880
|
const feeLeg = legs.find(
|
|
885
881
|
(leg) => leg.role === "fee" && leg.side === "debit"
|
|
886
882
|
);
|
|
887
883
|
const initiator = feeLeg?.accountId.replace("external:", "") ?? null;
|
|
888
|
-
|
|
889
|
-
|
|
884
|
+
if (!initiator) {
|
|
885
|
+
return null;
|
|
886
|
+
}
|
|
887
|
+
const initiatorAccountId = `external:${initiator}`;
|
|
888
|
+
const initiatorTokensOut = legs.filter(
|
|
889
|
+
(leg) => leg.accountId === initiatorAccountId && leg.side === "debit" && (leg.role === "sent" || leg.role === "protocol_deposit")
|
|
890
890
|
);
|
|
891
|
-
const
|
|
892
|
-
(leg) => leg.accountId
|
|
891
|
+
const initiatorTokensIn = legs.filter(
|
|
892
|
+
(leg) => leg.accountId === initiatorAccountId && leg.side === "credit" && (leg.role === "received" || leg.role === "protocol_withdraw")
|
|
893
893
|
);
|
|
894
|
-
if (
|
|
894
|
+
if (initiatorTokensOut.length === 0 || initiatorTokensIn.length === 0) {
|
|
895
895
|
return null;
|
|
896
896
|
}
|
|
897
|
-
const
|
|
898
|
-
const
|
|
899
|
-
if (
|
|
897
|
+
const initiatorOut = initiatorTokensOut[0];
|
|
898
|
+
const initiatorIn = initiatorTokensIn[0];
|
|
899
|
+
if (initiatorOut.amount.token.symbol === initiatorIn.amount.token.symbol) {
|
|
900
900
|
return null;
|
|
901
901
|
}
|
|
902
|
+
let tokenOut = initiatorOut;
|
|
903
|
+
let tokenIn = initiatorIn;
|
|
904
|
+
let perspectiveWallet = initiator;
|
|
905
|
+
if (walletAddress) {
|
|
906
|
+
const walletAccountId = `external:${walletAddress}`;
|
|
907
|
+
const walletOut = legs.find(
|
|
908
|
+
(leg) => leg.accountId === walletAccountId && leg.side === "debit" && (leg.role === "sent" || leg.role === "protocol_deposit")
|
|
909
|
+
);
|
|
910
|
+
const walletIn = legs.find(
|
|
911
|
+
(leg) => leg.accountId === walletAccountId && leg.side === "credit" && (leg.role === "received" || leg.role === "protocol_withdraw")
|
|
912
|
+
);
|
|
913
|
+
if (walletOut && walletIn && walletOut.amount.token.symbol !== walletIn.amount.token.symbol) {
|
|
914
|
+
tokenOut = walletOut;
|
|
915
|
+
tokenIn = walletIn;
|
|
916
|
+
perspectiveWallet = walletAddress;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
const hasDexProtocol = isDexProtocolById(tx.protocol?.id);
|
|
920
|
+
const confidence = hasDexProtocol ? 0.95 : 0.75;
|
|
902
921
|
return {
|
|
903
922
|
primaryType: "swap",
|
|
904
923
|
primaryAmount: tokenOut.amount,
|
|
905
924
|
secondaryAmount: tokenIn.amount,
|
|
906
|
-
sender:
|
|
907
|
-
receiver:
|
|
925
|
+
sender: perspectiveWallet,
|
|
926
|
+
receiver: perspectiveWallet,
|
|
908
927
|
counterparty: null,
|
|
909
|
-
confidence
|
|
928
|
+
confidence,
|
|
910
929
|
isRelevant: true,
|
|
911
930
|
metadata: {
|
|
912
931
|
swap_type: "token_to_token",
|
|
@@ -1038,7 +1057,7 @@ var SolanaPayClassifier = class {
|
|
|
1038
1057
|
counterparty: receiver ? {
|
|
1039
1058
|
address: receiver,
|
|
1040
1059
|
name: memo.merchant ?? void 0,
|
|
1041
|
-
type: memo.merchant ? "merchant" : "
|
|
1060
|
+
type: memo.merchant ? "merchant" : "unknown"
|
|
1042
1061
|
} : null,
|
|
1043
1062
|
confidence: 0.98,
|
|
1044
1063
|
isRelevant: true,
|
|
@@ -1246,8 +1265,8 @@ var ClassificationService = class {
|
|
|
1246
1265
|
this.classifiers.push(classifier);
|
|
1247
1266
|
this.classifiers.sort((a, b) => b.priority - a.priority);
|
|
1248
1267
|
}
|
|
1249
|
-
classify(legs, tx) {
|
|
1250
|
-
const context = { legs, tx };
|
|
1268
|
+
classify(legs, tx, walletAddress) {
|
|
1269
|
+
const context = { legs, tx, walletAddress };
|
|
1251
1270
|
for (const classifier of this.classifiers) {
|
|
1252
1271
|
const result = classifier.classify(context);
|
|
1253
1272
|
if (result && result.isRelevant) {
|
|
@@ -1268,8 +1287,8 @@ var ClassificationService = class {
|
|
|
1268
1287
|
}
|
|
1269
1288
|
};
|
|
1270
1289
|
var classificationService = new ClassificationService();
|
|
1271
|
-
function classifyTransaction(legs, tx) {
|
|
1272
|
-
return classificationService.classify(legs, tx);
|
|
1290
|
+
function classifyTransaction(legs, tx, walletAddress) {
|
|
1291
|
+
return classificationService.classify(legs, tx, walletAddress);
|
|
1273
1292
|
}
|
|
1274
1293
|
|
|
1275
1294
|
// ../domain/src/tx/spam-filter.ts
|
|
@@ -1446,10 +1465,11 @@ function createIndexer(options) {
|
|
|
1446
1465
|
client.rpc,
|
|
1447
1466
|
signatureObjects
|
|
1448
1467
|
);
|
|
1468
|
+
const walletAddressStr2 = walletAddress.toString();
|
|
1449
1469
|
const classified = transactions.map((tx) => {
|
|
1450
1470
|
tx.protocol = detectProtocol(tx.programIds);
|
|
1451
1471
|
const legs = transactionToLegs(tx);
|
|
1452
|
-
const classification = classifyTransaction(legs, tx);
|
|
1472
|
+
const classification = classifyTransaction(legs, tx, walletAddressStr2);
|
|
1453
1473
|
return { tx, classification, legs };
|
|
1454
1474
|
});
|
|
1455
1475
|
return enrichBatch(classified);
|
|
@@ -1458,6 +1478,7 @@ function createIndexer(options) {
|
|
|
1458
1478
|
let currentBefore = before;
|
|
1459
1479
|
const MAX_ITERATIONS = 10;
|
|
1460
1480
|
let iteration = 0;
|
|
1481
|
+
const walletAddressStr = walletAddress.toString();
|
|
1461
1482
|
while (accumulated.length < limit && iteration < MAX_ITERATIONS) {
|
|
1462
1483
|
iteration++;
|
|
1463
1484
|
const batchSize = iteration === 1 ? limit : limit * 2;
|
|
@@ -1479,7 +1500,7 @@ function createIndexer(options) {
|
|
|
1479
1500
|
const classified = transactions.map((tx) => {
|
|
1480
1501
|
tx.protocol = detectProtocol(tx.programIds);
|
|
1481
1502
|
const legs = transactionToLegs(tx);
|
|
1482
|
-
const classification = classifyTransaction(legs, tx);
|
|
1503
|
+
const classification = classifyTransaction(legs, tx, walletAddressStr);
|
|
1483
1504
|
return { tx, classification, legs };
|
|
1484
1505
|
});
|
|
1485
1506
|
const nonSpam = filterSpamTransactions(classified, spamConfig);
|