tx-indexer 0.5.3 → 0.5.5
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/dist/{client-xmDVjOy4.d.ts → client-D8iyrH5w.d.ts} +8 -3
- package/dist/client.d.ts +1 -1
- package/dist/client.js +93 -18
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +93 -18
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
|
@@ -110,27 +110,32 @@ interface SpamFilterConfig {
|
|
|
110
110
|
* - Has low classification confidence
|
|
111
111
|
* - Is not relevant to the wallet
|
|
112
112
|
* - Involves dust amounts below configured thresholds
|
|
113
|
+
* - The wallet only received dust amounts (spam airdrops)
|
|
113
114
|
*
|
|
114
115
|
* @param tx - Raw transaction data
|
|
115
116
|
* @param classification - Transaction classification result
|
|
116
117
|
* @param config - Optional spam filter configuration (uses defaults if omitted)
|
|
118
|
+
* @param legs - Optional legs to check wallet involvement
|
|
119
|
+
* @param walletAddress - Optional wallet address to check involvement
|
|
117
120
|
* @returns True if the transaction should be filtered as spam
|
|
118
121
|
*/
|
|
119
|
-
declare function isSpamTransaction(tx: RawTransaction, classification: TransactionClassification, config?: SpamFilterConfig): boolean;
|
|
122
|
+
declare function isSpamTransaction(tx: RawTransaction, classification: TransactionClassification, config?: SpamFilterConfig, legs?: TxLeg[], walletAddress?: string): boolean;
|
|
120
123
|
/**
|
|
121
124
|
* Filters an array of transactions to remove spam and dust transactions.
|
|
122
125
|
*
|
|
123
126
|
* Applies spam detection criteria to each transaction while preserving
|
|
124
127
|
* additional properties in the returned array items.
|
|
125
128
|
*
|
|
126
|
-
* @param transactions - Array of transaction objects with tx and
|
|
129
|
+
* @param transactions - Array of transaction objects with tx, classification, and legs
|
|
127
130
|
* @param config - Optional spam filter configuration
|
|
131
|
+
* @param walletAddress - Optional wallet address to check for dust airdrops
|
|
128
132
|
* @returns Filtered array with spam transactions removed
|
|
129
133
|
*/
|
|
130
134
|
declare function filterSpamTransactions<T extends {
|
|
131
135
|
tx: RawTransaction;
|
|
132
136
|
classification: TransactionClassification;
|
|
133
|
-
|
|
137
|
+
legs?: TxLeg[];
|
|
138
|
+
}>(transactions: T[], config?: SpamFilterConfig, walletAddress?: string): T[];
|
|
134
139
|
|
|
135
140
|
interface WalletBalance {
|
|
136
141
|
address: string;
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import '@solana/kit';
|
|
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-
|
|
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-D8iyrH5w.js';
|
|
3
3
|
import './classification.types-h046WjuF.js';
|
|
4
4
|
import 'zod';
|
package/dist/client.js
CHANGED
|
@@ -1101,21 +1101,38 @@ function determineTokenRole(change, tx, feePayer) {
|
|
|
1101
1101
|
}
|
|
1102
1102
|
|
|
1103
1103
|
// ../classification/src/classifiers/transfer-classifier.ts
|
|
1104
|
+
function findBestTransferPair(legs) {
|
|
1105
|
+
const senderLegs = legs.filter(
|
|
1106
|
+
(l) => l.accountId.startsWith("external:") && l.side === "debit" && l.role === "sent"
|
|
1107
|
+
);
|
|
1108
|
+
if (senderLegs.length === 0) return null;
|
|
1109
|
+
let bestPair = null;
|
|
1110
|
+
let bestAmount = 0;
|
|
1111
|
+
for (const senderLeg of senderLegs) {
|
|
1112
|
+
const receiverLeg = legs.find(
|
|
1113
|
+
(l) => l.accountId.startsWith("external:") && l.side === "credit" && l.role === "received" && l.amount.token.mint === senderLeg.amount.token.mint && l.accountId !== senderLeg.accountId
|
|
1114
|
+
// Must be different account
|
|
1115
|
+
);
|
|
1116
|
+
if (receiverLeg) {
|
|
1117
|
+
const amount = senderLeg.amount.amountUi;
|
|
1118
|
+
if (amount > bestAmount) {
|
|
1119
|
+
bestAmount = amount;
|
|
1120
|
+
bestPair = { senderLeg, receiverLeg };
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
return bestPair;
|
|
1125
|
+
}
|
|
1104
1126
|
var TransferClassifier = class {
|
|
1105
1127
|
name = "transfer";
|
|
1106
1128
|
priority = 20;
|
|
1107
1129
|
classify(context) {
|
|
1108
1130
|
const { legs, tx } = context;
|
|
1109
1131
|
const facilitator = tx.accountKeys ? detectFacilitator(tx.accountKeys) : null;
|
|
1110
|
-
const
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
if (!senderLeg) return null;
|
|
1132
|
+
const pair = findBestTransferPair(legs);
|
|
1133
|
+
if (!pair) return null;
|
|
1134
|
+
const { senderLeg, receiverLeg } = pair;
|
|
1114
1135
|
const sender = senderLeg.accountId.replace("external:", "");
|
|
1115
|
-
const receiverLeg = legs.find(
|
|
1116
|
-
(l) => l.accountId.startsWith("external:") && l.side === "credit" && l.role === "received" && l.amount.token.mint === senderLeg.amount.token.mint
|
|
1117
|
-
);
|
|
1118
|
-
if (!receiverLeg) return null;
|
|
1119
1136
|
const receiver = receiverLeg.accountId.replace("external:", "");
|
|
1120
1137
|
return {
|
|
1121
1138
|
primaryType: "transfer",
|
|
@@ -1594,6 +1611,27 @@ var FeeOnlyClassifier = class {
|
|
|
1594
1611
|
};
|
|
1595
1612
|
|
|
1596
1613
|
// ../classification/src/classifiers/solana-pay-classifier.ts
|
|
1614
|
+
function findBestTransferPair2(legs) {
|
|
1615
|
+
const senderLegs = legs.filter(
|
|
1616
|
+
(l) => l.accountId.startsWith("external:") && l.side === "debit" && l.role === "sent"
|
|
1617
|
+
);
|
|
1618
|
+
if (senderLegs.length === 0) return null;
|
|
1619
|
+
let bestPair = null;
|
|
1620
|
+
let bestAmount = 0;
|
|
1621
|
+
for (const senderLeg of senderLegs) {
|
|
1622
|
+
const receiverLeg = legs.find(
|
|
1623
|
+
(l) => l.accountId.startsWith("external:") && l.side === "credit" && l.role === "received" && l.amount.token.mint === senderLeg.amount.token.mint && l.accountId !== senderLeg.accountId
|
|
1624
|
+
);
|
|
1625
|
+
if (receiverLeg) {
|
|
1626
|
+
const amount = senderLeg.amount.amountUi;
|
|
1627
|
+
if (amount > bestAmount) {
|
|
1628
|
+
bestAmount = amount;
|
|
1629
|
+
bestPair = { senderLeg, receiverLeg };
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
return bestPair;
|
|
1634
|
+
}
|
|
1597
1635
|
var SolanaPayClassifier = class {
|
|
1598
1636
|
name = "solana-pay";
|
|
1599
1637
|
priority = 95;
|
|
@@ -1603,12 +1641,9 @@ var SolanaPayClassifier = class {
|
|
|
1603
1641
|
return null;
|
|
1604
1642
|
}
|
|
1605
1643
|
const memo = parseSolanaPayMemo(tx.memo);
|
|
1606
|
-
const
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
const receiverLeg = legs.find(
|
|
1610
|
-
(leg) => leg.accountId.startsWith("external:") && leg.side === "credit" && leg.role === "received"
|
|
1611
|
-
);
|
|
1644
|
+
const pair = findBestTransferPair2(legs);
|
|
1645
|
+
const senderLeg = pair?.senderLeg ?? null;
|
|
1646
|
+
const receiverLeg = pair?.receiverLeg ?? null;
|
|
1612
1647
|
const sender = senderLeg?.accountId.replace("external:", "") ?? null;
|
|
1613
1648
|
const receiver = receiverLeg?.accountId.replace("external:", "") ?? null;
|
|
1614
1649
|
const primaryAmount = senderLeg?.amount ?? receiverLeg?.amount ?? null;
|
|
@@ -1862,7 +1897,7 @@ var DEFAULT_CONFIG2 = {
|
|
|
1862
1897
|
minConfidence: 0.5,
|
|
1863
1898
|
allowFailed: false
|
|
1864
1899
|
};
|
|
1865
|
-
function isSpamTransaction(tx, classification, config = {}) {
|
|
1900
|
+
function isSpamTransaction(tx, classification, config = {}, legs, walletAddress) {
|
|
1866
1901
|
const cfg = { ...DEFAULT_CONFIG2, ...config };
|
|
1867
1902
|
if (!cfg.allowFailed && tx.err) {
|
|
1868
1903
|
return true;
|
|
@@ -1876,6 +1911,11 @@ function isSpamTransaction(tx, classification, config = {}) {
|
|
|
1876
1911
|
if (isDustTransaction(classification, cfg)) {
|
|
1877
1912
|
return true;
|
|
1878
1913
|
}
|
|
1914
|
+
if (legs && walletAddress) {
|
|
1915
|
+
if (isWalletDustOnly(legs, walletAddress, cfg)) {
|
|
1916
|
+
return true;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1879
1919
|
return false;
|
|
1880
1920
|
}
|
|
1881
1921
|
function isDustTransaction(classification, config) {
|
|
@@ -1892,9 +1932,40 @@ function isDustTransaction(classification, config) {
|
|
|
1892
1932
|
}
|
|
1893
1933
|
return Math.abs(amountUi) < config.minTokenAmountUsd;
|
|
1894
1934
|
}
|
|
1895
|
-
function
|
|
1935
|
+
function isWalletDustOnly(legs, walletAddress, config) {
|
|
1936
|
+
const walletAccountId = `external:${walletAddress}`;
|
|
1937
|
+
const walletLegs = legs.filter((leg) => leg.accountId === walletAccountId);
|
|
1938
|
+
if (walletLegs.length === 0) {
|
|
1939
|
+
return false;
|
|
1940
|
+
}
|
|
1941
|
+
const sentLegs = walletLegs.filter(
|
|
1942
|
+
(leg) => leg.side === "debit" && leg.role === "sent"
|
|
1943
|
+
);
|
|
1944
|
+
for (const leg of sentLegs) {
|
|
1945
|
+
if (!isDustAmount(leg.amount.token.symbol, leg.amount.amountUi, config)) {
|
|
1946
|
+
return false;
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
const receivedLegs = walletLegs.filter(
|
|
1950
|
+
(leg) => leg.side === "credit" && leg.role === "received"
|
|
1951
|
+
);
|
|
1952
|
+
for (const leg of receivedLegs) {
|
|
1953
|
+
if (!isDustAmount(leg.amount.token.symbol, leg.amount.amountUi, config)) {
|
|
1954
|
+
return false;
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
return receivedLegs.length > 0;
|
|
1958
|
+
}
|
|
1959
|
+
function isDustAmount(symbol, amountUi, config) {
|
|
1960
|
+
const absAmount = Math.abs(amountUi);
|
|
1961
|
+
if (symbol === "SOL" || symbol === "WSOL") {
|
|
1962
|
+
return absAmount < config.minSolAmount;
|
|
1963
|
+
}
|
|
1964
|
+
return absAmount < config.minTokenAmountUsd;
|
|
1965
|
+
}
|
|
1966
|
+
function filterSpamTransactions(transactions, config, walletAddress) {
|
|
1896
1967
|
return transactions.filter(
|
|
1897
|
-
({ tx, classification }) => !isSpamTransaction(tx, classification, config)
|
|
1968
|
+
({ tx, classification, legs }) => !isSpamTransaction(tx, classification, config, legs, walletAddress)
|
|
1898
1969
|
);
|
|
1899
1970
|
}
|
|
1900
1971
|
|
|
@@ -2310,7 +2381,11 @@ function createIndexer(options) {
|
|
|
2310
2381
|
);
|
|
2311
2382
|
return { tx, classification, legs };
|
|
2312
2383
|
});
|
|
2313
|
-
const nonSpam = filterSpamTransactions(
|
|
2384
|
+
const nonSpam = filterSpamTransactions(
|
|
2385
|
+
classified,
|
|
2386
|
+
spamConfig,
|
|
2387
|
+
walletAddressStr
|
|
2388
|
+
);
|
|
2314
2389
|
accumulated.push(...nonSpam);
|
|
2315
2390
|
const lastSignature = signatures[signatures.length - 1];
|
|
2316
2391
|
if (lastSignature) {
|