@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,529 @@
|
|
|
1
|
+
import { n as __exportAll } from "./chunk-hT5z_Zn9.mjs";
|
|
2
|
+
import { logger } from "@elizaos/core";
|
|
3
|
+
import { resolveCloudApiBaseUrl } from "@elizaos/plugin-elizacloud";
|
|
4
|
+
//#region src/routes/wallet-market-overview-route.ts
|
|
5
|
+
const MARKET_OVERVIEW_PATH = "/api/wallet/market-overview";
|
|
6
|
+
const CLOUD_MARKET_OVERVIEW_PREVIEW_PATH = "/market/preview/wallet-overview";
|
|
7
|
+
const MARKET_OVERVIEW_CACHE_TTL_MS = 12e4;
|
|
8
|
+
const MARKET_OVERVIEW_FETCH_TIMEOUT_MS = 8e3;
|
|
9
|
+
const MARKET_OVERVIEW_REFRESH_WINDOW_MS = 6e4;
|
|
10
|
+
const MARKET_OVERVIEW_REFRESH_LIMIT = 24;
|
|
11
|
+
const COINGECKO_MARKET_LIMIT = 80;
|
|
12
|
+
const POLYMARKET_MARKET_LIMIT = 10;
|
|
13
|
+
const CACHE_CONTROL_VALUE = "public, max-age=60, stale-while-revalidate=180";
|
|
14
|
+
const MARKET_PRICE_IDS = [
|
|
15
|
+
"bitcoin",
|
|
16
|
+
"ethereum",
|
|
17
|
+
"solana"
|
|
18
|
+
];
|
|
19
|
+
const MARKET_PRICE_ID_SET = new Set(MARKET_PRICE_IDS);
|
|
20
|
+
function isAbortError(error) {
|
|
21
|
+
return error instanceof DOMException ? error.name === "AbortError" || error.name === "TimeoutError" : error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
22
|
+
}
|
|
23
|
+
function createTimeoutError(message) {
|
|
24
|
+
const timeoutError = new Error(message);
|
|
25
|
+
timeoutError.name = "TimeoutError";
|
|
26
|
+
return timeoutError;
|
|
27
|
+
}
|
|
28
|
+
async function fetchWithTimeoutGuard(input, init, timeoutMs) {
|
|
29
|
+
const controller = new AbortController();
|
|
30
|
+
const upstreamSignal = init.signal;
|
|
31
|
+
let timedOut = false;
|
|
32
|
+
const onAbort = () => {
|
|
33
|
+
controller.abort();
|
|
34
|
+
};
|
|
35
|
+
if (upstreamSignal) if (upstreamSignal.aborted) controller.abort();
|
|
36
|
+
else upstreamSignal.addEventListener("abort", onAbort, { once: true });
|
|
37
|
+
const timeoutHandle = setTimeout(() => {
|
|
38
|
+
timedOut = true;
|
|
39
|
+
controller.abort();
|
|
40
|
+
}, timeoutMs);
|
|
41
|
+
try {
|
|
42
|
+
return await fetch(input, {
|
|
43
|
+
...init,
|
|
44
|
+
signal: controller.signal
|
|
45
|
+
});
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (timedOut && isAbortError(error)) throw createTimeoutError(`Upstream request timed out after ${timeoutMs}ms`);
|
|
48
|
+
throw error;
|
|
49
|
+
} finally {
|
|
50
|
+
clearTimeout(timeoutHandle);
|
|
51
|
+
if (upstreamSignal) upstreamSignal.removeEventListener("abort", onAbort);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const COINGECKO_SOURCE = {
|
|
55
|
+
providerId: "coingecko",
|
|
56
|
+
providerName: "CoinGecko",
|
|
57
|
+
providerUrl: "https://www.coingecko.com/"
|
|
58
|
+
};
|
|
59
|
+
const POLYMARKET_SOURCE = {
|
|
60
|
+
providerId: "polymarket",
|
|
61
|
+
providerName: "Polymarket",
|
|
62
|
+
providerUrl: "https://polymarket.com/"
|
|
63
|
+
};
|
|
64
|
+
const STABLE_ASSET_IDS = new Set([
|
|
65
|
+
"tether",
|
|
66
|
+
"usd-coin",
|
|
67
|
+
"binance-usd",
|
|
68
|
+
"first-digital-usd",
|
|
69
|
+
"dai",
|
|
70
|
+
"ethena-usde",
|
|
71
|
+
"true-usd",
|
|
72
|
+
"usds"
|
|
73
|
+
]);
|
|
74
|
+
const STABLE_ASSET_SYMBOLS = new Set([
|
|
75
|
+
"usdt",
|
|
76
|
+
"usdc",
|
|
77
|
+
"busd",
|
|
78
|
+
"fdusd",
|
|
79
|
+
"dai",
|
|
80
|
+
"usde",
|
|
81
|
+
"tusd",
|
|
82
|
+
"usds"
|
|
83
|
+
]);
|
|
84
|
+
let cachedWalletMarketOverview = null;
|
|
85
|
+
let walletMarketOverviewInFlight = null;
|
|
86
|
+
const walletMarketRefreshBuckets = /* @__PURE__ */ new Map();
|
|
87
|
+
let walletMarketOverviewFetch = fetchWithTimeoutGuard;
|
|
88
|
+
function scrubStackFields(value) {
|
|
89
|
+
if (value instanceof Error) return { error: value.message || "Internal error" };
|
|
90
|
+
if (Array.isArray(value)) return value.map(scrubStackFields);
|
|
91
|
+
if (value && typeof value === "object") {
|
|
92
|
+
const out = {};
|
|
93
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
94
|
+
if (key === "stack" || key === "stackTrace") continue;
|
|
95
|
+
out[key] = scrubStackFields(nestedValue);
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
function sendJson(res, status, body) {
|
|
102
|
+
if (res.headersSent) return;
|
|
103
|
+
res.statusCode = status;
|
|
104
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
105
|
+
res.end(JSON.stringify(scrubStackFields(body)));
|
|
106
|
+
}
|
|
107
|
+
function sendJsonError(res, status, message) {
|
|
108
|
+
sendJson(res, status, { error: message });
|
|
109
|
+
}
|
|
110
|
+
function marketOverviewErrorMessage(error) {
|
|
111
|
+
return error instanceof Error && error.message.trim().length > 0 ? error.message.trim() : "Upstream market feed failed";
|
|
112
|
+
}
|
|
113
|
+
function buildMarketOverviewSource(source, { available, stale, error }) {
|
|
114
|
+
return {
|
|
115
|
+
...source,
|
|
116
|
+
available,
|
|
117
|
+
stale,
|
|
118
|
+
error
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function markMarketOverviewSourcesStale(sources) {
|
|
122
|
+
return {
|
|
123
|
+
prices: {
|
|
124
|
+
...sources.prices,
|
|
125
|
+
stale: true
|
|
126
|
+
},
|
|
127
|
+
movers: {
|
|
128
|
+
...sources.movers,
|
|
129
|
+
stale: true
|
|
130
|
+
},
|
|
131
|
+
predictions: {
|
|
132
|
+
...sources.predictions,
|
|
133
|
+
stale: true
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function asRecord(value) {
|
|
138
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
139
|
+
}
|
|
140
|
+
function numberFromUnknown(value) {
|
|
141
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
142
|
+
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
143
|
+
const parsed = Number.parseFloat(value);
|
|
144
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
145
|
+
}
|
|
146
|
+
function integerFromUnknown(value) {
|
|
147
|
+
const parsed = numberFromUnknown(value);
|
|
148
|
+
if (parsed === null) return null;
|
|
149
|
+
return Number.isInteger(parsed) ? parsed : Math.round(parsed);
|
|
150
|
+
}
|
|
151
|
+
function stringFromUnknown(value) {
|
|
152
|
+
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
153
|
+
}
|
|
154
|
+
function parseStringArray(value) {
|
|
155
|
+
if (Array.isArray(value)) return value.filter((item) => typeof item === "string");
|
|
156
|
+
if (typeof value !== "string" || value.trim().length === 0) return [];
|
|
157
|
+
try {
|
|
158
|
+
const parsed = JSON.parse(value);
|
|
159
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
160
|
+
} catch {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function clampProbability(value) {
|
|
165
|
+
if (value === null || !Number.isFinite(value)) return null;
|
|
166
|
+
return Math.min(1, Math.max(0, value));
|
|
167
|
+
}
|
|
168
|
+
function isStableAsset(market) {
|
|
169
|
+
const id = market.id.toLowerCase();
|
|
170
|
+
const symbol = market.symbol.toLowerCase();
|
|
171
|
+
return STABLE_ASSET_IDS.has(id) || STABLE_ASSET_SYMBOLS.has(symbol);
|
|
172
|
+
}
|
|
173
|
+
function mapCoinGeckoMarket(input) {
|
|
174
|
+
const record = asRecord(input);
|
|
175
|
+
if (!record) return null;
|
|
176
|
+
const id = stringFromUnknown(record.id);
|
|
177
|
+
const symbol = stringFromUnknown(record.symbol);
|
|
178
|
+
const name = stringFromUnknown(record.name);
|
|
179
|
+
const currentPriceUsd = numberFromUnknown(record.current_price);
|
|
180
|
+
const change24hPct = numberFromUnknown(record.price_change_percentage_24h);
|
|
181
|
+
if (!id || !symbol || !name || currentPriceUsd === null || change24hPct === null) return null;
|
|
182
|
+
return {
|
|
183
|
+
id,
|
|
184
|
+
symbol: symbol.toUpperCase(),
|
|
185
|
+
name,
|
|
186
|
+
currentPriceUsd,
|
|
187
|
+
change24hPct,
|
|
188
|
+
marketCapRank: integerFromUnknown(record.market_cap_rank),
|
|
189
|
+
imageUrl: stringFromUnknown(record.image)
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function mapPolymarketMarket(input) {
|
|
193
|
+
const record = asRecord(input);
|
|
194
|
+
if (!record) return null;
|
|
195
|
+
const question = stringFromUnknown(record.question);
|
|
196
|
+
if (!question) return null;
|
|
197
|
+
const outcomeLabels = parseStringArray(record.outcomes);
|
|
198
|
+
const outcomeProbabilities = parseStringArray(record.outcomePrices).map((value) => clampProbability(numberFromUnknown(value))).filter((value) => value !== null);
|
|
199
|
+
const volume24hUsd = numberFromUnknown(record.volume24hr);
|
|
200
|
+
if (volume24hUsd === null) return null;
|
|
201
|
+
return {
|
|
202
|
+
slug: stringFromUnknown(record.slug),
|
|
203
|
+
question,
|
|
204
|
+
outcomeLabels,
|
|
205
|
+
outcomeProbabilities,
|
|
206
|
+
volume24hUsd,
|
|
207
|
+
totalVolumeUsd: numberFromUnknown(record.volume),
|
|
208
|
+
endsAt: stringFromUnknown(record.endDate),
|
|
209
|
+
imageUrl: stringFromUnknown(record.image) ?? stringFromUnknown(record.icon)
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function highlightedPredictionOutcome(market) {
|
|
213
|
+
const yesIndex = market.outcomeLabels.findIndex((label) => label.trim().toLowerCase() === "yes");
|
|
214
|
+
if (yesIndex >= 0) return {
|
|
215
|
+
label: market.outcomeLabels[yesIndex] ?? "Yes",
|
|
216
|
+
probability: market.outcomeProbabilities[yesIndex] ?? null
|
|
217
|
+
};
|
|
218
|
+
let highestIndex = -1;
|
|
219
|
+
let highestProbability = -1;
|
|
220
|
+
for (const [index, probability] of market.outcomeProbabilities.entries()) if (probability > highestProbability) {
|
|
221
|
+
highestIndex = index;
|
|
222
|
+
highestProbability = probability;
|
|
223
|
+
}
|
|
224
|
+
if (highestIndex >= 0) return {
|
|
225
|
+
label: market.outcomeLabels[highestIndex] ?? "Top",
|
|
226
|
+
probability: market.outcomeProbabilities[highestIndex] ?? null
|
|
227
|
+
};
|
|
228
|
+
return {
|
|
229
|
+
label: "Top",
|
|
230
|
+
probability: null
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
async function fetchCoinGeckoMarkets() {
|
|
234
|
+
const url = new URL("https://api.coingecko.com/api/v3/coins/markets");
|
|
235
|
+
url.searchParams.set("vs_currency", "usd");
|
|
236
|
+
url.searchParams.set("order", "market_cap_desc");
|
|
237
|
+
url.searchParams.set("per_page", String(COINGECKO_MARKET_LIMIT));
|
|
238
|
+
url.searchParams.set("page", "1");
|
|
239
|
+
url.searchParams.set("price_change_percentage", "24h");
|
|
240
|
+
const response = await walletMarketOverviewFetch(url, {
|
|
241
|
+
method: "GET",
|
|
242
|
+
headers: {
|
|
243
|
+
accept: "application/json",
|
|
244
|
+
"user-agent": "Eliza Wallet Market Feed/1.0"
|
|
245
|
+
}
|
|
246
|
+
}, MARKET_OVERVIEW_FETCH_TIMEOUT_MS);
|
|
247
|
+
if (!response.ok) throw new Error(`CoinGecko responded ${response.status}`);
|
|
248
|
+
const payload = await response.json();
|
|
249
|
+
if (!Array.isArray(payload)) throw new Error("CoinGecko payload was not an array");
|
|
250
|
+
return payload.map(mapCoinGeckoMarket).filter((market) => market !== null);
|
|
251
|
+
}
|
|
252
|
+
async function fetchPolymarketMarkets() {
|
|
253
|
+
const url = new URL("https://gamma-api.polymarket.com/markets");
|
|
254
|
+
url.searchParams.set("active", "true");
|
|
255
|
+
url.searchParams.set("closed", "false");
|
|
256
|
+
url.searchParams.set("order", "volume24hr");
|
|
257
|
+
url.searchParams.set("ascending", "false");
|
|
258
|
+
url.searchParams.set("limit", String(POLYMARKET_MARKET_LIMIT));
|
|
259
|
+
const response = await walletMarketOverviewFetch(url, {
|
|
260
|
+
method: "GET",
|
|
261
|
+
headers: {
|
|
262
|
+
accept: "application/json",
|
|
263
|
+
"user-agent": "Eliza Wallet Market Feed/1.0"
|
|
264
|
+
}
|
|
265
|
+
}, MARKET_OVERVIEW_FETCH_TIMEOUT_MS);
|
|
266
|
+
if (!response.ok) throw new Error(`Polymarket responded ${response.status}`);
|
|
267
|
+
const payload = await response.json();
|
|
268
|
+
if (!Array.isArray(payload)) throw new Error("Polymarket payload was not an array");
|
|
269
|
+
return payload.map(mapPolymarketMarket).filter((market) => market !== null);
|
|
270
|
+
}
|
|
271
|
+
function buildPriceSnapshots(markets) {
|
|
272
|
+
const byId = new Map(markets.map((market) => [market.id, market]));
|
|
273
|
+
return MARKET_PRICE_IDS.reduce((items, id) => {
|
|
274
|
+
const market = byId.get(id);
|
|
275
|
+
if (!market) return items;
|
|
276
|
+
items.push({
|
|
277
|
+
id: market.id,
|
|
278
|
+
symbol: market.symbol,
|
|
279
|
+
name: market.name,
|
|
280
|
+
priceUsd: market.currentPriceUsd,
|
|
281
|
+
change24hPct: market.change24hPct,
|
|
282
|
+
imageUrl: market.imageUrl
|
|
283
|
+
});
|
|
284
|
+
return items;
|
|
285
|
+
}, []);
|
|
286
|
+
}
|
|
287
|
+
function buildMovers(markets) {
|
|
288
|
+
return markets.filter((market) => !MARKET_PRICE_ID_SET.has(market.id)).filter((market) => !isStableAsset(market)).filter((market) => market.marketCapRank === null || market.marketCapRank <= 200).sort((left, right) => Math.abs(right.change24hPct) - Math.abs(left.change24hPct)).slice(0, 6).map((market) => ({
|
|
289
|
+
id: market.id,
|
|
290
|
+
symbol: market.symbol,
|
|
291
|
+
name: market.name,
|
|
292
|
+
priceUsd: market.currentPriceUsd,
|
|
293
|
+
change24hPct: market.change24hPct,
|
|
294
|
+
marketCapRank: market.marketCapRank,
|
|
295
|
+
imageUrl: market.imageUrl
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
function buildPredictions(markets) {
|
|
299
|
+
const seenQuestions = /* @__PURE__ */ new Set();
|
|
300
|
+
const predictions = [];
|
|
301
|
+
for (const market of markets) {
|
|
302
|
+
const normalizedQuestion = market.question.trim().toLowerCase();
|
|
303
|
+
if (seenQuestions.has(normalizedQuestion)) continue;
|
|
304
|
+
seenQuestions.add(normalizedQuestion);
|
|
305
|
+
const highlightedOutcome = highlightedPredictionOutcome(market);
|
|
306
|
+
predictions.push({
|
|
307
|
+
id: market.slug ?? normalizedQuestion,
|
|
308
|
+
slug: market.slug,
|
|
309
|
+
question: market.question,
|
|
310
|
+
highlightedOutcomeLabel: highlightedOutcome.label,
|
|
311
|
+
highlightedOutcomeProbability: highlightedOutcome.probability,
|
|
312
|
+
volume24hUsd: market.volume24hUsd,
|
|
313
|
+
totalVolumeUsd: market.totalVolumeUsd,
|
|
314
|
+
endsAt: market.endsAt,
|
|
315
|
+
imageUrl: market.imageUrl
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
return predictions.slice(0, 6);
|
|
319
|
+
}
|
|
320
|
+
function isWalletMarketOverviewSource(value) {
|
|
321
|
+
const record = asRecord(value);
|
|
322
|
+
return record !== null && typeof record.providerId === "string" && typeof record.providerName === "string" && typeof record.providerUrl === "string" && typeof record.available === "boolean" && typeof record.stale === "boolean" && (typeof record.error === "string" || record.error === null);
|
|
323
|
+
}
|
|
324
|
+
function isWalletMarketOverviewResponse(value) {
|
|
325
|
+
const record = asRecord(value);
|
|
326
|
+
const sources = asRecord(record?.sources);
|
|
327
|
+
return record !== null && typeof record.generatedAt === "string" && typeof record.cacheTtlSeconds === "number" && typeof record.stale === "boolean" && sources !== null && isWalletMarketOverviewSource(sources.prices) && isWalletMarketOverviewSource(sources.movers) && isWalletMarketOverviewSource(sources.predictions) && Array.isArray(record.prices) && Array.isArray(record.movers) && Array.isArray(record.predictions);
|
|
328
|
+
}
|
|
329
|
+
function resolveWalletMarketOverviewCloudPreviewUrl() {
|
|
330
|
+
return `${resolveCloudApiBaseUrl(process.env.ELIZAOS_CLOUD_BASE_URL)}${CLOUD_MARKET_OVERVIEW_PREVIEW_PATH}`;
|
|
331
|
+
}
|
|
332
|
+
async function fetchCloudWalletMarketOverview(clientAddress) {
|
|
333
|
+
const response = await walletMarketOverviewFetch(resolveWalletMarketOverviewCloudPreviewUrl(), {
|
|
334
|
+
method: "GET",
|
|
335
|
+
headers: {
|
|
336
|
+
accept: "application/json",
|
|
337
|
+
"user-agent": "Eliza Wallet Market Feed/1.0",
|
|
338
|
+
...clientAddress !== "unknown" ? { "x-forwarded-for": clientAddress } : {}
|
|
339
|
+
}
|
|
340
|
+
}, MARKET_OVERVIEW_FETCH_TIMEOUT_MS);
|
|
341
|
+
if (!response.ok) throw new Error(`Cloud preview responded ${response.status}`);
|
|
342
|
+
const payload = await response.json();
|
|
343
|
+
if (!isWalletMarketOverviewResponse(payload)) throw new Error("Cloud preview payload was invalid");
|
|
344
|
+
return payload;
|
|
345
|
+
}
|
|
346
|
+
async function buildWalletMarketOverview(clientAddress) {
|
|
347
|
+
const [cloudPreviewResult, polymarketResult] = await Promise.allSettled([fetchCloudWalletMarketOverview(clientAddress), fetchPolymarketMarkets()]);
|
|
348
|
+
const polymarketMarkets = polymarketResult.status === "fulfilled" ? polymarketResult.value : [];
|
|
349
|
+
const polymarketError = polymarketResult.status === "rejected" ? marketOverviewErrorMessage(polymarketResult.reason) : null;
|
|
350
|
+
if (cloudPreviewResult.status === "fulfilled") {
|
|
351
|
+
if (polymarketError) logger.warn(`[WalletMarketOverviewRoute] Polymarket feed unavailable (${polymarketError})`);
|
|
352
|
+
return {
|
|
353
|
+
...cloudPreviewResult.value,
|
|
354
|
+
sources: {
|
|
355
|
+
...cloudPreviewResult.value.sources,
|
|
356
|
+
predictions: buildMarketOverviewSource(POLYMARKET_SOURCE, {
|
|
357
|
+
available: polymarketError === null,
|
|
358
|
+
stale: false,
|
|
359
|
+
error: polymarketError
|
|
360
|
+
})
|
|
361
|
+
},
|
|
362
|
+
predictions: polymarketError === null ? buildPredictions(polymarketMarkets) : []
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
{
|
|
366
|
+
const error = cloudPreviewResult.reason;
|
|
367
|
+
logger.warn(`[WalletMarketOverviewRoute] Cloud preview unavailable (${marketOverviewErrorMessage(error)}); falling back to direct feeds`);
|
|
368
|
+
}
|
|
369
|
+
const [coinGeckoResult] = await Promise.allSettled([fetchCoinGeckoMarkets()]);
|
|
370
|
+
const coinGeckoMarkets = coinGeckoResult.status === "fulfilled" ? coinGeckoResult.value : [];
|
|
371
|
+
const coinGeckoError = coinGeckoResult.status === "rejected" ? marketOverviewErrorMessage(coinGeckoResult.reason) : null;
|
|
372
|
+
if (coinGeckoError) logger.warn(`[WalletMarketOverviewRoute] CoinGecko feed unavailable (${coinGeckoError})`);
|
|
373
|
+
if (polymarketError) logger.warn(`[WalletMarketOverviewRoute] Polymarket feed unavailable (${polymarketError})`);
|
|
374
|
+
if (coinGeckoError && polymarketError) throw new Error(`CoinGecko: ${coinGeckoError}; Polymarket: ${polymarketError}`);
|
|
375
|
+
return {
|
|
376
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
377
|
+
cacheTtlSeconds: Math.floor(MARKET_OVERVIEW_CACHE_TTL_MS / 1e3),
|
|
378
|
+
stale: false,
|
|
379
|
+
sources: {
|
|
380
|
+
prices: buildMarketOverviewSource(COINGECKO_SOURCE, {
|
|
381
|
+
available: coinGeckoError === null,
|
|
382
|
+
stale: false,
|
|
383
|
+
error: coinGeckoError
|
|
384
|
+
}),
|
|
385
|
+
movers: buildMarketOverviewSource(COINGECKO_SOURCE, {
|
|
386
|
+
available: coinGeckoError === null,
|
|
387
|
+
stale: false,
|
|
388
|
+
error: coinGeckoError
|
|
389
|
+
}),
|
|
390
|
+
predictions: buildMarketOverviewSource(POLYMARKET_SOURCE, {
|
|
391
|
+
available: polymarketError === null,
|
|
392
|
+
stale: false,
|
|
393
|
+
error: polymarketError
|
|
394
|
+
})
|
|
395
|
+
},
|
|
396
|
+
prices: buildPriceSnapshots(coinGeckoMarkets),
|
|
397
|
+
movers: buildMovers(coinGeckoMarkets),
|
|
398
|
+
predictions: buildPredictions(polymarketMarkets)
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
function freshCachedWalletMarketOverview() {
|
|
402
|
+
if (!cachedWalletMarketOverview || cachedWalletMarketOverview.expiresAt <= Date.now()) return null;
|
|
403
|
+
return cachedWalletMarketOverview.response;
|
|
404
|
+
}
|
|
405
|
+
function staleCachedWalletMarketOverview() {
|
|
406
|
+
if (!cachedWalletMarketOverview) return null;
|
|
407
|
+
return {
|
|
408
|
+
...cachedWalletMarketOverview.response,
|
|
409
|
+
stale: true,
|
|
410
|
+
sources: markMarketOverviewSourcesStale(cachedWalletMarketOverview.response.sources)
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
function resolveClientAddress(req) {
|
|
414
|
+
const forwardedFor = req.headers["x-forwarded-for"];
|
|
415
|
+
if (typeof forwardedFor === "string" && forwardedFor.trim().length > 0) return forwardedFor.split(",")[0]?.trim() || "unknown";
|
|
416
|
+
if (Array.isArray(forwardedFor) && forwardedFor.length > 0) return forwardedFor[0]?.trim() || "unknown";
|
|
417
|
+
return req.socket.remoteAddress ?? "unknown";
|
|
418
|
+
}
|
|
419
|
+
function consumeRefreshSlot(clientAddress) {
|
|
420
|
+
const now = Date.now();
|
|
421
|
+
for (const [key, bucket] of walletMarketRefreshBuckets) if (bucket.resetAt <= now) walletMarketRefreshBuckets.delete(key);
|
|
422
|
+
const bucket = walletMarketRefreshBuckets.get(clientAddress);
|
|
423
|
+
if (!bucket || bucket.resetAt <= now) {
|
|
424
|
+
walletMarketRefreshBuckets.set(clientAddress, {
|
|
425
|
+
count: 1,
|
|
426
|
+
resetAt: now + MARKET_OVERVIEW_REFRESH_WINDOW_MS
|
|
427
|
+
});
|
|
428
|
+
return {
|
|
429
|
+
allowed: true,
|
|
430
|
+
retryAfterSeconds: Math.ceil(MARKET_OVERVIEW_REFRESH_WINDOW_MS / 1e3)
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
if (bucket.count >= MARKET_OVERVIEW_REFRESH_LIMIT) return {
|
|
434
|
+
allowed: false,
|
|
435
|
+
retryAfterSeconds: Math.max(1, Math.ceil((bucket.resetAt - now) / 1e3))
|
|
436
|
+
};
|
|
437
|
+
bucket.count += 1;
|
|
438
|
+
walletMarketRefreshBuckets.set(clientAddress, bucket);
|
|
439
|
+
return {
|
|
440
|
+
allowed: true,
|
|
441
|
+
retryAfterSeconds: Math.max(1, Math.ceil((bucket.resetAt - now) / 1e3))
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function setPublicMarketHeaders(res) {
|
|
445
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
446
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
447
|
+
res.setHeader("Cache-Control", CACHE_CONTROL_VALUE);
|
|
448
|
+
}
|
|
449
|
+
async function loadWalletMarketOverview(clientAddress) {
|
|
450
|
+
const fresh = freshCachedWalletMarketOverview();
|
|
451
|
+
if (fresh) return fresh;
|
|
452
|
+
if (!walletMarketOverviewInFlight) walletMarketOverviewInFlight = buildWalletMarketOverview(clientAddress).then((response) => {
|
|
453
|
+
cachedWalletMarketOverview = {
|
|
454
|
+
response,
|
|
455
|
+
expiresAt: Date.now() + MARKET_OVERVIEW_CACHE_TTL_MS
|
|
456
|
+
};
|
|
457
|
+
return response;
|
|
458
|
+
}).catch((error) => {
|
|
459
|
+
const stale = staleCachedWalletMarketOverview();
|
|
460
|
+
if (stale) {
|
|
461
|
+
logger.warn(`[WalletMarketOverviewRoute] Refresh failed; serving stale market overview (${error instanceof Error ? error.message : String(error)})`);
|
|
462
|
+
return stale;
|
|
463
|
+
}
|
|
464
|
+
throw error;
|
|
465
|
+
}).finally(() => {
|
|
466
|
+
walletMarketOverviewInFlight = null;
|
|
467
|
+
});
|
|
468
|
+
return walletMarketOverviewInFlight;
|
|
469
|
+
}
|
|
470
|
+
async function handleWalletMarketOverviewRoute(req, res) {
|
|
471
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
472
|
+
if (new URL(req.url ?? "/", "http://localhost").pathname !== MARKET_OVERVIEW_PATH) return false;
|
|
473
|
+
setPublicMarketHeaders(res);
|
|
474
|
+
if (method === "OPTIONS") {
|
|
475
|
+
res.statusCode = 204;
|
|
476
|
+
res.end();
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
if (method !== "GET") {
|
|
480
|
+
sendJsonError(res, 405, "Method not allowed");
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
const clientAddress = resolveClientAddress(req);
|
|
484
|
+
const fresh = freshCachedWalletMarketOverview();
|
|
485
|
+
if (fresh) {
|
|
486
|
+
sendJson(res, 200, fresh);
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
if (!walletMarketOverviewInFlight) {
|
|
490
|
+
const rateLimit = consumeRefreshSlot(clientAddress);
|
|
491
|
+
if (!rateLimit.allowed) {
|
|
492
|
+
const stale = staleCachedWalletMarketOverview();
|
|
493
|
+
if (stale) {
|
|
494
|
+
sendJson(res, 200, stale);
|
|
495
|
+
return true;
|
|
496
|
+
}
|
|
497
|
+
res.setHeader("Retry-After", String(rateLimit.retryAfterSeconds));
|
|
498
|
+
sendJsonError(res, 429, "Too many market overview refreshes");
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
try {
|
|
503
|
+
sendJson(res, 200, await loadWalletMarketOverview(clientAddress));
|
|
504
|
+
} catch (error) {
|
|
505
|
+
logger.error(`[WalletMarketOverviewRoute] Failed to load market overview (${error instanceof Error ? error.message : String(error)})`);
|
|
506
|
+
sendJsonError(res, 502, "Failed to load market overview");
|
|
507
|
+
}
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
510
|
+
//#endregion
|
|
511
|
+
//#region src/routes/plugin.ts
|
|
512
|
+
var plugin_exports = /* @__PURE__ */ __exportAll({ walletRoutePlugin: () => walletRoutePlugin });
|
|
513
|
+
async function marketOverviewHandler(req, res, _runtime) {
|
|
514
|
+
await handleWalletMarketOverviewRoute(req, res);
|
|
515
|
+
}
|
|
516
|
+
const walletRoutePlugin = {
|
|
517
|
+
name: "@elizaos/plugin-wallet:routes",
|
|
518
|
+
description: "Wallet HTTP route handlers (market overview, etc.) — extracted from packages/app-core/src/api.",
|
|
519
|
+
routes: [{
|
|
520
|
+
type: "GET",
|
|
521
|
+
path: "/api/wallet/market-overview",
|
|
522
|
+
rawPath: true,
|
|
523
|
+
public: true,
|
|
524
|
+
name: "wallet-market-overview",
|
|
525
|
+
handler: marketOverviewHandler
|
|
526
|
+
}]
|
|
527
|
+
};
|
|
528
|
+
//#endregion
|
|
529
|
+
export { walletRoutePlugin as n, plugin_exports as t };
|