forgex-cli 1.0.59 → 1.0.62
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 +375 -368
- package/dist/bin/forgex.d.ts +5 -5
- package/dist/bin/forgex.js +14 -14
- package/dist/bin/forgex.js.map +1 -1
- package/dist/src/adapters/codex-adapter.d.ts +90 -90
- package/dist/src/adapters/codex-adapter.d.ts.map +1 -1
- package/dist/src/adapters/codex-adapter.js +76 -76
- package/dist/src/adapters/codex-adapter.js.map +1 -1
- package/dist/src/adapters/connection.d.ts +6 -6
- package/dist/src/adapters/connection.js +8 -8
- package/dist/src/adapters/connection.js.map +1 -1
- package/dist/src/adapters/ipfs.d.ts +3 -3
- package/dist/src/adapters/ipfs.js +3 -3
- package/dist/src/adapters/jito-adapter.d.ts +85 -85
- package/dist/src/adapters/jito-adapter.d.ts.map +1 -1
- package/dist/src/adapters/jito-adapter.js +111 -111
- package/dist/src/adapters/jito-adapter.js.map +1 -1
- package/dist/src/adapters/rpc-adapter.d.ts +53 -53
- package/dist/src/adapters/rpc-adapter.d.ts.map +1 -1
- package/dist/src/adapters/rpc-adapter.js +69 -69
- package/dist/src/adapters/rpc-adapter.js.map +1 -1
- package/dist/src/adapters/sdk-adapter.d.ts +21 -21
- package/dist/src/adapters/sdk-adapter.d.ts.map +1 -1
- package/dist/src/adapters/sdk-adapter.js +79 -79
- package/dist/src/adapters/sdk-adapter.js.map +1 -1
- package/dist/src/commands/config/index.d.ts +1 -1
- package/dist/src/commands/config/index.js +15 -15
- package/dist/src/commands/config/index.js.map +1 -1
- package/dist/src/commands/query/index.d.ts +2 -2
- package/dist/src/commands/query/index.js +82 -82
- package/dist/src/commands/query/index.js.map +1 -1
- package/dist/src/commands/token/index.d.ts +8 -8
- package/dist/src/commands/token/index.js +73 -73
- package/dist/src/commands/token/index.js.map +1 -1
- package/dist/src/commands/tools/index.d.ts +9 -9
- package/dist/src/commands/tools/index.js +137 -137
- package/dist/src/commands/tools/index.js.map +1 -1
- package/dist/src/commands/trade/index.d.ts +2 -2
- package/dist/src/commands/trade/index.d.ts.map +1 -1
- package/dist/src/commands/trade/index.js +120 -82
- package/dist/src/commands/trade/index.js.map +1 -1
- package/dist/src/commands/transfer/index.d.ts +8 -8
- package/dist/src/commands/transfer/index.js +106 -106
- package/dist/src/commands/transfer/index.js.map +1 -1
- package/dist/src/commands/wallet/index.d.ts +1 -1
- package/dist/src/commands/wallet/index.js +175 -175
- package/dist/src/commands/wallet/index.js.map +1 -1
- package/dist/src/config.d.ts +26 -26
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +28 -28
- package/dist/src/config.js.map +1 -1
- package/dist/src/const/index.js +1 -1
- package/dist/src/const/index.js.map +1 -1
- package/dist/src/data-source.d.ts +81 -81
- package/dist/src/data-source.d.ts.map +1 -1
- package/dist/src/data-source.js +149 -149
- package/dist/src/data-source.js.map +1 -1
- package/dist/src/data-store/index.d.ts +22 -22
- package/dist/src/data-store/index.d.ts.map +1 -1
- package/dist/src/data-store/index.js +46 -46
- package/dist/src/data-store/index.js.map +1 -1
- package/dist/src/data-store/types.d.ts +3 -3
- package/dist/src/data-store/types.js +3 -3
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +10 -10
- package/dist/src/index.js.map +1 -1
- package/dist/src/output.d.ts +18 -18
- package/dist/src/output.d.ts.map +1 -1
- package/dist/src/output.js +34 -34
- package/dist/src/output.js.map +1 -1
- package/dist/src/shims/store.d.ts +3 -2
- package/dist/src/shims/store.d.ts.map +1 -1
- package/dist/src/shims/store.js +6 -5
- package/dist/src/shims/store.js.map +1 -1
- package/dist/src/sol-sdk/batch/create.d.ts +4 -1
- package/dist/src/sol-sdk/batch/create.d.ts.map +1 -1
- package/dist/src/sol-sdk/batch/create.js +44 -44
- package/dist/src/sol-sdk/batch/create.js.map +1 -1
- package/dist/src/sol-sdk/batch/index.js +135 -135
- package/dist/src/sol-sdk/batch/index.js.map +1 -1
- package/dist/src/sol-sdk/calc.d.ts +63 -63
- package/dist/src/sol-sdk/calc.d.ts.map +1 -1
- package/dist/src/sol-sdk/calc.js +120 -120
- package/dist/src/sol-sdk/calc.js.map +1 -1
- package/dist/src/sol-sdk/jito/index.js +12 -12
- package/dist/src/sol-sdk/jito/index.js.map +1 -1
- package/dist/src/sol-sdk/launchlab/instructions/create.js +10 -10
- package/dist/src/sol-sdk/launchlab/instructions/create.js.map +1 -1
- package/dist/src/sol-sdk/meteora/index.d.ts +5 -5
- package/dist/src/sol-sdk/meteora/index.js +11 -11
- package/dist/src/sol-sdk/meteora/index.js.map +1 -1
- package/dist/src/sol-sdk/meteora/instructions/buy.js +8 -8
- package/dist/src/sol-sdk/meteora/instructions/buy.js.map +1 -1
- package/dist/src/sol-sdk/meteora/instructions/sell.js +6 -6
- package/dist/src/sol-sdk/meteora/instructions/sell.js.map +1 -1
- package/dist/src/sol-sdk/pump/index.js +3 -3
- package/dist/src/sol-sdk/pump/index.js.map +1 -1
- package/dist/src/sol-sdk/pump/instructions/buy.d.ts +12 -12
- package/dist/src/sol-sdk/pump/instructions/buy.d.ts.map +1 -1
- package/dist/src/sol-sdk/pump/instructions/buy.js +26 -26
- package/dist/src/sol-sdk/pump/instructions/buy.js.map +1 -1
- package/dist/src/sol-sdk/pump/instructions/createAndBuy.d.ts +13 -13
- package/dist/src/sol-sdk/pump/instructions/createAndBuy.js +17 -17
- package/dist/src/sol-sdk/pump/instructions/createAndBuy.js.map +1 -1
- package/dist/src/sol-sdk/pump/instructions/sell.d.ts +2 -2
- package/dist/src/sol-sdk/pump/instructions/sell.d.ts.map +1 -1
- package/dist/src/sol-sdk/pump/instructions/sell.js +7 -7
- package/dist/src/sol-sdk/pump/instructions/sell.js.map +1 -1
- package/dist/src/sol-sdk/pumpswap/index.d.ts +4 -4
- package/dist/src/sol-sdk/pumpswap/index.js +5 -5
- package/dist/src/sol-sdk/pumpswap/index.js.map +1 -1
- package/dist/src/sol-sdk/pumpswap/instructions/buy.d.ts +8 -8
- package/dist/src/sol-sdk/pumpswap/instructions/buy.js +19 -19
- package/dist/src/sol-sdk/pumpswap/instructions/buy.js.map +1 -1
- package/dist/src/sol-sdk/pumpswap/instructions/migrate.js +2 -2
- package/dist/src/sol-sdk/pumpswap/instructions/migrate.js.map +1 -1
- package/dist/src/sol-sdk/pumpswap/instructions/sell.js +4 -4
- package/dist/src/sol-sdk/pumpswap/instructions/sell.js.map +1 -1
- package/dist/src/sol-sdk/pumpswap/rpc/index.js +1 -1
- package/dist/src/sol-sdk/pumpswap/rpc/index.js.map +1 -1
- package/dist/src/sol-sdk/raydium/instructions/cpmmSell.js +3 -3
- package/dist/src/sol-sdk/raydium/instructions/cpmmSell.js.map +1 -1
- package/dist/src/sol-sdk/raydium/instructions/sell.d.ts +40 -8520
- package/dist/src/sol-sdk/raydium/instructions/sell.d.ts.map +1 -1
- package/dist/src/sol-sdk/raydium/instructions/sell.js +6 -6
- package/dist/src/sol-sdk/raydium/instructions/sell.js.map +1 -1
- package/dist/src/sol-sdk/raydium/rpc/index.d.ts +4 -4
- package/dist/src/sol-sdk/rpc/index.d.ts +14 -14
- package/dist/src/sol-sdk/rpc/index.d.ts.map +1 -1
- package/dist/src/sol-sdk/rpc/index.js +17 -17
- package/dist/src/sol-sdk/rpc/index.js.map +1 -1
- package/dist/src/sol-sdk/transfer/index.js +5 -5
- package/dist/src/sol-sdk/transfer/index.js.map +1 -1
- package/dist/src/sol-sdk/turnover/index.d.ts +3 -3
- package/dist/src/sol-sdk/turnover/index.js +56 -56
- package/dist/src/sol-sdk/turnover/index.js.map +1 -1
- package/dist/src/telemetry.d.ts +8 -8
- package/dist/src/telemetry.d.ts.map +1 -1
- package/dist/src/telemetry.js +25 -25
- package/dist/src/telemetry.js.map +1 -1
- package/dist/src/tx-tracker/detail-adapter.d.ts +53 -53
- package/dist/src/tx-tracker/detail-adapter.d.ts.map +1 -1
- package/dist/src/tx-tracker/detail-adapter.js +68 -68
- package/dist/src/tx-tracker/detail-adapter.js.map +1 -1
- package/dist/src/tx-tracker/index.d.ts +67 -67
- package/dist/src/tx-tracker/index.d.ts.map +1 -1
- package/dist/src/tx-tracker/index.js +103 -103
- package/dist/src/tx-tracker/index.js.map +1 -1
- package/dist/src/types/index.d.ts +10 -10
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/websocket.js +1 -1
- package/dist/src/types/websocket.js.map +1 -1
- package/dist/src/utils/index.js +20 -20
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/wallet-store.d.ts +51 -51
- package/dist/src/wallet-store.d.ts.map +1 -1
- package/dist/src/wallet-store.js +104 -104
- package/dist/src/wallet-store.js.map +1 -1
- package/package.json +1 -1
package/dist/src/data-source.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ForgeX CLI DataSource --
|
|
2
|
+
* ForgeX CLI DataSource -- Unified Data Source Facade
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* JitoAdapter
|
|
4
|
+
* Single data access interface for the command layer. Wraps all data adapters (CodexAdapter, RpcAdapter,
|
|
5
|
+
* JitoAdapter, DataStore) into a unified facade with simple interfaces for command use.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Bundle
|
|
11
|
-
*
|
|
7
|
+
* Routing logic:
|
|
8
|
+
* Token info / price / candlestick / pool info --> CodexAdapter (remote, cache-through to DataStore)
|
|
9
|
+
* Balance / account info / tx confirmation --> RpcAdapter (on-chain)
|
|
10
|
+
* Bundle send / status query --> JitoAdapter (Jito Block Engine)
|
|
11
|
+
* Trade records / positions / wallet group data --> DataStore (local files)
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Cache-through:
|
|
14
|
+
* Reads DataStore local cache first. On miss or expiry, fetches from remote (Codex/RPC),
|
|
15
|
+
* writes to DataStore, then returns.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* RPC/Jito
|
|
17
|
+
* Fault tolerance:
|
|
18
|
+
* When Codex API is unavailable, falls back to historical cache in DataStore.
|
|
19
|
+
* When RPC/Jito unavailable, throws DataSourceError for command layer to handle.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
21
|
+
* Design reference: ARCH-DESIGN-v2.md Section 2.7
|
|
22
22
|
*/
|
|
23
23
|
import { getCodexAdapter } from './adapters/codex-adapter.js';
|
|
24
24
|
import { getRpcAdapter } from './adapters/rpc-adapter.js';
|
|
@@ -26,14 +26,14 @@ import { getJitoAdapter } from './adapters/jito-adapter.js';
|
|
|
26
26
|
import { getDataStore } from './data-store/index.js';
|
|
27
27
|
import { getTxTracker } from './tx-tracker/index.js';
|
|
28
28
|
// ============================================================
|
|
29
|
-
//
|
|
29
|
+
// Unified Error Type
|
|
30
30
|
// ============================================================
|
|
31
31
|
export class DataSourceError extends Error {
|
|
32
|
-
/**
|
|
32
|
+
/** Error source */
|
|
33
33
|
source;
|
|
34
|
-
/**
|
|
34
|
+
/** Whether fallback to cache was used */
|
|
35
35
|
fellBackToCache;
|
|
36
|
-
/**
|
|
36
|
+
/** Original error */
|
|
37
37
|
cause;
|
|
38
38
|
constructor(message, source, options) {
|
|
39
39
|
super(message);
|
|
@@ -44,16 +44,16 @@ export class DataSourceError extends Error {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
// ============================================================
|
|
47
|
-
//
|
|
47
|
+
// Cache expiry thresholds (ms)
|
|
48
48
|
// ============================================================
|
|
49
|
-
/**
|
|
49
|
+
/** Token info cache TTL: 10 minutes */
|
|
50
50
|
const TOKEN_INFO_TTL_MS = 10 * 60 * 1000;
|
|
51
|
-
/**
|
|
51
|
+
/** Pool info cache TTL: 2 minutes */
|
|
52
52
|
const POOL_INFO_TTL_MS = 2 * 60 * 1000;
|
|
53
|
-
/** SOL
|
|
53
|
+
/** SOL price cache TTL: 1 minute */
|
|
54
54
|
const SOL_PRICE_TTL_MS = 60 * 1000;
|
|
55
55
|
// ============================================================
|
|
56
|
-
// DataSource
|
|
56
|
+
// DataSource Implementation
|
|
57
57
|
// ============================================================
|
|
58
58
|
export class DataSource {
|
|
59
59
|
codex;
|
|
@@ -69,24 +69,24 @@ export class DataSource {
|
|
|
69
69
|
this.tracker = options?.tracker ?? getTxTracker();
|
|
70
70
|
}
|
|
71
71
|
// ============================================================
|
|
72
|
-
//
|
|
72
|
+
// Token Info -- CodexAdapter + DataStore cache-through
|
|
73
73
|
// ============================================================
|
|
74
74
|
/**
|
|
75
|
-
*
|
|
75
|
+
* Get basic token info.
|
|
76
76
|
*
|
|
77
|
-
*
|
|
78
|
-
* 1.
|
|
79
|
-
* 2.
|
|
80
|
-
* 3.
|
|
81
|
-
* 4. Codex
|
|
77
|
+
* Flow:
|
|
78
|
+
* 1. Check DataStore cache first
|
|
79
|
+
* 2. Cache hit and not expired -> return directly
|
|
80
|
+
* 3. Cache miss or expired -> fetch from Codex and write to DataStore
|
|
81
|
+
* 4. Codex unavailable -> fallback to stale DataStore cache (if any)
|
|
82
82
|
*/
|
|
83
83
|
async getTokenInfo(ca) {
|
|
84
|
-
// 1.
|
|
84
|
+
// 1. Check local cache
|
|
85
85
|
const cached = this.store.getTokenInfo(ca);
|
|
86
86
|
if (cached && !this.isExpired(cached.updatedAt, TOKEN_INFO_TTL_MS)) {
|
|
87
87
|
return cached;
|
|
88
88
|
}
|
|
89
|
-
// 2.
|
|
89
|
+
// 2. Fetch from Codex
|
|
90
90
|
try {
|
|
91
91
|
const marketData = await this.codex.getTokenMarketData(ca);
|
|
92
92
|
const topPair = await this.codex.getTopPair(ca).catch(() => null);
|
|
@@ -100,37 +100,37 @@ export class DataSource {
|
|
|
100
100
|
pairAddress: marketData.topPairAddress || topPair?.pairAddress || '',
|
|
101
101
|
updatedAt: Date.now(),
|
|
102
102
|
};
|
|
103
|
-
//
|
|
103
|
+
// Write to DataStore
|
|
104
104
|
await this.store.saveTokenInfo(ca, tokenInfo);
|
|
105
105
|
return tokenInfo;
|
|
106
106
|
}
|
|
107
107
|
catch (err) {
|
|
108
|
-
// 3. Codex
|
|
108
|
+
// 3. Codex unavailable, fallback to stale cache
|
|
109
109
|
if (cached) {
|
|
110
110
|
return cached;
|
|
111
111
|
}
|
|
112
|
-
throw new DataSourceError(
|
|
112
|
+
throw new DataSourceError(`Failed to get token info (${ca}): ${err.message}`, 'codex', { cause: err });
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
116
|
+
* Get detailed token market data (including price, volume, holders, etc).
|
|
117
|
+
* Fetches directly from Codex without local cache-through (market data requires high timeliness).
|
|
118
118
|
*/
|
|
119
119
|
async getTokenMarketData(ca) {
|
|
120
120
|
try {
|
|
121
121
|
return await this.codex.getTokenMarketData(ca);
|
|
122
122
|
}
|
|
123
123
|
catch (err) {
|
|
124
|
-
throw new DataSourceError(
|
|
124
|
+
throw new DataSourceError(`Failed to get token market data (${ca}): ${err.message}`, 'codex', { cause: err });
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
// ============================================================
|
|
128
|
-
//
|
|
128
|
+
// Price -- CodexAdapter + DataStore cache-through
|
|
129
129
|
// ============================================================
|
|
130
130
|
/**
|
|
131
|
-
*
|
|
131
|
+
* Get token price (SOL and USD).
|
|
132
132
|
*
|
|
133
|
-
*
|
|
133
|
+
* Cache-through: checks DataStore pool-info cached price first, fetches from Codex if expired.
|
|
134
134
|
*/
|
|
135
135
|
async getTokenPrice(ca) {
|
|
136
136
|
try {
|
|
@@ -138,57 +138,57 @@ export class DataSource {
|
|
|
138
138
|
return result;
|
|
139
139
|
}
|
|
140
140
|
catch (err) {
|
|
141
|
-
// fallback:
|
|
141
|
+
// fallback: read historical price from DataStore pool-info
|
|
142
142
|
const poolInfo = this.store.getPoolInfo(ca);
|
|
143
143
|
if (poolInfo) {
|
|
144
144
|
return { priceSol: poolInfo.priceSol, priceUsd: poolInfo.priceUsd };
|
|
145
145
|
}
|
|
146
|
-
throw new DataSourceError(
|
|
146
|
+
throw new DataSourceError(`Failed to get token price (${ca}): ${err.message}`, 'codex', { cause: err });
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
/**
|
|
150
|
-
*
|
|
150
|
+
* Get SOL price (USD).
|
|
151
151
|
*
|
|
152
|
-
*
|
|
152
|
+
* Cache-through: checks DataStore first, fetches from Codex and updates if expired.
|
|
153
153
|
*/
|
|
154
154
|
async getSolPrice() {
|
|
155
|
-
// 1.
|
|
155
|
+
// 1. Check local cache
|
|
156
156
|
const cachedPrice = this.store.getSolPrice();
|
|
157
157
|
if (cachedPrice > 0) {
|
|
158
|
-
//
|
|
159
|
-
// DataStore
|
|
160
|
-
//
|
|
161
|
-
//
|
|
158
|
+
// Check if cache is expired -- getSolPrice returns a number, needs extra time check
|
|
159
|
+
// DataStore internal MemoryCache has 30s TTL, adding another file-level TTL here
|
|
160
|
+
// Since DataStore.getSolPrice() already has MemoryCache, trust it here
|
|
161
|
+
// But if refresh is explicitly needed, the try below will attempt it
|
|
162
162
|
}
|
|
163
|
-
// 2.
|
|
163
|
+
// 2. Fetch from Codex
|
|
164
164
|
try {
|
|
165
165
|
const price = await this.codex.getSolPrice();
|
|
166
166
|
await this.store.saveSolPrice(price);
|
|
167
167
|
return price;
|
|
168
168
|
}
|
|
169
169
|
catch (err) {
|
|
170
|
-
// fallback:
|
|
170
|
+
// fallback: use cache
|
|
171
171
|
if (cachedPrice > 0) {
|
|
172
172
|
return cachedPrice;
|
|
173
173
|
}
|
|
174
|
-
throw new DataSourceError(
|
|
174
|
+
throw new DataSourceError(`Failed to get SOL price: ${err.message}`, 'codex', { fellBackToCache: false, cause: err });
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
// ============================================================
|
|
178
|
-
//
|
|
178
|
+
// Pool/Liquidity Info -- CodexAdapter + DataStore cache-through
|
|
179
179
|
// ============================================================
|
|
180
180
|
/**
|
|
181
|
-
*
|
|
181
|
+
* Get liquidity pool info.
|
|
182
182
|
*
|
|
183
|
-
*
|
|
183
|
+
* Cache-through: DataStore valid -> return; miss/expired -> fetch from Codex and write.
|
|
184
184
|
*/
|
|
185
185
|
async getPoolInfo(ca) {
|
|
186
|
-
// 1.
|
|
186
|
+
// 1. Check local cache
|
|
187
187
|
const cached = this.store.getPoolInfo(ca);
|
|
188
188
|
if (cached && !this.isExpired(cached.updatedAt, POOL_INFO_TTL_MS)) {
|
|
189
189
|
return cached;
|
|
190
190
|
}
|
|
191
|
-
// 2.
|
|
191
|
+
// 2. Fetch from Codex
|
|
192
192
|
try {
|
|
193
193
|
const codexPool = await this.codex.getPoolInfo(ca);
|
|
194
194
|
const poolInfo = {
|
|
@@ -204,105 +204,105 @@ export class DataSource {
|
|
|
204
204
|
return poolInfo;
|
|
205
205
|
}
|
|
206
206
|
catch (err) {
|
|
207
|
-
// fallback
|
|
207
|
+
// fallback to stale cache
|
|
208
208
|
if (cached) {
|
|
209
209
|
return cached;
|
|
210
210
|
}
|
|
211
|
-
throw new DataSourceError(
|
|
211
|
+
throw new DataSourceError(`Failed to get pool info (${ca}): ${err.message}`, 'codex', { cause: err });
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
// ============================================================
|
|
215
|
-
//
|
|
215
|
+
// Candlestick Data -- CodexAdapter (no local cache)
|
|
216
216
|
// ============================================================
|
|
217
217
|
/**
|
|
218
|
-
*
|
|
219
|
-
*
|
|
218
|
+
* Get candlestick (OHLCV) data.
|
|
219
|
+
* Candlestick data is large and time-sensitive, no local caching.
|
|
220
220
|
*/
|
|
221
221
|
async getKlineData(params) {
|
|
222
222
|
try {
|
|
223
223
|
return await this.codex.getBars(params);
|
|
224
224
|
}
|
|
225
225
|
catch (err) {
|
|
226
|
-
throw new DataSourceError(
|
|
226
|
+
throw new DataSourceError(`Failed to get candlestick data: ${err.message}`, 'codex', { cause: err });
|
|
227
227
|
}
|
|
228
228
|
}
|
|
229
229
|
/**
|
|
230
|
-
*
|
|
230
|
+
* Get trading pair list for a token.
|
|
231
231
|
*/
|
|
232
232
|
async getPairsForToken(ca, limit) {
|
|
233
233
|
try {
|
|
234
234
|
return await this.codex.getPairsForToken(ca, limit);
|
|
235
235
|
}
|
|
236
236
|
catch (err) {
|
|
237
|
-
throw new DataSourceError(
|
|
237
|
+
throw new DataSourceError(`Failed to get trading pairs (${ca}): ${err.message}`, 'codex', { cause: err });
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
// ============================================================
|
|
241
|
-
//
|
|
241
|
+
// Balance Queries -- RpcAdapter (on-chain real-time data)
|
|
242
242
|
// ============================================================
|
|
243
243
|
/**
|
|
244
|
-
*
|
|
244
|
+
* Get SOL balance for a single wallet.
|
|
245
245
|
*/
|
|
246
246
|
async getSolBalance(address) {
|
|
247
247
|
try {
|
|
248
248
|
return await this.rpc.getSolBalance(address);
|
|
249
249
|
}
|
|
250
250
|
catch (err) {
|
|
251
|
-
throw new DataSourceError(
|
|
251
|
+
throw new DataSourceError(`Failed to get SOL balance (${address}): ${err.message}`, 'rpc', { cause: err });
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
/**
|
|
255
|
-
*
|
|
255
|
+
* Batch get SOL balances for multiple wallets.
|
|
256
256
|
*/
|
|
257
257
|
async getBatchSolBalances(addresses) {
|
|
258
258
|
try {
|
|
259
259
|
return await this.rpc.getBatchSolBalances(addresses);
|
|
260
260
|
}
|
|
261
261
|
catch (err) {
|
|
262
|
-
throw new DataSourceError(
|
|
262
|
+
throw new DataSourceError(`Failed to batch get SOL balances: ${err.message}`, 'rpc', { cause: err });
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
265
|
/**
|
|
266
|
-
*
|
|
266
|
+
* Get single token balance for a wallet.
|
|
267
267
|
*/
|
|
268
268
|
async getTokenBalance(walletAddress, tokenMint) {
|
|
269
269
|
try {
|
|
270
270
|
return await this.rpc.getTokenBalance(walletAddress, tokenMint);
|
|
271
271
|
}
|
|
272
272
|
catch (err) {
|
|
273
|
-
throw new DataSourceError(
|
|
273
|
+
throw new DataSourceError(`Failed to get token balance (${walletAddress}): ${err.message}`, 'rpc', { cause: err });
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
/**
|
|
277
|
-
*
|
|
277
|
+
* Batch get token balances for multiple wallets on a specific token.
|
|
278
278
|
*/
|
|
279
279
|
async getBatchTokenBalances(walletAddresses, tokenMint) {
|
|
280
280
|
try {
|
|
281
281
|
return await this.rpc.getBatchTokenBalances(walletAddresses, tokenMint);
|
|
282
282
|
}
|
|
283
283
|
catch (err) {
|
|
284
|
-
throw new DataSourceError(
|
|
284
|
+
throw new DataSourceError(`Failed to batch get token balances: ${err.message}`, 'rpc', { cause: err });
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
/**
|
|
288
|
-
*
|
|
288
|
+
* Get all token accounts for a single wallet.
|
|
289
289
|
*/
|
|
290
290
|
async getTokenAccountsByOwner(walletAddress) {
|
|
291
291
|
try {
|
|
292
292
|
return await this.rpc.getTokenAccountsByOwner(walletAddress);
|
|
293
293
|
}
|
|
294
294
|
catch (err) {
|
|
295
|
-
throw new DataSourceError(
|
|
295
|
+
throw new DataSourceError(`Failed to get token account list (${walletAddress}): ${err.message}`, 'rpc', { cause: err });
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
/**
|
|
299
|
-
*
|
|
299
|
+
* Get complete balance data for wallet group on a specific token.
|
|
300
300
|
*
|
|
301
|
-
*
|
|
301
|
+
* Fetches from RPC in real-time, updates DataStore, then returns BalancesFile.
|
|
302
302
|
*/
|
|
303
303
|
async getWalletBalances(walletAddresses, ca, groupId) {
|
|
304
304
|
try {
|
|
305
|
-
//
|
|
305
|
+
// Fetch SOL and Token balances in parallel
|
|
306
306
|
const [solBalances, tokenBalances] = await Promise.all([
|
|
307
307
|
this.rpc.getBatchSolBalances(walletAddresses),
|
|
308
308
|
this.rpc.getBatchTokenBalances(walletAddresses, ca),
|
|
@@ -314,7 +314,7 @@ export class DataSource {
|
|
|
314
314
|
tokenBalance: tokenBalances[addr] ?? 0,
|
|
315
315
|
updatedAt: now,
|
|
316
316
|
}));
|
|
317
|
-
//
|
|
317
|
+
// Batch write to DataStore
|
|
318
318
|
await this.store.updateBalancesBatch(ca, groupId, balances);
|
|
319
319
|
return {
|
|
320
320
|
ca,
|
|
@@ -324,240 +324,240 @@ export class DataSource {
|
|
|
324
324
|
};
|
|
325
325
|
}
|
|
326
326
|
catch (err) {
|
|
327
|
-
// fallback:
|
|
327
|
+
// fallback: return historical data from DataStore
|
|
328
328
|
const cached = this.store.getBalances(ca, groupId);
|
|
329
329
|
if (cached) {
|
|
330
330
|
return cached;
|
|
331
331
|
}
|
|
332
|
-
throw new DataSourceError(
|
|
332
|
+
throw new DataSourceError(`Failed to get wallet balances: ${err.message}`, 'rpc', { cause: err });
|
|
333
333
|
}
|
|
334
334
|
}
|
|
335
335
|
// ============================================================
|
|
336
|
-
//
|
|
336
|
+
// Transaction Status -- RpcAdapter
|
|
337
337
|
// ============================================================
|
|
338
338
|
/**
|
|
339
|
-
*
|
|
339
|
+
* Query confirmation status of a single transaction.
|
|
340
340
|
*/
|
|
341
341
|
async getTransactionStatus(txHash) {
|
|
342
342
|
try {
|
|
343
343
|
return await this.rpc.getTransactionStatus(txHash);
|
|
344
344
|
}
|
|
345
345
|
catch (err) {
|
|
346
|
-
throw new DataSourceError(
|
|
346
|
+
throw new DataSourceError(`Failed to get transaction status (${txHash}): ${err.message}`, 'rpc', { cause: err });
|
|
347
347
|
}
|
|
348
348
|
}
|
|
349
349
|
/**
|
|
350
|
-
*
|
|
350
|
+
* Get transaction details.
|
|
351
351
|
*/
|
|
352
352
|
async getTransactionDetail(txHash) {
|
|
353
353
|
try {
|
|
354
354
|
return await this.rpc.getTransactionDetail(txHash);
|
|
355
355
|
}
|
|
356
356
|
catch (err) {
|
|
357
|
-
throw new DataSourceError(
|
|
357
|
+
throw new DataSourceError(`Failed to get transaction details (${txHash}): ${err.message}`, 'rpc', { cause: err });
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
360
|
/**
|
|
361
|
-
*
|
|
361
|
+
* Batch query transaction statuses.
|
|
362
362
|
*/
|
|
363
363
|
async getBatchTransactionStatuses(txHashes) {
|
|
364
364
|
try {
|
|
365
365
|
return await this.rpc.getBatchTransactionStatuses(txHashes);
|
|
366
366
|
}
|
|
367
367
|
catch (err) {
|
|
368
|
-
throw new DataSourceError(
|
|
368
|
+
throw new DataSourceError(`Failed to batch get transaction statuses: ${err.message}`, 'rpc', { cause: err });
|
|
369
369
|
}
|
|
370
370
|
}
|
|
371
371
|
// ============================================================
|
|
372
|
-
// Bundle
|
|
372
|
+
// Bundle Operations -- JitoAdapter
|
|
373
373
|
// ============================================================
|
|
374
374
|
/**
|
|
375
|
-
*
|
|
375
|
+
* Send bundle to Jito Block Engine.
|
|
376
376
|
*/
|
|
377
377
|
async sendBundle(base64Txs) {
|
|
378
378
|
try {
|
|
379
379
|
return await this.jito.sendBundle(base64Txs);
|
|
380
380
|
}
|
|
381
381
|
catch (err) {
|
|
382
|
-
throw new DataSourceError(
|
|
382
|
+
throw new DataSourceError(`Failed to send bundle: ${err.message}`, 'jito', { cause: err });
|
|
383
383
|
}
|
|
384
384
|
}
|
|
385
385
|
/**
|
|
386
|
-
*
|
|
386
|
+
* Query bundle status.
|
|
387
387
|
*/
|
|
388
388
|
async getBundleStatus(bundleId) {
|
|
389
389
|
try {
|
|
390
390
|
return await this.jito.getBundleStatus(bundleId);
|
|
391
391
|
}
|
|
392
392
|
catch (err) {
|
|
393
|
-
throw new DataSourceError(
|
|
393
|
+
throw new DataSourceError(`Failed to get bundle status (${bundleId}): ${err.message}`, 'jito', { cause: err });
|
|
394
394
|
}
|
|
395
395
|
}
|
|
396
396
|
/**
|
|
397
|
-
*
|
|
397
|
+
* Wait for bundle confirmation.
|
|
398
398
|
*/
|
|
399
399
|
async waitForBundleConfirmation(bundleId, options) {
|
|
400
400
|
try {
|
|
401
401
|
return await this.jito.waitForBundleConfirmation(bundleId, options);
|
|
402
402
|
}
|
|
403
403
|
catch (err) {
|
|
404
|
-
throw new DataSourceError(
|
|
404
|
+
throw new DataSourceError(`Failed waiting for bundle confirmation (${bundleId}): ${err.message}`, 'jito', { cause: err });
|
|
405
405
|
}
|
|
406
406
|
}
|
|
407
407
|
// ============================================================
|
|
408
|
-
//
|
|
408
|
+
// Single Transaction Sending -- JitoAdapter (sendTransaction)
|
|
409
409
|
// ============================================================
|
|
410
410
|
/**
|
|
411
|
-
*
|
|
411
|
+
* Send single transaction via Jito.
|
|
412
412
|
*/
|
|
413
413
|
async sendTransaction(base64Tx) {
|
|
414
414
|
try {
|
|
415
415
|
return await this.jito.sendTransaction(base64Tx);
|
|
416
416
|
}
|
|
417
417
|
catch (err) {
|
|
418
|
-
throw new DataSourceError(
|
|
418
|
+
throw new DataSourceError(`Failed to send transaction: ${err.message}`, 'jito', { cause: err });
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
421
|
/**
|
|
422
|
-
*
|
|
422
|
+
* Confirm transaction status via RPC.
|
|
423
423
|
*/
|
|
424
424
|
async confirmTransactionByRpc(connection, signature, timeoutMs) {
|
|
425
425
|
try {
|
|
426
426
|
return await this.jito.confirmTransactionByRpc(connection, signature, timeoutMs);
|
|
427
427
|
}
|
|
428
428
|
catch (err) {
|
|
429
|
-
throw new DataSourceError(
|
|
429
|
+
throw new DataSourceError(`Failed to confirm transaction (${signature}): ${err.message}`, 'rpc', { cause: err });
|
|
430
430
|
}
|
|
431
431
|
}
|
|
432
432
|
// ============================================================
|
|
433
|
-
//
|
|
433
|
+
// Trade Records -- DataStore (local files)
|
|
434
434
|
// ============================================================
|
|
435
435
|
/**
|
|
436
|
-
*
|
|
436
|
+
* Get trade records.
|
|
437
437
|
*/
|
|
438
438
|
getTransactions(ca, groupId) {
|
|
439
439
|
return this.store.getTransactions(ca, groupId);
|
|
440
440
|
}
|
|
441
441
|
/**
|
|
442
|
-
*
|
|
442
|
+
* Get trade records for a specific wallet.
|
|
443
443
|
*/
|
|
444
444
|
getTransactionsByWallet(ca, groupId, wallet) {
|
|
445
445
|
return this.store.getTransactionsByWallet(ca, groupId, wallet);
|
|
446
446
|
}
|
|
447
447
|
/**
|
|
448
|
-
*
|
|
448
|
+
* Append trade records.
|
|
449
449
|
*/
|
|
450
450
|
async appendTransaction(ca, groupId, tx) {
|
|
451
451
|
try {
|
|
452
452
|
await this.store.appendTransaction(ca, groupId, tx);
|
|
453
453
|
}
|
|
454
454
|
catch (err) {
|
|
455
|
-
throw new DataSourceError(
|
|
455
|
+
throw new DataSourceError(`Failed to write trade records: ${err.message}`, 'store', { cause: err });
|
|
456
456
|
}
|
|
457
457
|
}
|
|
458
458
|
// ============================================================
|
|
459
|
-
//
|
|
459
|
+
// Position Data -- DataStore (local files)
|
|
460
460
|
// ============================================================
|
|
461
461
|
/**
|
|
462
|
-
*
|
|
462
|
+
* Get position data.
|
|
463
463
|
*/
|
|
464
464
|
getHoldings(ca, groupId) {
|
|
465
465
|
return this.store.getHoldings(ca, groupId);
|
|
466
466
|
}
|
|
467
467
|
/**
|
|
468
|
-
*
|
|
468
|
+
* Update position data.
|
|
469
469
|
*/
|
|
470
470
|
async updateHolding(ca, groupId, wallet, update) {
|
|
471
471
|
try {
|
|
472
472
|
await this.store.updateHolding(ca, groupId, wallet, update);
|
|
473
473
|
}
|
|
474
474
|
catch (err) {
|
|
475
|
-
throw new DataSourceError(
|
|
475
|
+
throw new DataSourceError(`Failed to update positions: ${err.message}`, 'store', { cause: err });
|
|
476
476
|
}
|
|
477
477
|
}
|
|
478
478
|
// ============================================================
|
|
479
|
-
//
|
|
479
|
+
// Balance Snapshots -- DataStore (local files)
|
|
480
480
|
// ============================================================
|
|
481
481
|
/**
|
|
482
|
-
*
|
|
482
|
+
* Get locally cached balance snapshot.
|
|
483
483
|
*/
|
|
484
484
|
getBalancesSnapshot(ca, groupId) {
|
|
485
485
|
return this.store.getBalances(ca, groupId);
|
|
486
486
|
}
|
|
487
487
|
/**
|
|
488
|
-
*
|
|
488
|
+
* Update balance snapshot.
|
|
489
489
|
*/
|
|
490
490
|
async updateBalance(ca, groupId, wallet, balance) {
|
|
491
491
|
try {
|
|
492
492
|
await this.store.updateBalance(ca, groupId, wallet, balance);
|
|
493
493
|
}
|
|
494
494
|
catch (err) {
|
|
495
|
-
throw new DataSourceError(
|
|
495
|
+
throw new DataSourceError(`Failed to update balance snapshot: ${err.message}`, 'store', { cause: err });
|
|
496
496
|
}
|
|
497
497
|
}
|
|
498
498
|
// ============================================================
|
|
499
|
-
//
|
|
499
|
+
// Global Data -- DataStore
|
|
500
500
|
// ============================================================
|
|
501
501
|
/**
|
|
502
|
-
*
|
|
502
|
+
* Get fee configuration.
|
|
503
503
|
*/
|
|
504
504
|
getFeeConfig() {
|
|
505
505
|
return this.store.getFeeConfig();
|
|
506
506
|
}
|
|
507
507
|
/**
|
|
508
|
-
*
|
|
508
|
+
* Save fee configuration.
|
|
509
509
|
*/
|
|
510
510
|
async saveFeeConfig(config) {
|
|
511
511
|
try {
|
|
512
512
|
await this.store.saveFeeConfig(config);
|
|
513
513
|
}
|
|
514
514
|
catch (err) {
|
|
515
|
-
throw new DataSourceError(
|
|
515
|
+
throw new DataSourceError(`Failed to save fee configuration: ${err.message}`, 'store', { cause: err });
|
|
516
516
|
}
|
|
517
517
|
}
|
|
518
518
|
// ============================================================
|
|
519
|
-
// DataStore
|
|
519
|
+
// DataStore Utility Methods
|
|
520
520
|
// ============================================================
|
|
521
521
|
/**
|
|
522
|
-
*
|
|
522
|
+
* List all token CAs with data.
|
|
523
523
|
*/
|
|
524
524
|
listTokens() {
|
|
525
525
|
return this.store.listTokens();
|
|
526
526
|
}
|
|
527
527
|
/**
|
|
528
|
-
*
|
|
528
|
+
* List all wallet group IDs with data for a token.
|
|
529
529
|
*/
|
|
530
530
|
listGroups(ca) {
|
|
531
531
|
return this.store.listGroups(ca);
|
|
532
532
|
}
|
|
533
533
|
// ============================================================
|
|
534
|
-
//
|
|
534
|
+
// Transaction Tracking -- TxTracker
|
|
535
535
|
// ============================================================
|
|
536
536
|
/**
|
|
537
|
-
*
|
|
538
|
-
*
|
|
537
|
+
* Track a single transaction.
|
|
538
|
+
* Auto-writes to DataStore after polling confirmation (trade records + positions + balances).
|
|
539
539
|
*/
|
|
540
540
|
async trackTransaction(txHash, context, options) {
|
|
541
541
|
return this.tracker.trackTransaction(txHash, context, options);
|
|
542
542
|
}
|
|
543
543
|
/**
|
|
544
|
-
*
|
|
545
|
-
*
|
|
544
|
+
* Track a Jito Bundle.
|
|
545
|
+
* Processes internal transactions individually after bundle confirmation and writes to DataStore.
|
|
546
546
|
*/
|
|
547
547
|
async trackBundle(bundleId, txHashes, context, options) {
|
|
548
548
|
return this.tracker.trackBundle(bundleId, txHashes, context, options);
|
|
549
549
|
}
|
|
550
550
|
/**
|
|
551
|
-
*
|
|
551
|
+
* Batch track multiple independent transactions.
|
|
552
552
|
*/
|
|
553
553
|
async trackBatch(entries, options) {
|
|
554
554
|
return this.tracker.trackBatch(entries, options);
|
|
555
555
|
}
|
|
556
556
|
// ============================================================
|
|
557
|
-
//
|
|
557
|
+
// Health Check
|
|
558
558
|
// ============================================================
|
|
559
559
|
/**
|
|
560
|
-
*
|
|
560
|
+
* Check health status of all data sources.
|
|
561
561
|
*/
|
|
562
562
|
async healthCheck() {
|
|
563
563
|
const [codexResult, rpcResult, jitoResult] = await Promise.all([
|
|
@@ -572,43 +572,43 @@ export class DataSource {
|
|
|
572
572
|
};
|
|
573
573
|
}
|
|
574
574
|
// ============================================================
|
|
575
|
-
//
|
|
575
|
+
// Direct adapter access (advanced use)
|
|
576
576
|
// ============================================================
|
|
577
|
-
/**
|
|
577
|
+
/** Get underlying CodexAdapter (only when DataSource interface is insufficient) */
|
|
578
578
|
getCodexAdapter() {
|
|
579
579
|
return this.codex;
|
|
580
580
|
}
|
|
581
|
-
/**
|
|
581
|
+
/** Get underlying RpcAdapter */
|
|
582
582
|
getRpcAdapter() {
|
|
583
583
|
return this.rpc;
|
|
584
584
|
}
|
|
585
|
-
/**
|
|
585
|
+
/** Get underlying JitoAdapter */
|
|
586
586
|
getJitoAdapter() {
|
|
587
587
|
return this.jito;
|
|
588
588
|
}
|
|
589
|
-
/**
|
|
589
|
+
/** Get underlying DataStore */
|
|
590
590
|
getDataStore() {
|
|
591
591
|
return this.store;
|
|
592
592
|
}
|
|
593
|
-
/**
|
|
593
|
+
/** Get underlying TxTracker */
|
|
594
594
|
getTxTracker() {
|
|
595
595
|
return this.tracker;
|
|
596
596
|
}
|
|
597
597
|
// ============================================================
|
|
598
|
-
//
|
|
598
|
+
// Internal utility methods
|
|
599
599
|
// ============================================================
|
|
600
600
|
/**
|
|
601
|
-
*
|
|
602
|
-
* @param updatedAt
|
|
603
|
-
* @param ttlMs
|
|
604
|
-
* @returns true
|
|
601
|
+
* Check if timestamp has expired.
|
|
602
|
+
* @param updatedAt Last update time (ms)
|
|
603
|
+
* @param ttlMs Time-to-live (ms)
|
|
604
|
+
* @returns true if expired
|
|
605
605
|
*/
|
|
606
606
|
isExpired(updatedAt, ttlMs) {
|
|
607
607
|
return Date.now() - updatedAt > ttlMs;
|
|
608
608
|
}
|
|
609
609
|
/**
|
|
610
|
-
*
|
|
611
|
-
* Codex API
|
|
610
|
+
* Infer DEX type from DEX/exchange name.
|
|
611
|
+
* Maps Codex API exchangeName to TokenInfoFile.dex enum.
|
|
612
612
|
*/
|
|
613
613
|
inferDex(exchangeName) {
|
|
614
614
|
if (!exchangeName)
|
|
@@ -624,17 +624,17 @@ export class DataSource {
|
|
|
624
624
|
return 'launchlab';
|
|
625
625
|
if (name.includes('meteora'))
|
|
626
626
|
return 'meteora';
|
|
627
|
-
//
|
|
627
|
+
// Default to pump when unrecognized
|
|
628
628
|
return 'pump';
|
|
629
629
|
}
|
|
630
630
|
}
|
|
631
631
|
// ============================================================
|
|
632
|
-
//
|
|
632
|
+
// Singleton Management
|
|
633
633
|
// ============================================================
|
|
634
634
|
let _instance = null;
|
|
635
635
|
/**
|
|
636
|
-
*
|
|
637
|
-
*
|
|
636
|
+
* Get DataSource global singleton.
|
|
637
|
+
* Standard entry point for command layer:
|
|
638
638
|
* import { getDataSource } from '../data-source.js';
|
|
639
639
|
* const ds = getDataSource();
|
|
640
640
|
*/
|
|
@@ -645,7 +645,7 @@ export function getDataSource() {
|
|
|
645
645
|
return _instance;
|
|
646
646
|
}
|
|
647
647
|
/**
|
|
648
|
-
*
|
|
648
|
+
* Reset singleton (for testing or re-initialization after config changes).
|
|
649
649
|
*/
|
|
650
650
|
export function resetDataSource() {
|
|
651
651
|
_instance = null;
|