@elizaos/plugin-wallet 2.0.0-beta.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/LICENSE +21 -0
- package/README.md +64 -0
- package/auto-enable.ts +76 -0
- package/dist/LpManagementService-BWrQ5-cO.mjs +353 -0
- package/dist/MockLpService-D_Apn4Fd.mjs +99 -0
- package/dist/aerodrome-CfnESC32.mjs +890 -0
- package/dist/chunk-hT5z_Zn9.mjs +35 -0
- package/dist/index.d.mts +34727 -0
- package/dist/index.mjs +21590 -0
- package/dist/lib/server-wallet-trade.d.mts +34 -0
- package/dist/lib/server-wallet-trade.mjs +306 -0
- package/dist/meteora-BPX39hZo.mjs +22640 -0
- package/dist/orca-Bybp1HXO.mjs +249 -0
- package/dist/pancakeswp-CkEXlXti.mjs +604 -0
- package/dist/plugin-ZO_MTyd0.mjs +529 -0
- package/dist/raydium-rfaM9yEf.mjs +539 -0
- package/dist/sdk/index.d.mts +32492 -0
- package/dist/sdk/index.mjs +6415 -0
- package/dist/types-D5252NZk.mjs +487 -0
- package/dist/uniswap-CReXgXVN.mjs +573 -0
- package/dist/wallet-action.d.mts +6 -0
- package/dist/wallet-action.mjs +820 -0
- package/package.json +152 -0
- package/src/actions/failure-codes.ts +79 -0
- package/src/actions/index.ts +1 -0
- package/src/analytics/birdeye/actions/wallet-search-address.ts +9 -0
- package/src/analytics/birdeye/birdeye-task.ts +175 -0
- package/src/analytics/birdeye/birdeye.ts +813 -0
- package/src/analytics/birdeye/constants.ts +74 -0
- package/src/analytics/birdeye/providers/agent-portfolio-provider.ts +18 -0
- package/src/analytics/birdeye/providers/market.ts +227 -0
- package/src/analytics/birdeye/providers/portfolio-factory.test.ts +138 -0
- package/src/analytics/birdeye/providers/portfolio-factory.ts +252 -0
- package/src/analytics/birdeye/providers/trending.ts +365 -0
- package/src/analytics/birdeye/providers/wallet.ts +14 -0
- package/src/analytics/birdeye/search-category.test.ts +207 -0
- package/src/analytics/birdeye/search-category.ts +506 -0
- package/src/analytics/birdeye/service.ts +992 -0
- package/src/analytics/birdeye/tasks/birdeye.ts +232 -0
- package/src/analytics/birdeye/types/api/common.ts +305 -0
- package/src/analytics/birdeye/types/api/defi.ts +220 -0
- package/src/analytics/birdeye/types/api/pair.ts +200 -0
- package/src/analytics/birdeye/types/api/search.ts +86 -0
- package/src/analytics/birdeye/types/api/token.ts +635 -0
- package/src/analytics/birdeye/types/api/trader.ts +76 -0
- package/src/analytics/birdeye/types/api/wallet.ts +181 -0
- package/src/analytics/birdeye/types/shared.ts +106 -0
- package/src/analytics/birdeye/utils.ts +700 -0
- package/src/analytics/dexscreener/errors.ts +28 -0
- package/src/analytics/dexscreener/index.ts +3 -0
- package/src/analytics/dexscreener/search-category.test.ts +49 -0
- package/src/analytics/dexscreener/search-category.ts +42 -0
- package/src/analytics/dexscreener/service.ts +595 -0
- package/src/analytics/dexscreener/types.ts +128 -0
- package/src/analytics/lpinfo/index.d.ts +7 -0
- package/src/analytics/lpinfo/index.ts +52 -0
- package/src/analytics/lpinfo/kamino/README.md +102 -0
- package/src/analytics/lpinfo/kamino/index.ts +24 -0
- package/src/analytics/lpinfo/kamino/providers/kaminoLiquidityProvider.ts +422 -0
- package/src/analytics/lpinfo/kamino/providers/kaminoPoolProvider.ts +365 -0
- package/src/analytics/lpinfo/kamino/providers/kaminoProvider.ts +496 -0
- package/src/analytics/lpinfo/kamino/services/kaminoLiquidityService.ts +1123 -0
- package/src/analytics/lpinfo/kamino/services/kaminoService.ts +758 -0
- package/src/analytics/lpinfo/steer/README.md +169 -0
- package/src/analytics/lpinfo/steer/index.ts +23 -0
- package/src/analytics/lpinfo/steer/providers/steerLiquidityProvider.ts +544 -0
- package/src/analytics/lpinfo/steer/services/steerLiquidityService.ts +1690 -0
- package/src/analytics/lpinfo/steer/steer-display-types.ts +99 -0
- package/src/analytics/news/index.ts +52 -0
- package/src/analytics/news/interfaces/types.ts +222 -0
- package/src/analytics/news/providers/defiNewsProvider.ts +734 -0
- package/src/analytics/news/services/newsDataService.ts +332 -0
- package/src/analytics/news/utils/formatters.ts +151 -0
- package/src/analytics/token-info/action.ts +240 -0
- package/src/analytics/token-info/index.ts +3 -0
- package/src/analytics/token-info/params.ts +215 -0
- package/src/analytics/token-info/providers.ts +681 -0
- package/src/analytics/token-info/service.ts +168 -0
- package/src/analytics/token-info/types.ts +74 -0
- package/src/audit/audit-log.ts +45 -0
- package/src/browser-shim/build-shim.ts +123 -0
- package/src/browser-shim/index.ts +5 -0
- package/src/browser-shim/shim.template.js +563 -0
- package/src/chains/evm/.github/workflows/npm-deploy.yml +112 -0
- package/src/chains/evm/LICENSE +21 -0
- package/src/chains/evm/README.md +106 -0
- package/src/chains/evm/actions/helpers.ts +147 -0
- package/src/chains/evm/actions/swap.ts +839 -0
- package/src/chains/evm/actions/transfer.ts +254 -0
- package/src/chains/evm/biome.json +61 -0
- package/src/chains/evm/bridge-router.ts +660 -0
- package/src/chains/evm/build.ts +89 -0
- package/src/chains/evm/chain-handler.ts +416 -0
- package/src/chains/evm/constants.ts +23 -0
- package/src/chains/evm/contracts/artifacts/OZGovernor.json +1707 -0
- package/src/chains/evm/contracts/artifacts/TimelockController.json +1007 -0
- package/src/chains/evm/contracts/artifacts/VoteToken.json +895 -0
- package/src/chains/evm/dex/aerodrome/index.ts +34 -0
- package/src/chains/evm/dex/aerodrome/services/AerodromeLpService.ts +558 -0
- package/src/chains/evm/dex/aerodrome/types.ts +318 -0
- package/src/chains/evm/dex/pancakeswp/index.ts +35 -0
- package/src/chains/evm/dex/pancakeswp/services/PancakeSwapV3LpService.ts +743 -0
- package/src/chains/evm/dex/pancakeswp/types.ts +65 -0
- package/src/chains/evm/dex/uniswap/index.ts +35 -0
- package/src/chains/evm/dex/uniswap/services/UniswapV3LpService.ts +759 -0
- package/src/chains/evm/dex/uniswap/types.ts +390 -0
- package/src/chains/evm/generated/specs/spec-helpers.ts +73 -0
- package/src/chains/evm/generated/specs/specs.ts +151 -0
- package/src/chains/evm/gov-router.ts +250 -0
- package/src/chains/evm/index.browser.ts +16 -0
- package/src/chains/evm/index.ts +31 -0
- package/src/chains/evm/prompts.ts +193 -0
- package/src/chains/evm/providers/get-balance.ts +123 -0
- package/src/chains/evm/providers/wallet.ts +715 -0
- package/src/chains/evm/routes/sign.ts +333 -0
- package/src/chains/evm/rpc-providers.ts +410 -0
- package/src/chains/evm/service.ts +140 -0
- package/src/chains/evm/templates/index.ts +10 -0
- package/src/chains/evm/types/index.ts +432 -0
- package/src/chains/evm/vitest.config.ts +18 -0
- package/src/chains/registry.ts +668 -0
- package/src/chains/solana/README.md +367 -0
- package/src/chains/wallet-action.ts +533 -0
- package/src/chains/wallet-router.test.ts +296 -0
- package/src/contracts.ts +65 -0
- package/src/core-augmentation.ts +10 -0
- package/src/index.ts +71 -0
- package/src/lib/server-wallet-trade.ts +192 -0
- package/src/lib/wallet-export-guard.ts +330 -0
- package/src/lp/actions/liquidity.ts +827 -0
- package/src/lp/e2e/real-token-tests.ts +428 -0
- package/src/lp/e2e/scenarios.ts +470 -0
- package/src/lp/e2e/test-utils.ts +145 -0
- package/src/lp/lp-manager-entry.ts +303 -0
- package/src/lp/services/ConcentratedLiquidityService.ts +120 -0
- package/src/lp/services/DexInteractionService.ts +226 -0
- package/src/lp/services/LpManagementService.test.ts +148 -0
- package/src/lp/services/LpManagementService.ts +632 -0
- package/src/lp/services/UserLpProfileService.ts +163 -0
- package/src/lp/services/VaultService.ts +153 -0
- package/src/lp/services/YieldOptimizationService.ts +344 -0
- package/src/lp/services/__tests__/MockLpService.ts +146 -0
- package/src/lp/tasks/LpAutoRebalanceTask.ts +117 -0
- package/src/lp/tasks/__tests__/LpAutoRebalanceTask.test.ts +370 -0
- package/src/lp/types.ts +582 -0
- package/src/lp/utils/solanaClient.ts +143 -0
- package/src/plugin.ts +125 -0
- package/src/policy/policy.ts +19 -0
- package/src/providers/canonical-provider.ts +27 -0
- package/src/providers/unified-wallet-provider.ts +79 -0
- package/src/register-routes.ts +11 -0
- package/src/routes/plugin.ts +47 -0
- package/src/routes/wallet-market-overview-route.ts +869 -0
- package/src/sdk/abi.ts +258 -0
- package/src/sdk/bridge/abis.ts +126 -0
- package/src/sdk/bridge/client.ts +518 -0
- package/src/sdk/bridge/index.ts +56 -0
- package/src/sdk/bridge/solana.ts +604 -0
- package/src/sdk/bridge/types.ts +202 -0
- package/src/sdk/convenience.ts +347 -0
- package/src/sdk/escrow/MutualStakeEscrow.ts +480 -0
- package/src/sdk/escrow/types.ts +64 -0
- package/src/sdk/escrow/verifiers.ts +73 -0
- package/src/sdk/identity/erc8004.ts +692 -0
- package/src/sdk/identity/reputation.ts +449 -0
- package/src/sdk/identity/uaid.ts +497 -0
- package/src/sdk/identity/validation.ts +372 -0
- package/src/sdk/index.ts +763 -0
- package/src/sdk/policy/SpendingPolicy.ts +260 -0
- package/src/sdk/policy/UptoBillingPolicy.ts +320 -0
- package/src/sdk/router/PaymentRouter.ts +215 -0
- package/src/sdk/router/index.ts +8 -0
- package/src/sdk/swap/SwapModule.ts +310 -0
- package/src/sdk/swap/abi.ts +117 -0
- package/src/sdk/swap/index.ts +34 -0
- package/src/sdk/swap/types.ts +135 -0
- package/src/sdk/tokens/decimals.ts +140 -0
- package/src/sdk/tokens/registry.ts +911 -0
- package/src/sdk/tokens/solana.ts +419 -0
- package/src/sdk/tokens/transfers.ts +327 -0
- package/src/sdk/types.ts +158 -0
- package/src/sdk/wallet-core.ts +115 -0
- package/src/sdk/x402/budget.ts +168 -0
- package/src/sdk/x402/chains/abstract/index.ts +280 -0
- package/src/sdk/x402/client.ts +320 -0
- package/src/sdk/x402/index.ts +46 -0
- package/src/sdk/x402/middleware.ts +92 -0
- package/src/sdk/x402/multi-asset.ts +144 -0
- package/src/sdk/x402/types.ts +156 -0
- package/src/services/wallet-backend-service.ts +328 -0
- package/src/types/wallet-router.ts +227 -0
- package/src/utils/intent-trajectory.ts +106 -0
- package/src/wallet/backend.ts +62 -0
- package/src/wallet/errors.ts +49 -0
- package/src/wallet/index.ts +27 -0
- package/src/wallet/local-eoa-backend.ts +201 -0
- package/src/wallet/pending.ts +60 -0
- package/src/wallet/select-backend.ts +47 -0
- package/src/wallet/steward-backend.ts +161 -0
- package/src/wallet-action.ts +1 -0
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
// @ts-nocheck ā legacy code from absorbed plugins (lp-manager, lpinfo, dexscreener, defi-news, birdeye); strict types pending cleanup
|
|
2
|
+
import type { IAgentRuntime, Memory, Provider, State } from "@elizaos/core";
|
|
3
|
+
import type { NewsDataService } from "../services/newsDataService";
|
|
4
|
+
|
|
5
|
+
interface CoinGeckoDefiData {
|
|
6
|
+
defi_market_cap: string;
|
|
7
|
+
eth_market_cap: string;
|
|
8
|
+
defi_to_eth_ratio: string;
|
|
9
|
+
trading_volume_24h: string;
|
|
10
|
+
defi_dominance: string;
|
|
11
|
+
top_coin_name: string;
|
|
12
|
+
top_coin_defi_dominance: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface CoinGeckoGlobalCryptoData {
|
|
16
|
+
active_cryptocurrencies: number;
|
|
17
|
+
markets: number;
|
|
18
|
+
total_market_cap: { usd: number };
|
|
19
|
+
total_volume: { usd: number };
|
|
20
|
+
market_cap_change_percentage_24h_usd: number;
|
|
21
|
+
market_cap_percentage?: Record<string, number>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface CoinGeckoSearchResult {
|
|
25
|
+
id: string;
|
|
26
|
+
platforms?: { solana?: string };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface CoinGeckoCoinData {
|
|
30
|
+
name: string;
|
|
31
|
+
symbol: string;
|
|
32
|
+
market_data?: {
|
|
33
|
+
current_price?: { usd?: number };
|
|
34
|
+
market_cap?: { usd?: number };
|
|
35
|
+
total_volume?: { usd?: number };
|
|
36
|
+
price_change_percentage_24h?: number;
|
|
37
|
+
price_change_percentage_7d?: number;
|
|
38
|
+
price_change_percentage_30d?: number;
|
|
39
|
+
};
|
|
40
|
+
community_data?: {
|
|
41
|
+
twitter_followers?: number;
|
|
42
|
+
reddit_subscribers?: number;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface CoinGeckoService {
|
|
47
|
+
getGlobalDefiData(): Promise<CoinGeckoDefiData>;
|
|
48
|
+
getGlobalCryptoData(): Promise<CoinGeckoGlobalCryptoData>;
|
|
49
|
+
searchCoin(symbol: string): Promise<CoinGeckoSearchResult[]>;
|
|
50
|
+
getCoinData(tokenId: string): Promise<CoinGeckoCoinData>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface BirdeyeSymbolOption {
|
|
54
|
+
symbol: string;
|
|
55
|
+
address: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface BirdeyeLookupService {
|
|
59
|
+
lookupSymbolAllChains(symbol: string): Promise<BirdeyeSymbolOption[]>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface SolanaTokenInfoService {
|
|
63
|
+
getAddressType(address: string): Promise<string>;
|
|
64
|
+
getTokenSymbol(publicKey: object): Promise<string | null | undefined>;
|
|
65
|
+
}
|
|
66
|
+
const DEFI_NEWS_TEXT_LIMIT = 4000;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* DeFi News Provider
|
|
70
|
+
*
|
|
71
|
+
* Automatically provides comprehensive DeFi and crypto market context to conversations.
|
|
72
|
+
* This provider is dynamic and fetches fresh data on each request.
|
|
73
|
+
*
|
|
74
|
+
* The provider aggregates data from:
|
|
75
|
+
* - Global DeFi market statistics (market cap, volume, dominance) - requires CoinGecko service
|
|
76
|
+
* - Global crypto market data (total market cap, active cryptocurrencies, dominance) - requires CoinGecko service
|
|
77
|
+
* - Latest crypto news from Brave New Coin RSS feed (top 5 articles) - always available
|
|
78
|
+
* - Token-specific data when mentioned - requires CoinGecko and optional Birdeye services
|
|
79
|
+
*
|
|
80
|
+
* The data is formatted as a comprehensive market report that can be used
|
|
81
|
+
* by the agent to provide informed responses about DeFi and crypto markets.
|
|
82
|
+
*
|
|
83
|
+
* Note: The CoinGecko service should be provided by the analytics plugin or similar.
|
|
84
|
+
* If not available, the provider will still work with news data only.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* // The provider is automatically called by the framework
|
|
88
|
+
* // No manual invocation needed - just add to plugin.providers array
|
|
89
|
+
*/
|
|
90
|
+
export const defiNewsProvider: Provider = {
|
|
91
|
+
name: "DEFI_NEWS",
|
|
92
|
+
description:
|
|
93
|
+
"Provides DeFi market data, global crypto statistics, token information, and real-world crypto news",
|
|
94
|
+
descriptionCompressed:
|
|
95
|
+
"provide DeFi market data, global crypto statistic, token information, real-world crypto new",
|
|
96
|
+
dynamic: true,
|
|
97
|
+
contexts: ["finance", "crypto", "wallet"],
|
|
98
|
+
contextGate: { anyOf: ["finance", "crypto", "wallet"] },
|
|
99
|
+
cacheStable: false,
|
|
100
|
+
cacheScope: "turn",
|
|
101
|
+
roleGate: { minRole: "USER" },
|
|
102
|
+
get: async (runtime: IAgentRuntime, message: Memory, _state: State) => {
|
|
103
|
+
console.log("DEFI_NEWS provider called");
|
|
104
|
+
|
|
105
|
+
let defiNewsInfo = "";
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
// Get services - CoinGecko from analytics or similar plugin, NewsData from this plugin
|
|
109
|
+
const coinGeckoService = runtime.getService(
|
|
110
|
+
"COINGECKO_SERVICE",
|
|
111
|
+
) as CoinGeckoService | null;
|
|
112
|
+
const newsDataService = runtime.getService(
|
|
113
|
+
"NEWS_DATA_SERVICE",
|
|
114
|
+
) as NewsDataService;
|
|
115
|
+
|
|
116
|
+
if (!newsDataService) {
|
|
117
|
+
console.log("NewsData service not available");
|
|
118
|
+
return {
|
|
119
|
+
data: {},
|
|
120
|
+
values: {},
|
|
121
|
+
text: "DeFi News service not available.",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log("DeFi News services found, generating report...");
|
|
126
|
+
|
|
127
|
+
// Check if a specific token is mentioned in the message
|
|
128
|
+
const messageText = message.content?.text || "";
|
|
129
|
+
|
|
130
|
+
defiNewsInfo += `=== DEFI & CRYPTO MARKET REPORT ===\n\n`;
|
|
131
|
+
|
|
132
|
+
// Extract symbols dynamically from the message
|
|
133
|
+
let extractedSymbols = extractSymbols(messageText, "loose");
|
|
134
|
+
extractedSymbols = filterTokenSymbols(extractedSymbols);
|
|
135
|
+
|
|
136
|
+
// Also check for token names (bitcoin, ethereum, etc.)
|
|
137
|
+
const namedSymbol = getSymbolFromTokenName(messageText);
|
|
138
|
+
if (namedSymbol && !extractedSymbols.includes(namedSymbol)) {
|
|
139
|
+
extractedSymbols.unshift(namedSymbol); // Add to front
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.log(`Extracted symbols: ${extractedSymbols.join(", ")}`);
|
|
143
|
+
|
|
144
|
+
// If token symbols are detected and services are available, look them up
|
|
145
|
+
if (extractedSymbols.length > 0 && coinGeckoService) {
|
|
146
|
+
// Try to get Birdeye service for symbol lookup
|
|
147
|
+
const birdeyeService = runtime.getService(
|
|
148
|
+
"birdeye",
|
|
149
|
+
) as BirdeyeLookupService | null;
|
|
150
|
+
const solanaService = runtime.getService(
|
|
151
|
+
"chain_solana",
|
|
152
|
+
) as SolanaTokenInfoService | null;
|
|
153
|
+
|
|
154
|
+
if (birdeyeService && solanaService) {
|
|
155
|
+
// Process up to 3 tokens
|
|
156
|
+
for (const detectedSymbol of extractedSymbols.slice(0, 3)) {
|
|
157
|
+
console.log(`Looking up symbol: ${detectedSymbol}`);
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// Look up token by symbol across all chains
|
|
161
|
+
const options =
|
|
162
|
+
await birdeyeService.lookupSymbolAllChains(detectedSymbol);
|
|
163
|
+
const exactOptions = options.filter(
|
|
164
|
+
(t) => t.symbol.toUpperCase() === detectedSymbol.toUpperCase(),
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
console.log(
|
|
168
|
+
`Birdeye found ${exactOptions.length} exact matches for ${detectedSymbol}`,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
if (exactOptions.length > 0) {
|
|
172
|
+
// Use the first exact match (usually the most popular/main token)
|
|
173
|
+
const tokenOption = exactOptions[0];
|
|
174
|
+
const tokenCA = tokenOption.address;
|
|
175
|
+
|
|
176
|
+
console.log(`Using token: ${tokenOption.symbol} at ${tokenCA}`);
|
|
177
|
+
|
|
178
|
+
// Verify it's actually a token
|
|
179
|
+
const addressType = await solanaService.getAddressType(tokenCA);
|
|
180
|
+
|
|
181
|
+
if (addressType === "Token") {
|
|
182
|
+
const tokenData = await getTokenInfoByAddress(
|
|
183
|
+
coinGeckoService,
|
|
184
|
+
solanaService,
|
|
185
|
+
tokenCA,
|
|
186
|
+
tokenOption.symbol,
|
|
187
|
+
);
|
|
188
|
+
if (tokenData) {
|
|
189
|
+
defiNewsInfo += tokenData;
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
console.log(
|
|
193
|
+
`Address ${tokenCA} is not a Token, it's a ${addressType}`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
console.log(
|
|
198
|
+
`No exact matches found for ${detectedSymbol}, skipping...`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.log(
|
|
203
|
+
`Error looking up ${detectedSymbol} via Birdeye:`,
|
|
204
|
+
error,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
// Fallback to CoinGecko ID lookup for major tokens
|
|
210
|
+
console.log(
|
|
211
|
+
"Birdeye or Solana service not available, using CoinGecko fallback",
|
|
212
|
+
);
|
|
213
|
+
for (const detectedSymbol of extractedSymbols.slice(0, 1)) {
|
|
214
|
+
const coingeckoId = getCoinGeckoIdFromSymbol(detectedSymbol);
|
|
215
|
+
if (coingeckoId) {
|
|
216
|
+
const tokenData = await getTokenInfo(
|
|
217
|
+
coinGeckoService,
|
|
218
|
+
coingeckoId,
|
|
219
|
+
);
|
|
220
|
+
defiNewsInfo += tokenData;
|
|
221
|
+
break; // Only one token in fallback mode
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Get global DeFi data (if CoinGecko service is available)
|
|
228
|
+
if (coinGeckoService) {
|
|
229
|
+
const globalDefiData = await getGlobalDefiData(coinGeckoService);
|
|
230
|
+
defiNewsInfo += globalDefiData;
|
|
231
|
+
|
|
232
|
+
// Get global crypto market data
|
|
233
|
+
const globalCryptoData = await getGlobalCryptoData(coinGeckoService);
|
|
234
|
+
defiNewsInfo += globalCryptoData;
|
|
235
|
+
} else {
|
|
236
|
+
console.log("CoinGecko service not available, skipping market data");
|
|
237
|
+
defiNewsInfo +=
|
|
238
|
+
"ā ļø Market data unavailable (CoinGecko service not configured)\n\n";
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Get latest crypto news (always available)
|
|
242
|
+
const latestNews = await getLatestCryptoNews(newsDataService);
|
|
243
|
+
defiNewsInfo += latestNews;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error("Error in DeFi News provider:", error);
|
|
246
|
+
defiNewsInfo = `Error generating DeFi News report: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const data = {
|
|
250
|
+
defiNews: defiNewsInfo,
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const values = {};
|
|
254
|
+
|
|
255
|
+
const text = `${defiNewsInfo}\n`.slice(0, DEFI_NEWS_TEXT_LIMIT);
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
data,
|
|
259
|
+
values,
|
|
260
|
+
text,
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Extract symbols from text
|
|
267
|
+
* Dynamically extracts token symbols from natural language
|
|
268
|
+
*
|
|
269
|
+
* @param text - The text to extract symbols from
|
|
270
|
+
* @param mode - "strict" only matches $SYMBOL format, "loose" matches various patterns
|
|
271
|
+
* @returns Array of detected symbols
|
|
272
|
+
*/
|
|
273
|
+
export const extractSymbols = (
|
|
274
|
+
text: string,
|
|
275
|
+
// loose mode will try to extract more symbols but may include false positives
|
|
276
|
+
// strict mode will only extract symbols that are clearly formatted as a symbol using $SOL format
|
|
277
|
+
mode: "strict" | "loose" = "loose",
|
|
278
|
+
): string[] => {
|
|
279
|
+
if (!text?.matchAll) return [];
|
|
280
|
+
const symbols = new Set<string>();
|
|
281
|
+
|
|
282
|
+
// Match patterns
|
|
283
|
+
const patterns =
|
|
284
|
+
mode === "strict"
|
|
285
|
+
? [
|
|
286
|
+
// $SYMBOL format
|
|
287
|
+
/\$([A-Z0-9]{2,10})\b/gi,
|
|
288
|
+
// $SYMBOL format with lowercase
|
|
289
|
+
/\$([a-z0-9]{2,10})\b/gi,
|
|
290
|
+
]
|
|
291
|
+
: [
|
|
292
|
+
// $SYMBOL format
|
|
293
|
+
/\$([A-Z0-9]{2,10})\b/gi,
|
|
294
|
+
// After articles (a/an)
|
|
295
|
+
/\b(?:a|an)\s+([A-Z0-9]{2,10})\b/gi,
|
|
296
|
+
// Standalone caps
|
|
297
|
+
/\b[A-Z0-9]{2,10}\b/g,
|
|
298
|
+
// Quoted symbols
|
|
299
|
+
/["']([A-Z0-9]{2,10})["']/gi,
|
|
300
|
+
// Common price patterns
|
|
301
|
+
/\b([A-Z0-9]{2,10})\/USD\b/gi,
|
|
302
|
+
/\b([A-Z0-9]{2,10})-USD\b/gi,
|
|
303
|
+
];
|
|
304
|
+
|
|
305
|
+
// Extract all matches
|
|
306
|
+
patterns.forEach((pattern) => {
|
|
307
|
+
const matches = text.matchAll(pattern);
|
|
308
|
+
for (const match of matches) {
|
|
309
|
+
const symbol = (match[1] || match[0]).toUpperCase();
|
|
310
|
+
symbols.add(symbol);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
return Array.from(symbols);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Filter extracted symbols to remove common words and validate potential tokens
|
|
319
|
+
*/
|
|
320
|
+
function filterTokenSymbols(symbols: string[]): string[] {
|
|
321
|
+
// Common words to exclude (not tokens)
|
|
322
|
+
const excludeWords = new Set([
|
|
323
|
+
"THE",
|
|
324
|
+
"AND",
|
|
325
|
+
"FOR",
|
|
326
|
+
"NOT",
|
|
327
|
+
"BUT",
|
|
328
|
+
"GET",
|
|
329
|
+
"SET",
|
|
330
|
+
"CAN",
|
|
331
|
+
"ARE",
|
|
332
|
+
"WAS",
|
|
333
|
+
"HAS",
|
|
334
|
+
"HAD",
|
|
335
|
+
"HER",
|
|
336
|
+
"HIS",
|
|
337
|
+
"OUR",
|
|
338
|
+
"YOU",
|
|
339
|
+
"ALL",
|
|
340
|
+
"OUT",
|
|
341
|
+
"NEW",
|
|
342
|
+
"OLD",
|
|
343
|
+
"NOW",
|
|
344
|
+
"SEE",
|
|
345
|
+
"OWN",
|
|
346
|
+
"TWO",
|
|
347
|
+
"WAY",
|
|
348
|
+
"WHO",
|
|
349
|
+
"ITS",
|
|
350
|
+
"MAY",
|
|
351
|
+
"DAY",
|
|
352
|
+
"USE",
|
|
353
|
+
"USD",
|
|
354
|
+
"EUR",
|
|
355
|
+
"GBP",
|
|
356
|
+
"JPY",
|
|
357
|
+
"CNY", // Fiat currencies
|
|
358
|
+
]);
|
|
359
|
+
|
|
360
|
+
return symbols.filter((symbol) => {
|
|
361
|
+
// Must be 2-10 characters
|
|
362
|
+
if (symbol.length < 2 || symbol.length > 10) return false;
|
|
363
|
+
|
|
364
|
+
// Exclude common words
|
|
365
|
+
if (excludeWords.has(symbol)) return false;
|
|
366
|
+
|
|
367
|
+
// Should have at least one letter
|
|
368
|
+
if (!/[A-Z]/.test(symbol)) return false;
|
|
369
|
+
|
|
370
|
+
return true;
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Map common token names to their symbols
|
|
376
|
+
*/
|
|
377
|
+
function getSymbolFromTokenName(text: string): string | null {
|
|
378
|
+
const lowerText = text.toLowerCase();
|
|
379
|
+
|
|
380
|
+
const tokenNameToSymbol: Record<string, string> = {
|
|
381
|
+
bitcoin: "BTC",
|
|
382
|
+
ethereum: "ETH",
|
|
383
|
+
solana: "SOL",
|
|
384
|
+
cardano: "ADA",
|
|
385
|
+
polkadot: "DOT",
|
|
386
|
+
avalanche: "AVAX",
|
|
387
|
+
polygon: "MATIC",
|
|
388
|
+
uniswap: "UNI",
|
|
389
|
+
chainlink: "LINK",
|
|
390
|
+
"binance coin": "BNB",
|
|
391
|
+
ripple: "XRP",
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
for (const [name, symbol] of Object.entries(tokenNameToSymbol)) {
|
|
395
|
+
if (lowerText.includes(name)) {
|
|
396
|
+
return symbol;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Get CoinGecko ID from token symbol
|
|
405
|
+
* Fallback mapping for major tokens when Birdeye is not available
|
|
406
|
+
*/
|
|
407
|
+
function getCoinGeckoIdFromSymbol(symbol: string): string | null {
|
|
408
|
+
const symbolToCoinGeckoId: Record<string, string> = {
|
|
409
|
+
BTC: "bitcoin",
|
|
410
|
+
ETH: "ethereum",
|
|
411
|
+
SOL: "solana",
|
|
412
|
+
ADA: "cardano",
|
|
413
|
+
DOT: "polkadot",
|
|
414
|
+
AVAX: "avalanche",
|
|
415
|
+
MATIC: "matic-network",
|
|
416
|
+
UNI: "uniswap",
|
|
417
|
+
LINK: "chainlink",
|
|
418
|
+
BNB: "binancecoin",
|
|
419
|
+
XRP: "ripple",
|
|
420
|
+
USDC: "usd-coin",
|
|
421
|
+
USDT: "tether",
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
return symbolToCoinGeckoId[symbol.toUpperCase()] || null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Get global DeFi market data
|
|
429
|
+
*/
|
|
430
|
+
async function getGlobalDefiData(
|
|
431
|
+
coinGeckoService: CoinGeckoService,
|
|
432
|
+
): Promise<string> {
|
|
433
|
+
let defiInfo = "š GLOBAL DEFI MARKET DATA:\n\n";
|
|
434
|
+
|
|
435
|
+
try {
|
|
436
|
+
const defiData = await coinGeckoService.getGlobalDefiData();
|
|
437
|
+
|
|
438
|
+
defiInfo += `š° DeFi Market Cap: $${parseFloat(defiData.defi_market_cap).toLocaleString()}\n`;
|
|
439
|
+
defiInfo += `š ETH Market Cap: $${parseFloat(defiData.eth_market_cap).toLocaleString()}\n`;
|
|
440
|
+
defiInfo += `š DeFi/ETH Ratio: ${parseFloat(defiData.defi_to_eth_ratio).toFixed(4)}\n`;
|
|
441
|
+
defiInfo += `š 24h Trading Volume: $${parseFloat(defiData.trading_volume_24h).toLocaleString()}\n`;
|
|
442
|
+
defiInfo += `šÆ DeFi Dominance: ${parseFloat(defiData.defi_dominance).toFixed(2)}%\n`;
|
|
443
|
+
defiInfo += `š Top DeFi Coin: ${defiData.top_coin_name} (${defiData.top_coin_defi_dominance.toFixed(2)}% dominance)\n\n`;
|
|
444
|
+
} catch (error) {
|
|
445
|
+
console.error("Error fetching global DeFi data:", error);
|
|
446
|
+
defiInfo += "Error fetching DeFi data. Please try again later.\n\n";
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return defiInfo;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Get global crypto market data
|
|
454
|
+
*/
|
|
455
|
+
async function getGlobalCryptoData(
|
|
456
|
+
coinGeckoService: CoinGeckoService,
|
|
457
|
+
): Promise<string> {
|
|
458
|
+
let cryptoInfo = "š GLOBAL CRYPTO MARKET DATA:\n\n";
|
|
459
|
+
|
|
460
|
+
try {
|
|
461
|
+
const cryptoData = await coinGeckoService.getGlobalCryptoData();
|
|
462
|
+
|
|
463
|
+
cryptoInfo += `šŖ Active Cryptocurrencies: ${cryptoData.active_cryptocurrencies.toLocaleString()}\n`;
|
|
464
|
+
cryptoInfo += `š± Active Markets: ${cryptoData.markets.toLocaleString()}\n`;
|
|
465
|
+
cryptoInfo += `š° Total Market Cap: $${(cryptoData.total_market_cap.usd / 1e9).toFixed(2)}B\n`;
|
|
466
|
+
cryptoInfo += `š 24h Volume: $${(cryptoData.total_volume.usd / 1e9).toFixed(2)}B\n`;
|
|
467
|
+
cryptoInfo += `š 24h Market Cap Change: ${cryptoData.market_cap_change_percentage_24h_usd.toFixed(2)}%\n`;
|
|
468
|
+
|
|
469
|
+
if (cryptoData.market_cap_percentage) {
|
|
470
|
+
cryptoInfo += "\nš MARKET DOMINANCE:\n";
|
|
471
|
+
const topCoins = Object.entries(cryptoData.market_cap_percentage)
|
|
472
|
+
.sort((a, b) => (b[1] as number) - (a[1] as number))
|
|
473
|
+
.slice(0, 5) as [string, number][];
|
|
474
|
+
topCoins.forEach(([coin, percentage]) => {
|
|
475
|
+
cryptoInfo += ` ⢠${coin.toUpperCase()}: ${percentage.toFixed(2)}%\n`;
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
cryptoInfo += "\n";
|
|
480
|
+
} catch (error) {
|
|
481
|
+
console.error("Error fetching global crypto data:", error);
|
|
482
|
+
cryptoInfo +=
|
|
483
|
+
"Error fetching crypto market data. Please try again later.\n\n";
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return cryptoInfo;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Get latest crypto news
|
|
491
|
+
*/
|
|
492
|
+
async function getLatestCryptoNews(
|
|
493
|
+
newsDataService: NewsDataService,
|
|
494
|
+
): Promise<string> {
|
|
495
|
+
let newsInfo = "š° LATEST CRYPTO NEWS:\n\n";
|
|
496
|
+
|
|
497
|
+
try {
|
|
498
|
+
const articles = await newsDataService.getLatestNews({
|
|
499
|
+
limit: 5,
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
if (articles.length === 0) {
|
|
503
|
+
newsInfo += "No recent news articles available.\n\n";
|
|
504
|
+
return newsInfo;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
articles.forEach((article, index) => {
|
|
508
|
+
newsInfo += `${index + 1}. ${article.title}\n`;
|
|
509
|
+
|
|
510
|
+
if (article.description) {
|
|
511
|
+
const shortDesc = article.description.substring(0, 100);
|
|
512
|
+
newsInfo += ` ${shortDesc}${article.description.length > 100 ? "..." : ""}\n`;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (article.pubDate) {
|
|
516
|
+
const pubDate = new Date(article.pubDate);
|
|
517
|
+
newsInfo += ` š
${pubDate.toLocaleDateString()} | š° ${article.source_id}\n`;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (article.link) {
|
|
521
|
+
newsInfo += ` š ${article.link}\n`;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
newsInfo += "\n";
|
|
525
|
+
});
|
|
526
|
+
} catch (error) {
|
|
527
|
+
console.error("Error fetching latest crypto news:", error);
|
|
528
|
+
newsInfo += "Error fetching news. Please try again later.\n\n";
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return newsInfo;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Get token information by contract address
|
|
536
|
+
* Uses Birdeye + CoinGecko to fetch comprehensive token data
|
|
537
|
+
*/
|
|
538
|
+
async function getTokenInfoByAddress(
|
|
539
|
+
coinGeckoService: CoinGeckoService,
|
|
540
|
+
solanaService: SolanaTokenInfoService,
|
|
541
|
+
tokenAddress: string,
|
|
542
|
+
symbol: string,
|
|
543
|
+
): Promise<string | null> {
|
|
544
|
+
let tokenInfo = `š TOKEN INFORMATION:\n\n`;
|
|
545
|
+
|
|
546
|
+
try {
|
|
547
|
+
// Import PublicKey if needed
|
|
548
|
+
const { PublicKey } = await import("@solana/web3.js");
|
|
549
|
+
|
|
550
|
+
// Get token symbol from Solana (for verification)
|
|
551
|
+
let tokenSymbol = symbol;
|
|
552
|
+
try {
|
|
553
|
+
const onChainSymbol = await solanaService.getTokenSymbol(
|
|
554
|
+
new PublicKey(tokenAddress),
|
|
555
|
+
);
|
|
556
|
+
if (onChainSymbol) {
|
|
557
|
+
tokenSymbol = onChainSymbol;
|
|
558
|
+
}
|
|
559
|
+
} catch (_error) {
|
|
560
|
+
console.log("Could not fetch on-chain symbol, using provided:", symbol);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
console.log(
|
|
564
|
+
`Fetching CoinGecko data for ${tokenSymbol} at ${tokenAddress}`,
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
// Try to search CoinGecko by symbol
|
|
568
|
+
let coinData = null;
|
|
569
|
+
const searchResults = await coinGeckoService.searchCoin(tokenSymbol);
|
|
570
|
+
|
|
571
|
+
if (searchResults && searchResults.length > 0) {
|
|
572
|
+
// Try to find exact match by Solana platform address
|
|
573
|
+
const solanaMatch = searchResults.find(
|
|
574
|
+
(coin) =>
|
|
575
|
+
coin.platforms?.solana?.toLowerCase() === tokenAddress.toLowerCase(),
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
if (solanaMatch) {
|
|
579
|
+
console.log(`Found exact Solana platform match: ${solanaMatch.id}`);
|
|
580
|
+
coinData = await coinGeckoService.getCoinData(solanaMatch.id);
|
|
581
|
+
} else {
|
|
582
|
+
// Use first result as fallback
|
|
583
|
+
console.log(`Using first search result: ${searchResults[0].id}`);
|
|
584
|
+
coinData = await coinGeckoService.getCoinData(searchResults[0].id);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (!coinData) {
|
|
589
|
+
tokenInfo += `šŖ Token: ${tokenSymbol}\n`;
|
|
590
|
+
tokenInfo += `š Address: ${tokenAddress}\n`;
|
|
591
|
+
tokenInfo += `ā ļø Detailed market data not available on CoinGecko\n\n`;
|
|
592
|
+
return tokenInfo;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Format comprehensive token data
|
|
596
|
+
tokenInfo += `šŖ ${coinData.name} (${coinData.symbol.toUpperCase()})\n`;
|
|
597
|
+
tokenInfo += `š Contract Address: ${tokenAddress}\n\n`;
|
|
598
|
+
|
|
599
|
+
if (coinData.market_data) {
|
|
600
|
+
const md = coinData.market_data;
|
|
601
|
+
tokenInfo += "šµ PRICE INFORMATION:\n";
|
|
602
|
+
if (md.current_price?.usd) {
|
|
603
|
+
tokenInfo += ` Current Price: $${md.current_price.usd.toLocaleString()}\n`;
|
|
604
|
+
}
|
|
605
|
+
if (md.market_cap?.usd) {
|
|
606
|
+
tokenInfo += ` Market Cap: $${(md.market_cap.usd / 1e9).toFixed(2)}B\n`;
|
|
607
|
+
}
|
|
608
|
+
if (md.total_volume?.usd) {
|
|
609
|
+
tokenInfo += ` 24h Volume: $${(md.total_volume.usd / 1e9).toFixed(2)}B\n`;
|
|
610
|
+
}
|
|
611
|
+
if (md.market_cap_rank) {
|
|
612
|
+
tokenInfo += ` Market Cap Rank: #${md.market_cap_rank}\n`;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
tokenInfo += "\nš PRICE CHANGES:\n";
|
|
616
|
+
if (md.price_change_percentage_24h !== undefined) {
|
|
617
|
+
const emoji = md.price_change_percentage_24h >= 0 ? "š" : "š";
|
|
618
|
+
tokenInfo += ` ${emoji} 24h: ${md.price_change_percentage_24h.toFixed(2)}%\n`;
|
|
619
|
+
}
|
|
620
|
+
if (md.price_change_percentage_7d !== undefined) {
|
|
621
|
+
const emoji = md.price_change_percentage_7d >= 0 ? "š" : "š";
|
|
622
|
+
tokenInfo += ` ${emoji} 7d: ${md.price_change_percentage_7d.toFixed(2)}%\n`;
|
|
623
|
+
}
|
|
624
|
+
if (md.price_change_percentage_30d !== undefined) {
|
|
625
|
+
const emoji = md.price_change_percentage_30d >= 0 ? "š" : "š";
|
|
626
|
+
tokenInfo += ` ${emoji} 30d: ${md.price_change_percentage_30d.toFixed(2)}%\n`;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (md.high_24h?.usd && md.low_24h?.usd) {
|
|
630
|
+
tokenInfo += "\nš 24H RANGE:\n";
|
|
631
|
+
tokenInfo += ` High: $${md.high_24h.usd.toLocaleString()}\n`;
|
|
632
|
+
tokenInfo += ` Low: $${md.low_24h.usd.toLocaleString()}\n`;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (coinData.community_data) {
|
|
637
|
+
const cd = coinData.community_data;
|
|
638
|
+
if (
|
|
639
|
+
cd.twitter_followers ||
|
|
640
|
+
cd.reddit_subscribers ||
|
|
641
|
+
cd.telegram_channel_user_count
|
|
642
|
+
) {
|
|
643
|
+
tokenInfo += "\nš„ COMMUNITY:\n";
|
|
644
|
+
if (cd.twitter_followers)
|
|
645
|
+
tokenInfo += ` š¦ Twitter: ${cd.twitter_followers.toLocaleString()} followers\n`;
|
|
646
|
+
if (cd.reddit_subscribers)
|
|
647
|
+
tokenInfo += ` š“ Reddit: ${cd.reddit_subscribers.toLocaleString()} subscribers\n`;
|
|
648
|
+
if (cd.telegram_channel_user_count)
|
|
649
|
+
tokenInfo += ` āļø Telegram: ${cd.telegram_channel_user_count.toLocaleString()} members\n`;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (coinData.developer_data) {
|
|
654
|
+
const dd = coinData.developer_data;
|
|
655
|
+
if (dd.stars || dd.forks) {
|
|
656
|
+
tokenInfo += "\nš» DEVELOPER ACTIVITY:\n";
|
|
657
|
+
if (dd.stars)
|
|
658
|
+
tokenInfo += ` ā GitHub Stars: ${dd.stars.toLocaleString()}\n`;
|
|
659
|
+
if (dd.forks)
|
|
660
|
+
tokenInfo += ` š± Forks: ${dd.forks.toLocaleString()}\n`;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
tokenInfo += "\n";
|
|
665
|
+
} catch (error) {
|
|
666
|
+
console.error("Error fetching token info by address:", error);
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return tokenInfo;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Get token information
|
|
675
|
+
* This is a helper function that can be used for specific token queries
|
|
676
|
+
*/
|
|
677
|
+
export async function getTokenInfo(
|
|
678
|
+
coinGeckoService: CoinGeckoService,
|
|
679
|
+
tokenId: string,
|
|
680
|
+
): Promise<string> {
|
|
681
|
+
let tokenInfo = `š TOKEN INFORMATION:\n\n`;
|
|
682
|
+
|
|
683
|
+
try {
|
|
684
|
+
const tokenData = await coinGeckoService.getCoinData(tokenId);
|
|
685
|
+
|
|
686
|
+
tokenInfo += `šŖ ${tokenData.name} (${tokenData.symbol.toUpperCase()})\n\n`;
|
|
687
|
+
|
|
688
|
+
if (tokenData.market_data) {
|
|
689
|
+
const md = tokenData.market_data;
|
|
690
|
+
tokenInfo += "šµ PRICE INFORMATION:\n";
|
|
691
|
+
if (md.current_price?.usd) {
|
|
692
|
+
tokenInfo += ` Current Price: $${md.current_price.usd.toLocaleString()}\n`;
|
|
693
|
+
}
|
|
694
|
+
if (md.market_cap?.usd) {
|
|
695
|
+
tokenInfo += ` Market Cap: $${(md.market_cap.usd / 1e9).toFixed(2)}B\n`;
|
|
696
|
+
}
|
|
697
|
+
if (md.total_volume?.usd) {
|
|
698
|
+
tokenInfo += ` 24h Volume: $${(md.total_volume.usd / 1e9).toFixed(2)}B\n`;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
tokenInfo += "\nš PRICE CHANGES:\n";
|
|
702
|
+
if (md.price_change_percentage_24h !== undefined) {
|
|
703
|
+
const emoji = md.price_change_percentage_24h >= 0 ? "š" : "š";
|
|
704
|
+
tokenInfo += ` ${emoji} 24h: ${md.price_change_percentage_24h.toFixed(2)}%\n`;
|
|
705
|
+
}
|
|
706
|
+
if (md.price_change_percentage_7d !== undefined) {
|
|
707
|
+
const emoji = md.price_change_percentage_7d >= 0 ? "š" : "š";
|
|
708
|
+
tokenInfo += ` ${emoji} 7d: ${md.price_change_percentage_7d.toFixed(2)}%\n`;
|
|
709
|
+
}
|
|
710
|
+
if (md.price_change_percentage_30d !== undefined) {
|
|
711
|
+
const emoji = md.price_change_percentage_30d >= 0 ? "š" : "š";
|
|
712
|
+
tokenInfo += ` ${emoji} 30d: ${md.price_change_percentage_30d.toFixed(2)}%\n`;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if (tokenData.community_data) {
|
|
717
|
+
const cd = tokenData.community_data;
|
|
718
|
+
if (cd.twitter_followers || cd.reddit_subscribers) {
|
|
719
|
+
tokenInfo += "\nš„ COMMUNITY:\n";
|
|
720
|
+
if (cd.twitter_followers)
|
|
721
|
+
tokenInfo += ` š¦ Twitter: ${cd.twitter_followers.toLocaleString()} followers\n`;
|
|
722
|
+
if (cd.reddit_subscribers)
|
|
723
|
+
tokenInfo += ` š“ Reddit: ${cd.reddit_subscribers.toLocaleString()} subscribers\n`;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
tokenInfo += "\n";
|
|
728
|
+
} catch (error) {
|
|
729
|
+
console.error("Error fetching token info:", error);
|
|
730
|
+
tokenInfo += "Error fetching token data. Please try again later.\n\n";
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
return tokenInfo;
|
|
734
|
+
}
|