@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,681 @@
|
|
|
1
|
+
// @ts-nocheck — bridges legacy analytics services behind the consolidated TOKEN_INFO registry.
|
|
2
|
+
import type { ActionResult, IAgentRuntime } from "@elizaos/core";
|
|
3
|
+
import { BirdeyeProvider } from "../birdeye/birdeye";
|
|
4
|
+
import { searchBirdeyeTokens } from "../birdeye/search-category";
|
|
5
|
+
import type { WalletPortfolioResponse } from "../birdeye/types/api/wallet";
|
|
6
|
+
import { extractAddresses } from "../birdeye/utils";
|
|
7
|
+
import type { DexScreenerService } from "../dexscreener/service";
|
|
8
|
+
import type {
|
|
9
|
+
DexScreenerBoostedToken,
|
|
10
|
+
DexScreenerPair,
|
|
11
|
+
DexScreenerProfile,
|
|
12
|
+
DexScreenerServiceResponse,
|
|
13
|
+
} from "../dexscreener/types";
|
|
14
|
+
import type { TokenInfoDispatchContext, TokenInfoProvider } from "./types";
|
|
15
|
+
|
|
16
|
+
function success(text: string, data: Record<string, unknown>): ActionResult {
|
|
17
|
+
return {
|
|
18
|
+
success: true,
|
|
19
|
+
text,
|
|
20
|
+
data: { actionName: "TOKEN_INFO", ...data },
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function failure(
|
|
25
|
+
text: string,
|
|
26
|
+
error: string,
|
|
27
|
+
data: Record<string, unknown> = {},
|
|
28
|
+
): ActionResult {
|
|
29
|
+
return {
|
|
30
|
+
success: false,
|
|
31
|
+
text,
|
|
32
|
+
error,
|
|
33
|
+
data: { actionName: "TOKEN_INFO", ...data },
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function emit(
|
|
38
|
+
context: TokenInfoDispatchContext,
|
|
39
|
+
result: ActionResult,
|
|
40
|
+
): Promise<ActionResult> {
|
|
41
|
+
await context.callback?.({
|
|
42
|
+
text: result.text ?? "",
|
|
43
|
+
actions: ["TOKEN_INFO"],
|
|
44
|
+
data: result.data,
|
|
45
|
+
});
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function describeError(error: unknown): string {
|
|
50
|
+
return error instanceof Error ? error.message : String(error);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getDexScreenerService(
|
|
54
|
+
runtime: IAgentRuntime,
|
|
55
|
+
): DexScreenerService | null {
|
|
56
|
+
const service = runtime.getService(
|
|
57
|
+
"dexscreener",
|
|
58
|
+
) as DexScreenerService | null;
|
|
59
|
+
return service && typeof service.search === "function" ? service : null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function topPairs(
|
|
63
|
+
service: DexScreenerService,
|
|
64
|
+
pairs: readonly DexScreenerPair[],
|
|
65
|
+
limit = 5,
|
|
66
|
+
): string {
|
|
67
|
+
return pairs
|
|
68
|
+
.slice(0, limit)
|
|
69
|
+
.map((pair, index) => {
|
|
70
|
+
const price = service.formatPrice(pair.priceUsd || pair.priceNative);
|
|
71
|
+
const volume = pair.volume?.h24
|
|
72
|
+
? service.formatUsdValue(pair.volume.h24)
|
|
73
|
+
: "n/a";
|
|
74
|
+
const liquidity = pair.liquidity?.usd
|
|
75
|
+
? service.formatUsdValue(pair.liquidity.usd)
|
|
76
|
+
: "n/a";
|
|
77
|
+
return `${index + 1}. ${pair.baseToken.symbol}/${pair.quoteToken.symbol} on ${pair.dexId} (${pair.chainId}) - price ${price}, volume24h ${volume}, liquidity ${liquidity}`;
|
|
78
|
+
})
|
|
79
|
+
.join("\n");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function requireDexResult<T>(
|
|
83
|
+
result: DexScreenerServiceResponse<T>,
|
|
84
|
+
fallbackError: string,
|
|
85
|
+
): T {
|
|
86
|
+
if (!result.success || !result.data) {
|
|
87
|
+
throw new Error(String(result.error ?? fallbackError));
|
|
88
|
+
}
|
|
89
|
+
return result.data;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function executeDexScreener(
|
|
93
|
+
context: TokenInfoDispatchContext,
|
|
94
|
+
): Promise<ActionResult> {
|
|
95
|
+
const service = getDexScreenerService(context.runtime);
|
|
96
|
+
if (!service) {
|
|
97
|
+
return emit(
|
|
98
|
+
context,
|
|
99
|
+
failure("DexScreener service is not available.", "SERVICE_UNAVAILABLE", {
|
|
100
|
+
target: "dexscreener",
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const { params } = context;
|
|
106
|
+
try {
|
|
107
|
+
switch (params.subaction) {
|
|
108
|
+
case "search": {
|
|
109
|
+
const query = params.query?.trim();
|
|
110
|
+
if (!query) {
|
|
111
|
+
return emit(
|
|
112
|
+
context,
|
|
113
|
+
failure("Provide query for token search.", "MISSING_QUERY"),
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
const pairs = requireDexResult(
|
|
117
|
+
await service.search({ query }),
|
|
118
|
+
"DEXSCREENER_SEARCH_FAILED",
|
|
119
|
+
);
|
|
120
|
+
return emit(
|
|
121
|
+
context,
|
|
122
|
+
success(
|
|
123
|
+
`Token search results for "${query}":\n${topPairs(service, pairs)}`,
|
|
124
|
+
{
|
|
125
|
+
target: "dexscreener",
|
|
126
|
+
subaction: "search",
|
|
127
|
+
query,
|
|
128
|
+
pairs: pairs.slice(0, 10),
|
|
129
|
+
},
|
|
130
|
+
),
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
case "token": {
|
|
134
|
+
const tokenAddress = params.tokenAddress ?? params.address;
|
|
135
|
+
if (!tokenAddress) {
|
|
136
|
+
return emit(
|
|
137
|
+
context,
|
|
138
|
+
failure(
|
|
139
|
+
"Provide tokenAddress or address.",
|
|
140
|
+
"MISSING_TOKEN_ADDRESS",
|
|
141
|
+
),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
const pairs = requireDexResult(
|
|
145
|
+
await service.getTokenPairs({ tokenAddress }),
|
|
146
|
+
"DEXSCREENER_TOKEN_FAILED",
|
|
147
|
+
);
|
|
148
|
+
if (pairs.length === 0) {
|
|
149
|
+
return emit(
|
|
150
|
+
context,
|
|
151
|
+
failure(`No pairs found for token ${tokenAddress}.`, "NO_PAIRS", {
|
|
152
|
+
target: "dexscreener",
|
|
153
|
+
tokenAddress,
|
|
154
|
+
}),
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
const mainPair = pairs.reduce((prev, curr) =>
|
|
158
|
+
(curr.liquidity?.usd || 0) > (prev.liquidity?.usd || 0) ? curr : prev,
|
|
159
|
+
);
|
|
160
|
+
const text = [
|
|
161
|
+
`${mainPair.baseToken.name} (${mainPair.baseToken.symbol})`,
|
|
162
|
+
`Address: ${mainPair.baseToken.address}`,
|
|
163
|
+
`Price: ${service.formatPrice(mainPair.priceUsd || mainPair.priceNative)}`,
|
|
164
|
+
`24h change: ${service.formatPriceChange(mainPair.priceChange?.h24 ?? 0)}`,
|
|
165
|
+
`24h volume: ${service.formatUsdValue(mainPair.volume?.h24 ?? 0)}`,
|
|
166
|
+
`Top pairs:\n${topPairs(service, pairs, 3)}`,
|
|
167
|
+
].join("\n");
|
|
168
|
+
return emit(
|
|
169
|
+
context,
|
|
170
|
+
success(text, {
|
|
171
|
+
target: "dexscreener",
|
|
172
|
+
subaction: "token",
|
|
173
|
+
tokenAddress,
|
|
174
|
+
pairs,
|
|
175
|
+
}),
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
case "trending": {
|
|
179
|
+
const timeframe = params.timeframe ?? "24h";
|
|
180
|
+
const limit = Math.min(25, Math.max(1, params.limit ?? 10));
|
|
181
|
+
const pairs = requireDexResult(
|
|
182
|
+
await service.getTrending({ timeframe, limit }),
|
|
183
|
+
"DEXSCREENER_TRENDING_FAILED",
|
|
184
|
+
);
|
|
185
|
+
return emit(
|
|
186
|
+
context,
|
|
187
|
+
success(
|
|
188
|
+
`Trending tokens (${timeframe}):\n${topPairs(service, pairs, limit)}`,
|
|
189
|
+
{
|
|
190
|
+
target: "dexscreener",
|
|
191
|
+
subaction: "trending",
|
|
192
|
+
timeframe,
|
|
193
|
+
pairs,
|
|
194
|
+
},
|
|
195
|
+
),
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
case "new-pairs": {
|
|
199
|
+
const limit = Math.min(25, Math.max(1, params.limit ?? 10));
|
|
200
|
+
const pairs = requireDexResult(
|
|
201
|
+
await service.getNewPairs({ chain: params.chain, limit }),
|
|
202
|
+
"DEXSCREENER_NEW_PAIRS_FAILED",
|
|
203
|
+
);
|
|
204
|
+
return emit(
|
|
205
|
+
context,
|
|
206
|
+
success(
|
|
207
|
+
`New trading pairs${params.chain ? ` on ${params.chain}` : ""}:\n${topPairs(service, pairs, limit)}`,
|
|
208
|
+
{
|
|
209
|
+
target: "dexscreener",
|
|
210
|
+
subaction: "new-pairs",
|
|
211
|
+
chain: params.chain,
|
|
212
|
+
pairs,
|
|
213
|
+
},
|
|
214
|
+
),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
case "chain-pairs": {
|
|
218
|
+
if (!params.chain) {
|
|
219
|
+
return emit(
|
|
220
|
+
context,
|
|
221
|
+
failure("Provide chain for chain-pairs.", "MISSING_CHAIN"),
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
const limit = Math.min(25, Math.max(1, params.limit ?? 10));
|
|
225
|
+
const sortBy = params.sortBy ?? "volume";
|
|
226
|
+
const pairs = requireDexResult(
|
|
227
|
+
await service.getPairsByChain({ chain: params.chain, sortBy, limit }),
|
|
228
|
+
"DEXSCREENER_CHAIN_PAIRS_FAILED",
|
|
229
|
+
);
|
|
230
|
+
return emit(
|
|
231
|
+
context,
|
|
232
|
+
success(
|
|
233
|
+
`Top ${params.chain} pairs by ${sortBy}:\n${topPairs(service, pairs, limit)}`,
|
|
234
|
+
{
|
|
235
|
+
target: "dexscreener",
|
|
236
|
+
subaction: "chain-pairs",
|
|
237
|
+
chain: params.chain,
|
|
238
|
+
sortBy,
|
|
239
|
+
pairs,
|
|
240
|
+
},
|
|
241
|
+
),
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
case "boosted": {
|
|
245
|
+
const result: DexScreenerServiceResponse<DexScreenerBoostedToken[]> =
|
|
246
|
+
params.top
|
|
247
|
+
? await service.getTopBoostedTokens()
|
|
248
|
+
: await service.getLatestBoostedTokens();
|
|
249
|
+
const tokens = requireDexResult(result, "DEXSCREENER_BOOSTED_FAILED");
|
|
250
|
+
const text = tokens
|
|
251
|
+
.slice(0, Math.min(10, params.limit ?? 10))
|
|
252
|
+
.map(
|
|
253
|
+
(token, index) =>
|
|
254
|
+
`${index + 1}. ${token.tokenAddress} on ${token.chainId} - boost ${token.amount}, total ${token.totalAmount}`,
|
|
255
|
+
)
|
|
256
|
+
.join("\n");
|
|
257
|
+
return emit(
|
|
258
|
+
context,
|
|
259
|
+
success(`${params.top ? "Top" : "Latest"} boosted tokens:\n${text}`, {
|
|
260
|
+
target: "dexscreener",
|
|
261
|
+
subaction: "boosted",
|
|
262
|
+
tokens,
|
|
263
|
+
}),
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
case "profiles": {
|
|
267
|
+
const result: DexScreenerServiceResponse<DexScreenerProfile[]> =
|
|
268
|
+
await service.getLatestTokenProfiles();
|
|
269
|
+
const profiles = requireDexResult(
|
|
270
|
+
result,
|
|
271
|
+
"DEXSCREENER_PROFILES_FAILED",
|
|
272
|
+
);
|
|
273
|
+
const text = profiles
|
|
274
|
+
.slice(0, Math.min(10, params.limit ?? 10))
|
|
275
|
+
.map(
|
|
276
|
+
(profile, index) =>
|
|
277
|
+
`${index + 1}. ${profile.tokenAddress} on ${profile.chainId}${profile.description ? ` - ${profile.description}` : ""}`,
|
|
278
|
+
)
|
|
279
|
+
.join("\n");
|
|
280
|
+
return emit(
|
|
281
|
+
context,
|
|
282
|
+
success(`Latest token profiles:\n${text}`, {
|
|
283
|
+
target: "dexscreener",
|
|
284
|
+
subaction: "profiles",
|
|
285
|
+
profiles,
|
|
286
|
+
}),
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
case "wallet":
|
|
290
|
+
return emit(
|
|
291
|
+
context,
|
|
292
|
+
failure(
|
|
293
|
+
"DexScreener does not support wallet lookup.",
|
|
294
|
+
"UNSUPPORTED_SUBACTION",
|
|
295
|
+
{
|
|
296
|
+
target: "dexscreener",
|
|
297
|
+
},
|
|
298
|
+
),
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
} catch (error) {
|
|
302
|
+
return emit(
|
|
303
|
+
context,
|
|
304
|
+
failure(describeError(error), describeError(error), {
|
|
305
|
+
target: "dexscreener",
|
|
306
|
+
subaction: params.subaction,
|
|
307
|
+
}),
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
type BirdeyePortfolioToken = WalletPortfolioResponse["data"]["items"][number];
|
|
313
|
+
|
|
314
|
+
function formatBirdeyeWallet(
|
|
315
|
+
result: WalletPortfolioResponse,
|
|
316
|
+
address: string,
|
|
317
|
+
): string {
|
|
318
|
+
const tokens = result?.data?.items?.slice(0, 10) ?? [];
|
|
319
|
+
const totalValue =
|
|
320
|
+
typeof result?.data?.totalUsd === "number"
|
|
321
|
+
? result.data.totalUsd
|
|
322
|
+
: tokens.reduce(
|
|
323
|
+
(sum: number, token: BirdeyePortfolioToken) =>
|
|
324
|
+
sum + (token.valueUsd ?? 0),
|
|
325
|
+
0,
|
|
326
|
+
);
|
|
327
|
+
const holdings = tokens
|
|
328
|
+
.map(
|
|
329
|
+
(token: BirdeyePortfolioToken) =>
|
|
330
|
+
`- ${String(token.symbol ?? "TOKEN").toUpperCase()}: $${Number(token.valueUsd ?? 0).toLocaleString()} (${token.uiAmount ?? "n/a"})`,
|
|
331
|
+
)
|
|
332
|
+
.join("\n");
|
|
333
|
+
return `Wallet ${address}\nTotal value: $${Number(totalValue ?? 0).toLocaleString()}\nTop holdings:\n${holdings}`;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async function executeBirdeye(
|
|
337
|
+
context: TokenInfoDispatchContext,
|
|
338
|
+
): Promise<ActionResult> {
|
|
339
|
+
const { params } = context;
|
|
340
|
+
try {
|
|
341
|
+
if (params.subaction === "search" || params.subaction === "token") {
|
|
342
|
+
const query = params.query ?? params.tokenAddress ?? params.address;
|
|
343
|
+
if (!query) {
|
|
344
|
+
return emit(
|
|
345
|
+
context,
|
|
346
|
+
failure(
|
|
347
|
+
"Provide query or tokenAddress for Birdeye token lookup.",
|
|
348
|
+
"MISSING_QUERY",
|
|
349
|
+
),
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
const mode =
|
|
353
|
+
params.kind === "token-address" || /^0x[a-fA-F0-9]{40}$/.test(query)
|
|
354
|
+
? "address"
|
|
355
|
+
: "symbol";
|
|
356
|
+
const result = await searchBirdeyeTokens(context.runtime, {
|
|
357
|
+
query,
|
|
358
|
+
mode,
|
|
359
|
+
});
|
|
360
|
+
return emit(
|
|
361
|
+
context,
|
|
362
|
+
success(result.text, {
|
|
363
|
+
target: "birdeye",
|
|
364
|
+
subaction: params.subaction,
|
|
365
|
+
query,
|
|
366
|
+
result,
|
|
367
|
+
}),
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (params.subaction === "wallet") {
|
|
372
|
+
const query = params.query ?? params.address;
|
|
373
|
+
if (!query) {
|
|
374
|
+
return emit(
|
|
375
|
+
context,
|
|
376
|
+
failure(
|
|
377
|
+
"Provide wallet address for Birdeye wallet lookup.",
|
|
378
|
+
"MISSING_WALLET",
|
|
379
|
+
),
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
const addresses = extractAddresses(query);
|
|
383
|
+
if (addresses.length === 0) {
|
|
384
|
+
return emit(
|
|
385
|
+
context,
|
|
386
|
+
failure("No wallet address found in query.", "MISSING_WALLET"),
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
const provider = new BirdeyeProvider(context.runtime);
|
|
390
|
+
const results = await Promise.all(
|
|
391
|
+
addresses.map(async ({ address, chain: addressChain }) => {
|
|
392
|
+
const chain = addressChain === "evm" ? "ethereum" : addressChain;
|
|
393
|
+
return {
|
|
394
|
+
address,
|
|
395
|
+
chain,
|
|
396
|
+
result: await provider.fetchWalletPortfolio(
|
|
397
|
+
{ wallet: address },
|
|
398
|
+
{ headers: { chain } },
|
|
399
|
+
),
|
|
400
|
+
};
|
|
401
|
+
}),
|
|
402
|
+
);
|
|
403
|
+
return emit(
|
|
404
|
+
context,
|
|
405
|
+
success(
|
|
406
|
+
results
|
|
407
|
+
.map(({ address, result }) => formatBirdeyeWallet(result, address))
|
|
408
|
+
.join("\n\n"),
|
|
409
|
+
{
|
|
410
|
+
target: "birdeye",
|
|
411
|
+
subaction: "wallet",
|
|
412
|
+
results,
|
|
413
|
+
},
|
|
414
|
+
),
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return emit(
|
|
419
|
+
context,
|
|
420
|
+
failure(
|
|
421
|
+
`Birdeye does not support ${params.subaction}.`,
|
|
422
|
+
"UNSUPPORTED_SUBACTION",
|
|
423
|
+
{
|
|
424
|
+
target: "birdeye",
|
|
425
|
+
},
|
|
426
|
+
),
|
|
427
|
+
);
|
|
428
|
+
} catch (error) {
|
|
429
|
+
return emit(
|
|
430
|
+
context,
|
|
431
|
+
failure(describeError(error), describeError(error), {
|
|
432
|
+
target: "birdeye",
|
|
433
|
+
subaction: params.subaction,
|
|
434
|
+
}),
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function coingeckoHeaders(runtime: IAgentRuntime): Record<string, string> {
|
|
440
|
+
const proKey = runtime.getSetting("COINGECKO_PRO_API_KEY");
|
|
441
|
+
if (typeof proKey === "string" && proKey.trim()) {
|
|
442
|
+
return { "x-cg-pro-api-key": proKey.trim() };
|
|
443
|
+
}
|
|
444
|
+
const demoKey =
|
|
445
|
+
runtime.getSetting("COINGECKO_API_KEY") ??
|
|
446
|
+
runtime.getSetting("COINGECKO_DEMO_API_KEY");
|
|
447
|
+
if (typeof demoKey === "string" && demoKey.trim()) {
|
|
448
|
+
return { "x-cg-demo-api-key": demoKey.trim() };
|
|
449
|
+
}
|
|
450
|
+
return {};
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function coingeckoBaseUrl(runtime: IAgentRuntime): string {
|
|
454
|
+
const explicit = runtime.getSetting("COINGECKO_API_URL");
|
|
455
|
+
if (typeof explicit === "string" && explicit.trim()) {
|
|
456
|
+
return explicit.trim().replace(/\/$/, "");
|
|
457
|
+
}
|
|
458
|
+
return runtime.getSetting("COINGECKO_PRO_API_KEY")
|
|
459
|
+
? "https://pro-api.coingecko.com/api/v3"
|
|
460
|
+
: "https://api.coingecko.com/api/v3";
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/** Subset of CoinGecko `/coins/{id}` JSON used by TOKEN_INFO formatting. */
|
|
464
|
+
interface CoingeckoCoinDetail {
|
|
465
|
+
id?: string;
|
|
466
|
+
name?: string;
|
|
467
|
+
symbol?: string;
|
|
468
|
+
market_data?: {
|
|
469
|
+
current_price?: { usd?: number | string };
|
|
470
|
+
market_cap?: { usd?: number | string };
|
|
471
|
+
total_volume?: { usd?: number | string };
|
|
472
|
+
price_change_percentage_24h?: number | string;
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
async function fetchCoingecko<T>(
|
|
477
|
+
runtime: IAgentRuntime,
|
|
478
|
+
path: string,
|
|
479
|
+
): Promise<T> {
|
|
480
|
+
const response = await fetch(`${coingeckoBaseUrl(runtime)}${path}`, {
|
|
481
|
+
headers: {
|
|
482
|
+
accept: "application/json",
|
|
483
|
+
...coingeckoHeaders(runtime),
|
|
484
|
+
},
|
|
485
|
+
signal: AbortSignal.timeout(15_000),
|
|
486
|
+
});
|
|
487
|
+
const payload = (await response.json().catch(() => null)) as T;
|
|
488
|
+
if (!response.ok) {
|
|
489
|
+
const message =
|
|
490
|
+
payload && typeof payload === "object" && "error" in payload
|
|
491
|
+
? String((payload as { error?: unknown }).error)
|
|
492
|
+
: `CoinGecko request failed with HTTP ${response.status}`;
|
|
493
|
+
throw new Error(message);
|
|
494
|
+
}
|
|
495
|
+
return payload;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const COINGECKO_PLATFORM_ALIASES: Record<string, string> = {
|
|
499
|
+
eth: "ethereum",
|
|
500
|
+
ethereum: "ethereum",
|
|
501
|
+
mainnet: "ethereum",
|
|
502
|
+
base: "base",
|
|
503
|
+
bsc: "binance-smart-chain",
|
|
504
|
+
binance: "binance-smart-chain",
|
|
505
|
+
polygon: "polygon-pos",
|
|
506
|
+
matic: "polygon-pos",
|
|
507
|
+
arbitrum: "arbitrum-one",
|
|
508
|
+
optimism: "optimistic-ethereum",
|
|
509
|
+
avalanche: "avalanche",
|
|
510
|
+
avax: "avalanche",
|
|
511
|
+
solana: "solana",
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
async function executeCoingecko(
|
|
515
|
+
context: TokenInfoDispatchContext,
|
|
516
|
+
): Promise<ActionResult> {
|
|
517
|
+
const { params, runtime } = context;
|
|
518
|
+
try {
|
|
519
|
+
if (params.subaction === "search") {
|
|
520
|
+
const query = params.query;
|
|
521
|
+
if (!query) {
|
|
522
|
+
return emit(
|
|
523
|
+
context,
|
|
524
|
+
failure("Provide query for CoinGecko search.", "MISSING_QUERY"),
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
const result = await fetchCoingecko<{
|
|
528
|
+
coins?: Array<{
|
|
529
|
+
id: string;
|
|
530
|
+
name: string;
|
|
531
|
+
symbol: string;
|
|
532
|
+
market_cap_rank?: number;
|
|
533
|
+
}>;
|
|
534
|
+
}>(runtime, `/search?query=${encodeURIComponent(query)}`);
|
|
535
|
+
const coins = (result.coins ?? []).slice(0, params.limit ?? 10);
|
|
536
|
+
const text = coins
|
|
537
|
+
.map(
|
|
538
|
+
(coin, index) =>
|
|
539
|
+
`${index + 1}. ${coin.name} (${coin.symbol.toUpperCase()}) - id ${coin.id}${coin.market_cap_rank ? `, rank ${coin.market_cap_rank}` : ""}`,
|
|
540
|
+
)
|
|
541
|
+
.join("\n");
|
|
542
|
+
return emit(
|
|
543
|
+
context,
|
|
544
|
+
success(`CoinGecko search results for "${query}":\n${text}`, {
|
|
545
|
+
target: "coingecko",
|
|
546
|
+
subaction: "search",
|
|
547
|
+
query,
|
|
548
|
+
coins,
|
|
549
|
+
}),
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (params.subaction === "token") {
|
|
554
|
+
const id = params.id ?? params.query;
|
|
555
|
+
const address = params.tokenAddress ?? params.address;
|
|
556
|
+
let path: string;
|
|
557
|
+
if (address && params.chain) {
|
|
558
|
+
const platform =
|
|
559
|
+
COINGECKO_PLATFORM_ALIASES[params.chain.toLowerCase()] ??
|
|
560
|
+
params.chain;
|
|
561
|
+
path = `/coins/${encodeURIComponent(platform)}/contract/${encodeURIComponent(address)}`;
|
|
562
|
+
} else if (id) {
|
|
563
|
+
path = `/coins/${encodeURIComponent(id)}?localization=false&tickers=false&market_data=true&community_data=false&developer_data=false&sparkline=false`;
|
|
564
|
+
} else {
|
|
565
|
+
return emit(
|
|
566
|
+
context,
|
|
567
|
+
failure(
|
|
568
|
+
"Provide CoinGecko coin id/query, or address with chain.",
|
|
569
|
+
"MISSING_TOKEN",
|
|
570
|
+
),
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
const coin = await fetchCoingecko<CoingeckoCoinDetail>(runtime, path);
|
|
574
|
+
const market = coin.market_data ?? {};
|
|
575
|
+
const text = [
|
|
576
|
+
`${coin.name} (${String(coin.symbol ?? "").toUpperCase()})`,
|
|
577
|
+
`CoinGecko id: ${coin.id}`,
|
|
578
|
+
`Price: $${market.current_price?.usd ?? "n/a"}`,
|
|
579
|
+
`Market cap: $${market.market_cap?.usd ?? "n/a"}`,
|
|
580
|
+
`24h volume: $${market.total_volume?.usd ?? "n/a"}`,
|
|
581
|
+
`24h change: ${market.price_change_percentage_24h ?? "n/a"}%`,
|
|
582
|
+
].join("\n");
|
|
583
|
+
return emit(
|
|
584
|
+
context,
|
|
585
|
+
success(text, {
|
|
586
|
+
target: "coingecko",
|
|
587
|
+
subaction: "token",
|
|
588
|
+
coin,
|
|
589
|
+
}),
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (params.subaction === "trending") {
|
|
594
|
+
const result = await fetchCoingecko<{
|
|
595
|
+
coins?: Array<{
|
|
596
|
+
item?: {
|
|
597
|
+
id: string;
|
|
598
|
+
name: string;
|
|
599
|
+
symbol: string;
|
|
600
|
+
market_cap_rank?: number;
|
|
601
|
+
};
|
|
602
|
+
}>;
|
|
603
|
+
}>(runtime, "/search/trending");
|
|
604
|
+
const coins = (result.coins ?? [])
|
|
605
|
+
.map((entry) => entry.item)
|
|
606
|
+
.filter(Boolean)
|
|
607
|
+
.slice(0, params.limit ?? 10);
|
|
608
|
+
const text = coins
|
|
609
|
+
.map(
|
|
610
|
+
(coin, index) =>
|
|
611
|
+
`${index + 1}. ${coin.name} (${coin.symbol.toUpperCase()}) - id ${coin.id}${coin.market_cap_rank ? `, rank ${coin.market_cap_rank}` : ""}`,
|
|
612
|
+
)
|
|
613
|
+
.join("\n");
|
|
614
|
+
return emit(
|
|
615
|
+
context,
|
|
616
|
+
success(`CoinGecko trending coins:\n${text}`, {
|
|
617
|
+
target: "coingecko",
|
|
618
|
+
subaction: "trending",
|
|
619
|
+
coins,
|
|
620
|
+
}),
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return emit(
|
|
625
|
+
context,
|
|
626
|
+
failure(
|
|
627
|
+
`CoinGecko does not support ${params.subaction}.`,
|
|
628
|
+
"UNSUPPORTED_SUBACTION",
|
|
629
|
+
{
|
|
630
|
+
target: "coingecko",
|
|
631
|
+
},
|
|
632
|
+
),
|
|
633
|
+
);
|
|
634
|
+
} catch (error) {
|
|
635
|
+
return emit(
|
|
636
|
+
context,
|
|
637
|
+
failure(describeError(error), describeError(error), {
|
|
638
|
+
target: "coingecko",
|
|
639
|
+
subaction: params.subaction,
|
|
640
|
+
}),
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export function createDexScreenerTokenInfoProvider(): TokenInfoProvider {
|
|
646
|
+
return {
|
|
647
|
+
name: "dexscreener",
|
|
648
|
+
aliases: ["dex", "dex-screener"],
|
|
649
|
+
supportedSubactions: [
|
|
650
|
+
"search",
|
|
651
|
+
"token",
|
|
652
|
+
"trending",
|
|
653
|
+
"new-pairs",
|
|
654
|
+
"chain-pairs",
|
|
655
|
+
"boosted",
|
|
656
|
+
"profiles",
|
|
657
|
+
],
|
|
658
|
+
description: "DEX pair, boosted-token, and token profile analytics.",
|
|
659
|
+
execute: executeDexScreener,
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
export function createBirdeyeTokenInfoProvider(): TokenInfoProvider {
|
|
664
|
+
return {
|
|
665
|
+
name: "birdeye",
|
|
666
|
+
aliases: ["bird-eye"],
|
|
667
|
+
supportedSubactions: ["search", "token", "wallet"],
|
|
668
|
+
description: "Birdeye token search and wallet portfolio lookup.",
|
|
669
|
+
execute: executeBirdeye,
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
export function createCoinGeckoTokenInfoProvider(): TokenInfoProvider {
|
|
674
|
+
return {
|
|
675
|
+
name: "coingecko",
|
|
676
|
+
aliases: ["coin-gecko", "gecko"],
|
|
677
|
+
supportedSubactions: ["search", "token", "trending"],
|
|
678
|
+
description: "CoinGecko coin search, metadata, and broad market data.",
|
|
679
|
+
execute: executeCoingecko,
|
|
680
|
+
};
|
|
681
|
+
}
|