@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.
Files changed (200) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +64 -0
  3. package/auto-enable.ts +76 -0
  4. package/dist/LpManagementService-BWrQ5-cO.mjs +353 -0
  5. package/dist/MockLpService-D_Apn4Fd.mjs +99 -0
  6. package/dist/aerodrome-CfnESC32.mjs +890 -0
  7. package/dist/chunk-hT5z_Zn9.mjs +35 -0
  8. package/dist/index.d.mts +34727 -0
  9. package/dist/index.mjs +21590 -0
  10. package/dist/lib/server-wallet-trade.d.mts +34 -0
  11. package/dist/lib/server-wallet-trade.mjs +306 -0
  12. package/dist/meteora-BPX39hZo.mjs +22640 -0
  13. package/dist/orca-Bybp1HXO.mjs +249 -0
  14. package/dist/pancakeswp-CkEXlXti.mjs +604 -0
  15. package/dist/plugin-ZO_MTyd0.mjs +529 -0
  16. package/dist/raydium-rfaM9yEf.mjs +539 -0
  17. package/dist/sdk/index.d.mts +32492 -0
  18. package/dist/sdk/index.mjs +6415 -0
  19. package/dist/types-D5252NZk.mjs +487 -0
  20. package/dist/uniswap-CReXgXVN.mjs +573 -0
  21. package/dist/wallet-action.d.mts +6 -0
  22. package/dist/wallet-action.mjs +820 -0
  23. package/package.json +152 -0
  24. package/src/actions/failure-codes.ts +79 -0
  25. package/src/actions/index.ts +1 -0
  26. package/src/analytics/birdeye/actions/wallet-search-address.ts +9 -0
  27. package/src/analytics/birdeye/birdeye-task.ts +175 -0
  28. package/src/analytics/birdeye/birdeye.ts +813 -0
  29. package/src/analytics/birdeye/constants.ts +74 -0
  30. package/src/analytics/birdeye/providers/agent-portfolio-provider.ts +18 -0
  31. package/src/analytics/birdeye/providers/market.ts +227 -0
  32. package/src/analytics/birdeye/providers/portfolio-factory.test.ts +138 -0
  33. package/src/analytics/birdeye/providers/portfolio-factory.ts +252 -0
  34. package/src/analytics/birdeye/providers/trending.ts +365 -0
  35. package/src/analytics/birdeye/providers/wallet.ts +14 -0
  36. package/src/analytics/birdeye/search-category.test.ts +207 -0
  37. package/src/analytics/birdeye/search-category.ts +506 -0
  38. package/src/analytics/birdeye/service.ts +992 -0
  39. package/src/analytics/birdeye/tasks/birdeye.ts +232 -0
  40. package/src/analytics/birdeye/types/api/common.ts +305 -0
  41. package/src/analytics/birdeye/types/api/defi.ts +220 -0
  42. package/src/analytics/birdeye/types/api/pair.ts +200 -0
  43. package/src/analytics/birdeye/types/api/search.ts +86 -0
  44. package/src/analytics/birdeye/types/api/token.ts +635 -0
  45. package/src/analytics/birdeye/types/api/trader.ts +76 -0
  46. package/src/analytics/birdeye/types/api/wallet.ts +181 -0
  47. package/src/analytics/birdeye/types/shared.ts +106 -0
  48. package/src/analytics/birdeye/utils.ts +700 -0
  49. package/src/analytics/dexscreener/errors.ts +28 -0
  50. package/src/analytics/dexscreener/index.ts +3 -0
  51. package/src/analytics/dexscreener/search-category.test.ts +49 -0
  52. package/src/analytics/dexscreener/search-category.ts +42 -0
  53. package/src/analytics/dexscreener/service.ts +595 -0
  54. package/src/analytics/dexscreener/types.ts +128 -0
  55. package/src/analytics/lpinfo/index.d.ts +7 -0
  56. package/src/analytics/lpinfo/index.ts +52 -0
  57. package/src/analytics/lpinfo/kamino/README.md +102 -0
  58. package/src/analytics/lpinfo/kamino/index.ts +24 -0
  59. package/src/analytics/lpinfo/kamino/providers/kaminoLiquidityProvider.ts +422 -0
  60. package/src/analytics/lpinfo/kamino/providers/kaminoPoolProvider.ts +365 -0
  61. package/src/analytics/lpinfo/kamino/providers/kaminoProvider.ts +496 -0
  62. package/src/analytics/lpinfo/kamino/services/kaminoLiquidityService.ts +1123 -0
  63. package/src/analytics/lpinfo/kamino/services/kaminoService.ts +758 -0
  64. package/src/analytics/lpinfo/steer/README.md +169 -0
  65. package/src/analytics/lpinfo/steer/index.ts +23 -0
  66. package/src/analytics/lpinfo/steer/providers/steerLiquidityProvider.ts +544 -0
  67. package/src/analytics/lpinfo/steer/services/steerLiquidityService.ts +1690 -0
  68. package/src/analytics/lpinfo/steer/steer-display-types.ts +99 -0
  69. package/src/analytics/news/index.ts +52 -0
  70. package/src/analytics/news/interfaces/types.ts +222 -0
  71. package/src/analytics/news/providers/defiNewsProvider.ts +734 -0
  72. package/src/analytics/news/services/newsDataService.ts +332 -0
  73. package/src/analytics/news/utils/formatters.ts +151 -0
  74. package/src/analytics/token-info/action.ts +240 -0
  75. package/src/analytics/token-info/index.ts +3 -0
  76. package/src/analytics/token-info/params.ts +215 -0
  77. package/src/analytics/token-info/providers.ts +681 -0
  78. package/src/analytics/token-info/service.ts +168 -0
  79. package/src/analytics/token-info/types.ts +74 -0
  80. package/src/audit/audit-log.ts +45 -0
  81. package/src/browser-shim/build-shim.ts +123 -0
  82. package/src/browser-shim/index.ts +5 -0
  83. package/src/browser-shim/shim.template.js +563 -0
  84. package/src/chains/evm/.github/workflows/npm-deploy.yml +112 -0
  85. package/src/chains/evm/LICENSE +21 -0
  86. package/src/chains/evm/README.md +106 -0
  87. package/src/chains/evm/actions/helpers.ts +147 -0
  88. package/src/chains/evm/actions/swap.ts +839 -0
  89. package/src/chains/evm/actions/transfer.ts +254 -0
  90. package/src/chains/evm/biome.json +61 -0
  91. package/src/chains/evm/bridge-router.ts +660 -0
  92. package/src/chains/evm/build.ts +89 -0
  93. package/src/chains/evm/chain-handler.ts +416 -0
  94. package/src/chains/evm/constants.ts +23 -0
  95. package/src/chains/evm/contracts/artifacts/OZGovernor.json +1707 -0
  96. package/src/chains/evm/contracts/artifacts/TimelockController.json +1007 -0
  97. package/src/chains/evm/contracts/artifacts/VoteToken.json +895 -0
  98. package/src/chains/evm/dex/aerodrome/index.ts +34 -0
  99. package/src/chains/evm/dex/aerodrome/services/AerodromeLpService.ts +558 -0
  100. package/src/chains/evm/dex/aerodrome/types.ts +318 -0
  101. package/src/chains/evm/dex/pancakeswp/index.ts +35 -0
  102. package/src/chains/evm/dex/pancakeswp/services/PancakeSwapV3LpService.ts +743 -0
  103. package/src/chains/evm/dex/pancakeswp/types.ts +65 -0
  104. package/src/chains/evm/dex/uniswap/index.ts +35 -0
  105. package/src/chains/evm/dex/uniswap/services/UniswapV3LpService.ts +759 -0
  106. package/src/chains/evm/dex/uniswap/types.ts +390 -0
  107. package/src/chains/evm/generated/specs/spec-helpers.ts +73 -0
  108. package/src/chains/evm/generated/specs/specs.ts +151 -0
  109. package/src/chains/evm/gov-router.ts +250 -0
  110. package/src/chains/evm/index.browser.ts +16 -0
  111. package/src/chains/evm/index.ts +31 -0
  112. package/src/chains/evm/prompts.ts +193 -0
  113. package/src/chains/evm/providers/get-balance.ts +123 -0
  114. package/src/chains/evm/providers/wallet.ts +715 -0
  115. package/src/chains/evm/routes/sign.ts +333 -0
  116. package/src/chains/evm/rpc-providers.ts +410 -0
  117. package/src/chains/evm/service.ts +140 -0
  118. package/src/chains/evm/templates/index.ts +10 -0
  119. package/src/chains/evm/types/index.ts +432 -0
  120. package/src/chains/evm/vitest.config.ts +18 -0
  121. package/src/chains/registry.ts +668 -0
  122. package/src/chains/solana/README.md +367 -0
  123. package/src/chains/wallet-action.ts +533 -0
  124. package/src/chains/wallet-router.test.ts +296 -0
  125. package/src/contracts.ts +65 -0
  126. package/src/core-augmentation.ts +10 -0
  127. package/src/index.ts +71 -0
  128. package/src/lib/server-wallet-trade.ts +192 -0
  129. package/src/lib/wallet-export-guard.ts +330 -0
  130. package/src/lp/actions/liquidity.ts +827 -0
  131. package/src/lp/e2e/real-token-tests.ts +428 -0
  132. package/src/lp/e2e/scenarios.ts +470 -0
  133. package/src/lp/e2e/test-utils.ts +145 -0
  134. package/src/lp/lp-manager-entry.ts +303 -0
  135. package/src/lp/services/ConcentratedLiquidityService.ts +120 -0
  136. package/src/lp/services/DexInteractionService.ts +226 -0
  137. package/src/lp/services/LpManagementService.test.ts +148 -0
  138. package/src/lp/services/LpManagementService.ts +632 -0
  139. package/src/lp/services/UserLpProfileService.ts +163 -0
  140. package/src/lp/services/VaultService.ts +153 -0
  141. package/src/lp/services/YieldOptimizationService.ts +344 -0
  142. package/src/lp/services/__tests__/MockLpService.ts +146 -0
  143. package/src/lp/tasks/LpAutoRebalanceTask.ts +117 -0
  144. package/src/lp/tasks/__tests__/LpAutoRebalanceTask.test.ts +370 -0
  145. package/src/lp/types.ts +582 -0
  146. package/src/lp/utils/solanaClient.ts +143 -0
  147. package/src/plugin.ts +125 -0
  148. package/src/policy/policy.ts +19 -0
  149. package/src/providers/canonical-provider.ts +27 -0
  150. package/src/providers/unified-wallet-provider.ts +79 -0
  151. package/src/register-routes.ts +11 -0
  152. package/src/routes/plugin.ts +47 -0
  153. package/src/routes/wallet-market-overview-route.ts +869 -0
  154. package/src/sdk/abi.ts +258 -0
  155. package/src/sdk/bridge/abis.ts +126 -0
  156. package/src/sdk/bridge/client.ts +518 -0
  157. package/src/sdk/bridge/index.ts +56 -0
  158. package/src/sdk/bridge/solana.ts +604 -0
  159. package/src/sdk/bridge/types.ts +202 -0
  160. package/src/sdk/convenience.ts +347 -0
  161. package/src/sdk/escrow/MutualStakeEscrow.ts +480 -0
  162. package/src/sdk/escrow/types.ts +64 -0
  163. package/src/sdk/escrow/verifiers.ts +73 -0
  164. package/src/sdk/identity/erc8004.ts +692 -0
  165. package/src/sdk/identity/reputation.ts +449 -0
  166. package/src/sdk/identity/uaid.ts +497 -0
  167. package/src/sdk/identity/validation.ts +372 -0
  168. package/src/sdk/index.ts +763 -0
  169. package/src/sdk/policy/SpendingPolicy.ts +260 -0
  170. package/src/sdk/policy/UptoBillingPolicy.ts +320 -0
  171. package/src/sdk/router/PaymentRouter.ts +215 -0
  172. package/src/sdk/router/index.ts +8 -0
  173. package/src/sdk/swap/SwapModule.ts +310 -0
  174. package/src/sdk/swap/abi.ts +117 -0
  175. package/src/sdk/swap/index.ts +34 -0
  176. package/src/sdk/swap/types.ts +135 -0
  177. package/src/sdk/tokens/decimals.ts +140 -0
  178. package/src/sdk/tokens/registry.ts +911 -0
  179. package/src/sdk/tokens/solana.ts +419 -0
  180. package/src/sdk/tokens/transfers.ts +327 -0
  181. package/src/sdk/types.ts +158 -0
  182. package/src/sdk/wallet-core.ts +115 -0
  183. package/src/sdk/x402/budget.ts +168 -0
  184. package/src/sdk/x402/chains/abstract/index.ts +280 -0
  185. package/src/sdk/x402/client.ts +320 -0
  186. package/src/sdk/x402/index.ts +46 -0
  187. package/src/sdk/x402/middleware.ts +92 -0
  188. package/src/sdk/x402/multi-asset.ts +144 -0
  189. package/src/sdk/x402/types.ts +156 -0
  190. package/src/services/wallet-backend-service.ts +328 -0
  191. package/src/types/wallet-router.ts +227 -0
  192. package/src/utils/intent-trajectory.ts +106 -0
  193. package/src/wallet/backend.ts +62 -0
  194. package/src/wallet/errors.ts +49 -0
  195. package/src/wallet/index.ts +27 -0
  196. package/src/wallet/local-eoa-backend.ts +201 -0
  197. package/src/wallet/pending.ts +60 -0
  198. package/src/wallet/select-backend.ts +47 -0
  199. package/src/wallet/steward-backend.ts +161 -0
  200. package/src/wallet-action.ts +1 -0
@@ -0,0 +1,827 @@
1
+ // @ts-nocheck — legacy code from absorbed plugins (lp-manager, lpinfo, dexscreener, defi-news, birdeye); strict types pending cleanup
2
+ import type {
3
+ Action,
4
+ ActionExample,
5
+ IAgentRuntime,
6
+ Memory,
7
+ State,
8
+ } from "@elizaos/core";
9
+ import { privateKeyToAccount } from "viem/accounts";
10
+ import {
11
+ getLpManagementService,
12
+ type LpManagementService,
13
+ NoMatchingLpProtocolError,
14
+ } from "../services/LpManagementService.ts";
15
+ import type {
16
+ IUserLpProfileService,
17
+ IVaultService,
18
+ LpActionParams,
19
+ LpManagementSubaction,
20
+ LpPositionDetails,
21
+ PoolInfo,
22
+ TokenBalance,
23
+ UserLpProfile,
24
+ } from "../types.ts";
25
+ import { getChainConfig } from "../types.ts";
26
+
27
+ const SOLANA_DEXES = new Set(["raydium", "orca", "meteora"]);
28
+ const EVM_DEXES = new Set(["uniswap", "aerodrome", "pancakeswap"]);
29
+
30
+ function selectedContextMatches(
31
+ state: State | undefined,
32
+ contexts: readonly string[],
33
+ ): boolean {
34
+ const selected = new Set<string>();
35
+ const collect = (value: unknown) => {
36
+ if (!Array.isArray(value)) return;
37
+ for (const item of value) {
38
+ if (typeof item === "string") selected.add(item);
39
+ }
40
+ };
41
+ collect(
42
+ (state?.values as Record<string, unknown> | undefined)?.selectedContexts,
43
+ );
44
+ collect(
45
+ (state?.data as Record<string, unknown> | undefined)?.selectedContexts,
46
+ );
47
+ const contextObject = (state?.data as Record<string, unknown> | undefined)
48
+ ?.contextObject as
49
+ | {
50
+ trajectoryPrefix?: { selectedContexts?: unknown };
51
+ metadata?: { selectedContexts?: unknown };
52
+ }
53
+ | undefined;
54
+ collect(contextObject?.trajectoryPrefix?.selectedContexts);
55
+ collect(contextObject?.metadata?.selectedContexts);
56
+ return contexts.some((context) => selected.has(context));
57
+ }
58
+
59
+ const formatPositions = (positions: LpPositionDetails[]): string => {
60
+ if (!positions || positions.length === 0) {
61
+ return "No active LP positions found.";
62
+ }
63
+
64
+ let response = "LP positions:\n";
65
+ positions.forEach((pos, index) => {
66
+ const underlying = (pos.underlyingTokens || [])
67
+ .map(
68
+ (token: TokenBalance) =>
69
+ `${token.uiAmount?.toFixed(4) || token.balance || "N/A"} ${token.symbol || token.address}`,
70
+ )
71
+ .join(" / ");
72
+ response +=
73
+ `\n${index + 1}. ${pos.poolId} on ${pos.dex}\n` +
74
+ ` Value: $${pos.valueUsd?.toFixed(2) || "N/A"}\n` +
75
+ ` Tokens: ${underlying || "N/A"}\n` +
76
+ ` LP balance: ${pos.lpTokenBalance?.uiAmount?.toFixed(6) || pos.lpTokenBalance?.balance || "N/A"} ${pos.lpTokenBalance?.symbol || ""}\n`;
77
+ });
78
+ return response;
79
+ };
80
+
81
+ const formatPools = (pools: PoolInfo[]): string => {
82
+ if (!pools || pools.length === 0) {
83
+ return "No matching LP pools are registered or available.";
84
+ }
85
+
86
+ let response = "LP pools:\n";
87
+ pools.slice(0, 10).forEach((pool, index) => {
88
+ const tokenA =
89
+ pool.tokenA?.symbol ||
90
+ pool.tokenA?.mint ||
91
+ pool.tokenA?.address ||
92
+ "tokenA";
93
+ const tokenB =
94
+ pool.tokenB?.symbol ||
95
+ pool.tokenB?.mint ||
96
+ pool.tokenB?.address ||
97
+ "tokenB";
98
+ response +=
99
+ `\n${index + 1}. ${pool.displayName || pool.id} on ${pool.dex}\n` +
100
+ ` Pair: ${tokenA}/${tokenB}\n` +
101
+ ` APR: ${pool.apr?.toFixed(2) || pool.apy?.toFixed(2) || "N/A"}%\n` +
102
+ ` TVL: ${pool.tvl !== undefined ? `$${pool.tvl.toLocaleString()}` : "N/A"}\n`;
103
+ });
104
+
105
+ if (pools.length > 10) {
106
+ response += `\nShowing 10 of ${pools.length} pools.`;
107
+ }
108
+ return response;
109
+ };
110
+
111
+ const parseIntentFromMessage = (text: string): LpActionParams | null => {
112
+ const lowerText = text.toLowerCase();
113
+
114
+ if (
115
+ lowerText.includes("start lp management") ||
116
+ lowerText.includes("set me up") ||
117
+ lowerText.includes("onboard") ||
118
+ lowerText.includes("get started")
119
+ ) {
120
+ return { subaction: "onboard" };
121
+ }
122
+
123
+ if (
124
+ lowerText.includes("auto-rebalance") ||
125
+ lowerText.includes("auto rebalance") ||
126
+ lowerText.includes("preference") ||
127
+ lowerText.includes("slippage")
128
+ ) {
129
+ return { subaction: "set_preferences" };
130
+ }
131
+
132
+ if (
133
+ lowerText.includes("rebalance") ||
134
+ lowerText.includes("reposition") ||
135
+ lowerText.includes("adjust range") ||
136
+ lowerText.includes("move range")
137
+ ) {
138
+ return { subaction: "reposition" };
139
+ }
140
+
141
+ if (
142
+ lowerText.includes("withdraw") ||
143
+ lowerText.includes("remove liquidity") ||
144
+ lowerText.includes("close position") ||
145
+ (lowerText.includes("exit") && lowerText.includes("position"))
146
+ ) {
147
+ return { subaction: "close" };
148
+ }
149
+
150
+ if (
151
+ lowerText.includes("add liquidity") ||
152
+ lowerText.includes("deposit") ||
153
+ lowerText.includes("open position") ||
154
+ (lowerText.includes("add") && lowerText.includes("pool"))
155
+ ) {
156
+ return { subaction: "open" };
157
+ }
158
+
159
+ if (
160
+ lowerText.includes("show pools") ||
161
+ lowerText.includes("list pools") ||
162
+ lowerText.includes("find pools") ||
163
+ lowerText.includes("best pool")
164
+ ) {
165
+ return { subaction: "list_pools" };
166
+ }
167
+
168
+ if (
169
+ (lowerText.includes("show") &&
170
+ (lowerText.includes("position") || lowerText.includes("lp"))) ||
171
+ lowerText.includes("my lp") ||
172
+ (lowerText.includes("check") && lowerText.includes("position"))
173
+ ) {
174
+ return { subaction: "list_positions" };
175
+ }
176
+
177
+ return null;
178
+ };
179
+
180
+ function subactionFromLegacyIntent(
181
+ intent?: LpActionParams["intent"],
182
+ ): LpManagementSubaction | undefined {
183
+ switch (intent) {
184
+ case "onboard_lp":
185
+ return "onboard";
186
+ case "deposit_lp":
187
+ case "create_concentrated_lp":
188
+ return "open";
189
+ case "withdraw_lp":
190
+ return "close";
191
+ case "show_lps":
192
+ case "show_concentrated_lps":
193
+ return "list_positions";
194
+ case "rebalance_concentrated_lp":
195
+ return "reposition";
196
+ case "set_lp_preferences":
197
+ return "set_preferences";
198
+ default:
199
+ return undefined;
200
+ }
201
+ }
202
+
203
+ function normalizeParams(
204
+ message: Memory,
205
+ handlerParams?: Record<string, unknown>,
206
+ ): LpActionParams | null {
207
+ const contentParams = (message?.content || {}) as Record<string, unknown>;
208
+ const params = {
209
+ ...contentParams,
210
+ ...(handlerParams || {}),
211
+ } as LpActionParams & {
212
+ action?: LpManagementSubaction;
213
+ op?: LpManagementSubaction;
214
+ };
215
+
216
+ // Canonical planner discriminator is action; keep legacy aliases accepted.
217
+ if (!params.subaction && params.action) {
218
+ params.subaction = params.action;
219
+ }
220
+ if (!params.subaction && params.op) {
221
+ params.subaction = params.op;
222
+ }
223
+ if (!params.subaction && params.intent) {
224
+ params.subaction = subactionFromLegacyIntent(params.intent);
225
+ }
226
+ if (!params.subaction && typeof contentParams.text === "string") {
227
+ const parsed = parseIntentFromMessage(contentParams.text);
228
+ if (parsed) {
229
+ Object.assign(params, parsed);
230
+ }
231
+ }
232
+
233
+ return params.subaction ? params : null;
234
+ }
235
+
236
+ function resolveChain(params: LpActionParams): {
237
+ chain?: "solana" | "evm";
238
+ chainId?: number;
239
+ } {
240
+ const dex = (params.dex || params.dexName || "").toLowerCase();
241
+ let chain = params.chain?.toString().toLowerCase();
242
+ let chainId = params.chainId;
243
+
244
+ const numericChain = chain && /^\d+$/.test(chain) ? Number(chain) : undefined;
245
+ const evmChain = getChainConfig(numericChain ?? chain);
246
+ if (evmChain) {
247
+ chain = "evm";
248
+ chainId = evmChain.chainId;
249
+ }
250
+
251
+ if (!chain && SOLANA_DEXES.has(dex)) chain = "solana";
252
+ if (!chain && EVM_DEXES.has(dex)) chain = "evm";
253
+
254
+ if (chain !== "solana" && chain !== "evm") {
255
+ return { chain: undefined, chainId };
256
+ }
257
+
258
+ return { chain, chainId };
259
+ }
260
+
261
+ function getPoolParam(params: LpActionParams): string | undefined {
262
+ return params.pool || params.poolId;
263
+ }
264
+
265
+ function getPositionParam(params: LpActionParams): string | undefined {
266
+ return params.position || params.positionId;
267
+ }
268
+
269
+ function getAmountParam(params: LpActionParams): LpActionParams["amount"] {
270
+ if (params.amount !== undefined) return params.amount;
271
+ if (
272
+ params.tokenAAmount !== undefined ||
273
+ params.tokenBAmount !== undefined ||
274
+ params.lpTokenAmount !== undefined ||
275
+ params.percentage !== undefined
276
+ ) {
277
+ return {
278
+ tokenA: params.tokenAAmount,
279
+ tokenB: params.tokenBAmount,
280
+ lpToken: params.lpTokenAmount,
281
+ percentage: params.percentage,
282
+ };
283
+ }
284
+ return undefined;
285
+ }
286
+
287
+ function getRangeParam(params: LpActionParams): LpActionParams["range"] {
288
+ return (
289
+ params.range || {
290
+ tickLowerIndex: params.tickLowerIndex,
291
+ tickUpperIndex: params.tickUpperIndex,
292
+ priceLower: params.priceLower,
293
+ priceUpper: params.priceUpper,
294
+ }
295
+ );
296
+ }
297
+
298
+ function getEvmWallet(runtime: IAgentRuntime) {
299
+ const rawPrivateKey = runtime.getSetting("EVM_PRIVATE_KEY");
300
+ if (!rawPrivateKey || typeof rawPrivateKey !== "string") {
301
+ throw new Error("EVM_PRIVATE_KEY is required for EVM LP operations.");
302
+ }
303
+ const privateKey = rawPrivateKey.startsWith("0x")
304
+ ? rawPrivateKey
305
+ : `0x${rawPrivateKey}`;
306
+ const account = privateKeyToAccount(privateKey as `0x${string}`);
307
+ return { address: account.address, privateKey: privateKey as `0x${string}` };
308
+ }
309
+
310
+ async function getProfileServices(runtime: IAgentRuntime) {
311
+ const vault = runtime.getService<IVaultService>("VaultService");
312
+ const profileService = runtime.getService<IUserLpProfileService>(
313
+ "UserLpProfileService",
314
+ );
315
+ if (!vault || !profileService) {
316
+ throw new Error("LP vault/profile services are unavailable.");
317
+ }
318
+ return { vault, profileService };
319
+ }
320
+
321
+ async function requireProfile(
322
+ runtime: IAgentRuntime,
323
+ userId: string,
324
+ ): Promise<{
325
+ vault: IVaultService;
326
+ profileService: IUserLpProfileService;
327
+ profile: UserLpProfile;
328
+ }> {
329
+ const { vault, profileService } = await getProfileServices(runtime);
330
+ const profile = await profileService.getProfile(userId);
331
+ if (!profile) {
332
+ throw new Error(
333
+ "No LP profile found. Use subaction=onboard before managing Solana LP positions.",
334
+ );
335
+ }
336
+ return { vault, profileService, profile };
337
+ }
338
+
339
+ async function operationAuth(
340
+ runtime: IAgentRuntime,
341
+ userId: string,
342
+ params: LpActionParams,
343
+ ) {
344
+ const { chain, chainId } = resolveChain(params);
345
+ if (chain === "evm") {
346
+ const wallet = getEvmWallet(runtime);
347
+ return { chain, chainId, wallet, owner: wallet.address };
348
+ }
349
+
350
+ const { vault, profile, profileService } = await requireProfile(
351
+ runtime,
352
+ userId,
353
+ );
354
+ const userVault = await vault.getVaultKeypair(
355
+ userId,
356
+ profile.encryptedSecretKey,
357
+ );
358
+ return {
359
+ chain: chain || "solana",
360
+ chainId,
361
+ userVault,
362
+ owner: profile.vaultPublicKey,
363
+ profile,
364
+ profileService,
365
+ };
366
+ }
367
+
368
+ async function handleOnboard(runtime: IAgentRuntime, userId: string) {
369
+ const { vault, profileService } = await getProfileServices(runtime);
370
+ const existingProfile = await profileService.getProfile(userId);
371
+ if (existingProfile) {
372
+ return {
373
+ success: true,
374
+ text: `You're already set up. Vault address: ${existingProfile.vaultPublicKey}`,
375
+ };
376
+ }
377
+
378
+ const { publicKey, secretKeyEncrypted } = await vault.createVault(userId);
379
+ const newProfile = await profileService.ensureProfile(
380
+ userId,
381
+ publicKey,
382
+ secretKeyEncrypted,
383
+ );
384
+ return {
385
+ success: true,
386
+ text: `LP vault created. Vault address: ${newProfile.vaultPublicKey}. Auto-rebalancing is ${newProfile.autoRebalanceConfig.enabled ? "on" : "off"}.`,
387
+ };
388
+ }
389
+
390
+ async function handlePreferences(
391
+ runtime: IAgentRuntime,
392
+ userId: string,
393
+ params: LpActionParams,
394
+ ) {
395
+ const { profileService, profile } = await requireProfile(runtime, userId);
396
+ const newConfig = { ...profile.autoRebalanceConfig };
397
+ const updates: string[] = [];
398
+
399
+ if (params.autoRebalanceEnabled !== undefined) {
400
+ newConfig.enabled = params.autoRebalanceEnabled;
401
+ updates.push(`autoRebalance=${newConfig.enabled}`);
402
+ }
403
+ if (params.minGainThresholdPercent !== undefined) {
404
+ newConfig.minGainThresholdPercent = params.minGainThresholdPercent;
405
+ updates.push(`minGainThresholdPercent=${params.minGainThresholdPercent}`);
406
+ }
407
+ if (params.maxSlippageBps !== undefined || params.slippageBps !== undefined) {
408
+ newConfig.maxSlippageBps = params.maxSlippageBps ?? params.slippageBps;
409
+ updates.push(`maxSlippageBps=${newConfig.maxSlippageBps}`);
410
+ }
411
+ if (params.preferredDexes) {
412
+ newConfig.preferredDexes = params.preferredDexes;
413
+ updates.push(`preferredDexes=${params.preferredDexes.join(",")}`);
414
+ }
415
+
416
+ await profileService.updateProfile(userId, {
417
+ autoRebalanceConfig: newConfig,
418
+ });
419
+
420
+ return {
421
+ success: true,
422
+ text: updates.length
423
+ ? `LP preferences updated: ${updates.join(", ")}`
424
+ : "No LP preference changes were provided.",
425
+ };
426
+ }
427
+
428
+ function baseRoute(params: LpActionParams) {
429
+ const { chain, chainId } = resolveChain(params);
430
+ return {
431
+ chain,
432
+ chainId,
433
+ dex: params.dex || params.dexName,
434
+ };
435
+ }
436
+
437
+ async function handleLpOperation(
438
+ runtime: IAgentRuntime,
439
+ lp: LpManagementService,
440
+ userId: string,
441
+ params: LpActionParams,
442
+ ) {
443
+ const route = baseRoute(params);
444
+
445
+ switch (params.subaction) {
446
+ case "list_pools": {
447
+ const pools = await lp.listPools({
448
+ ...route,
449
+ tokenA: params.tokenA,
450
+ tokenB: params.tokenB,
451
+ feeTier: params.feeTier,
452
+ });
453
+ return { success: true, text: formatPools(pools), data: { pools } };
454
+ }
455
+
456
+ case "list_positions": {
457
+ const auth = await operationAuth(runtime, userId, params);
458
+ let positions = await lp.listPositions({
459
+ ...route,
460
+ chain: route.chain || auth.chain,
461
+ chainId: route.chainId || auth.chainId,
462
+ owner: auth.owner,
463
+ });
464
+ if (
465
+ positions.length === 0 &&
466
+ auth.chain === "solana" &&
467
+ auth.profileService
468
+ ) {
469
+ const trackedPositions =
470
+ await auth.profileService.getTrackedPositions(userId);
471
+ positions = (
472
+ await Promise.all(
473
+ trackedPositions.map((tracked) =>
474
+ lp
475
+ .getPosition({
476
+ chain: "solana",
477
+ dex: tracked.dex,
478
+ owner: auth.owner,
479
+ pool: tracked.poolAddress,
480
+ position: tracked.positionIdentifier,
481
+ })
482
+ .catch(() => null),
483
+ ),
484
+ )
485
+ ).filter(Boolean);
486
+ }
487
+ return {
488
+ success: true,
489
+ text: formatPositions(positions),
490
+ data: { positions },
491
+ };
492
+ }
493
+
494
+ case "get_position": {
495
+ const auth = await operationAuth(runtime, userId, params);
496
+ const position = await lp.getPosition({
497
+ ...route,
498
+ chain: route.chain || auth.chain,
499
+ chainId: route.chainId || auth.chainId,
500
+ owner: auth.owner,
501
+ pool: getPoolParam(params),
502
+ position: getPositionParam(params),
503
+ });
504
+ return {
505
+ success: true,
506
+ text: position
507
+ ? formatPositions([position])
508
+ : "No matching LP position found.",
509
+ data: { position },
510
+ };
511
+ }
512
+
513
+ case "open": {
514
+ const auth = await operationAuth(runtime, userId, params);
515
+ const result = await lp.openPosition({
516
+ ...route,
517
+ chain: route.chain || auth.chain,
518
+ chainId: route.chainId || auth.chainId,
519
+ userVault: auth.userVault,
520
+ wallet: auth.wallet,
521
+ owner: auth.owner,
522
+ pool: getPoolParam(params),
523
+ amount: getAmountParam(params),
524
+ amounts: params.amounts,
525
+ range: getRangeParam(params),
526
+ slippageBps: params.slippageBps ?? params.maxSlippageBps,
527
+ });
528
+ return {
529
+ success: result.success,
530
+ text: result.success
531
+ ? `LP position opened on ${route.dex || "registered protocol"}. Transaction: ${result.transactionId || result.hash || "submitted"}`
532
+ : `LP open failed: ${result.error || "unknown error"}`,
533
+ data: result,
534
+ };
535
+ }
536
+
537
+ case "close": {
538
+ const auth = await operationAuth(runtime, userId, params);
539
+ const result = await lp.closePosition({
540
+ ...route,
541
+ chain: route.chain || auth.chain,
542
+ chainId: route.chainId || auth.chainId,
543
+ userVault: auth.userVault,
544
+ wallet: auth.wallet,
545
+ owner: auth.owner,
546
+ pool: getPoolParam(params),
547
+ position: getPositionParam(params),
548
+ amount: getAmountParam(params),
549
+ amounts: params.amounts,
550
+ slippageBps: params.slippageBps ?? params.maxSlippageBps,
551
+ });
552
+ return {
553
+ success: result.success,
554
+ text: result.success
555
+ ? `LP position closed on ${route.dex || "registered protocol"}. Transaction: ${result.transactionId || result.hash || "submitted"}`
556
+ : `LP close failed: ${result.error || "unknown error"}`,
557
+ data: result,
558
+ };
559
+ }
560
+
561
+ case "reposition": {
562
+ const auth = await operationAuth(runtime, userId, params);
563
+ const result = await lp.repositionPosition({
564
+ ...route,
565
+ chain: route.chain || auth.chain,
566
+ chainId: route.chainId || auth.chainId,
567
+ userVault: auth.userVault,
568
+ wallet: auth.wallet,
569
+ owner: auth.owner,
570
+ pool: getPoolParam(params),
571
+ position: getPositionParam(params),
572
+ amount: getAmountParam(params),
573
+ amounts: params.amounts,
574
+ range: getRangeParam(params),
575
+ slippageBps: params.slippageBps ?? params.maxSlippageBps,
576
+ });
577
+ return {
578
+ success: result.success,
579
+ text: result.success
580
+ ? `LP position repositioned on ${route.dex || "registered protocol"}. Transaction: ${result.transactionId || result.hash || "submitted"}`
581
+ : `LP reposition failed: ${result.error || "unknown error"}`,
582
+ data: result,
583
+ };
584
+ }
585
+
586
+ default:
587
+ return {
588
+ success: false,
589
+ text: `Unsupported LP action: ${params.subaction}`,
590
+ };
591
+ }
592
+ }
593
+
594
+ export const liquidityAction: Action = {
595
+ name: "LIQUIDITY",
596
+ contexts: ["finance", "crypto", "wallet", "automation"],
597
+ contextGate: { anyOf: ["finance", "crypto", "wallet", "automation"] },
598
+ roleGate: { minRole: "USER" },
599
+ description:
600
+ "Single LP/liquidity management action. action=onboard|list_pools|open|close|reposition|list_positions|get_position|set_preferences. dex=orca|raydium|meteora|uniswap|aerodrome|pancakeswap selects the protocol; chain=solana|evm is inferred from dex when omitted.",
601
+ descriptionCompressed:
602
+ "Manage LP positions by action, chain, dex, pool, position, amount, range, token filters.",
603
+ parameters: [
604
+ {
605
+ name: "action",
606
+ description:
607
+ "Liquidity operation: onboard, list_pools, open, close, reposition, list_positions, get_position, set_preferences.",
608
+ required: true,
609
+ schema: {
610
+ type: "string",
611
+ enum: [
612
+ "onboard",
613
+ "list_pools",
614
+ "open",
615
+ "close",
616
+ "reposition",
617
+ "list_positions",
618
+ "get_position",
619
+ "set_preferences",
620
+ ],
621
+ },
622
+ },
623
+ {
624
+ name: "subaction",
625
+ description: "Legacy alias for action.",
626
+ required: false,
627
+ schema: { type: "string" },
628
+ },
629
+ {
630
+ name: "chain",
631
+ description: "Chain for the LP operation.",
632
+ required: false,
633
+ schema: { type: "string", enum: ["solana", "evm"] },
634
+ },
635
+ {
636
+ name: "dex",
637
+ description: "DEX/protocol name.",
638
+ required: false,
639
+ schema: { type: "string" },
640
+ },
641
+ {
642
+ name: "pool",
643
+ description:
644
+ "Pool id/address for open, close, reposition, or position lookup.",
645
+ required: false,
646
+ schema: { type: "string" },
647
+ },
648
+ {
649
+ name: "position",
650
+ description: "LP position id/mint/address.",
651
+ required: false,
652
+ schema: { type: "string" },
653
+ },
654
+ {
655
+ name: "amount",
656
+ description:
657
+ "Liquidity amount for open, close, or reposition operations.",
658
+ required: false,
659
+ schema: { type: ["string", "number"] },
660
+ },
661
+ {
662
+ name: "range",
663
+ description: "Desired concentrated liquidity price range.",
664
+ required: false,
665
+ schema: { type: "object" },
666
+ },
667
+ {
668
+ name: "tokenA",
669
+ description: "First token filter or deposit token.",
670
+ required: false,
671
+ schema: { type: "string" },
672
+ },
673
+ {
674
+ name: "tokenB",
675
+ description: "Second token filter or deposit token.",
676
+ required: false,
677
+ schema: { type: "string" },
678
+ },
679
+ {
680
+ name: "chainId",
681
+ description: "Optional numeric EVM chain id.",
682
+ required: false,
683
+ schema: { type: "number" },
684
+ },
685
+ {
686
+ name: "slippageBps",
687
+ description: "Maximum allowed slippage in basis points.",
688
+ required: false,
689
+ schema: { type: "number" },
690
+ },
691
+ ],
692
+
693
+ similes: [
694
+ "lp_management",
695
+ "LP_MANAGEMENT",
696
+ "LIQUIDITY_POOL_MANAGEMENT",
697
+ "LP_MANAGER",
698
+ "MANAGE_LP",
699
+ "MANAGE_LIQUIDITY",
700
+ "MANAGE_LP_POSITIONS",
701
+ "manage_positions",
702
+ "manage_raydium_positions",
703
+ "AUTOMATE_REBALANCING",
704
+ "AUTOMATE_POSITIONS",
705
+ "START_MANAGING_POSITIONS",
706
+ "AUTOMATE_RAYDIUM_REBALANCING",
707
+ "AUTOMATE_RAYDIUM_POSITIONS",
708
+ "START_MANAGING_RAYDIUM_POSITIONS",
709
+ ],
710
+
711
+ examples: [
712
+ [
713
+ {
714
+ name: "{{name1}}",
715
+ content: { text: "Open a Raydium LP position with 100 USDC paired against SOL.", source: "chat" },
716
+ },
717
+ {
718
+ name: "{{agentName}}",
719
+ content: {
720
+ text: "Opening the LP position.",
721
+ actions: ["LIQUIDITY"],
722
+ thought:
723
+ "User wants to provide liquidity on a DEX; LIQUIDITY action=open with the token pair and amount routes to the LP manager.",
724
+ },
725
+ },
726
+ ],
727
+ [
728
+ {
729
+ name: "{{name1}}",
730
+ content: { text: "Show my current liquidity positions.", source: "chat" },
731
+ },
732
+ {
733
+ name: "{{agentName}}",
734
+ content: {
735
+ text: "Listing your LP positions.",
736
+ actions: ["LIQUIDITY"],
737
+ thought:
738
+ "Position inventory query maps to LIQUIDITY action=list_positions and returns active LP positions across pools.",
739
+ },
740
+ },
741
+ ],
742
+ [
743
+ {
744
+ name: "{{name1}}",
745
+ content: { text: "Close my SOL/USDC liquidity position.", source: "chat" },
746
+ },
747
+ {
748
+ name: "{{agentName}}",
749
+ content: {
750
+ text: "Closing the LP position.",
751
+ actions: ["LIQUIDITY"],
752
+ thought:
753
+ "Withdraw / unwind intent maps to LIQUIDITY action=close on the named pair.",
754
+ },
755
+ },
756
+ ],
757
+ ] as ActionExample[][],
758
+
759
+ validate: async (
760
+ _runtime: IAgentRuntime,
761
+ message: Memory,
762
+ state?: State,
763
+ ): Promise<boolean> => {
764
+ if (!message?.content) return false;
765
+ if ((message.content as LpActionParams & { action?: string }).action)
766
+ return true;
767
+ if ((message.content as LpActionParams).subaction) return true;
768
+ if ((message.content as LpActionParams & { op?: string }).op) return true;
769
+ if (
770
+ selectedContextMatches(state, [
771
+ "finance",
772
+ "crypto",
773
+ "wallet",
774
+ "automation",
775
+ ])
776
+ ) {
777
+ return true;
778
+ }
779
+
780
+ return false;
781
+ },
782
+
783
+ handler: async (runtime, message, _state, handlerParams) => {
784
+ const params = normalizeParams(message, handlerParams);
785
+ if (!params) {
786
+ return {
787
+ success: true,
788
+ text: "Use LIQUIDITY with action=list_pools, open, close, reposition, list_positions, get_position, set_preferences, or onboard.",
789
+ };
790
+ }
791
+
792
+ const userId = message.entityId || message.userId || "unknown-user";
793
+
794
+ try {
795
+ if (params.subaction === "onboard") {
796
+ return await handleOnboard(runtime, userId);
797
+ }
798
+ if (params.subaction === "set_preferences") {
799
+ return await handlePreferences(runtime, userId, params);
800
+ }
801
+
802
+ const lp = await getLpManagementService(runtime);
803
+ if (!lp) {
804
+ return {
805
+ success: false,
806
+ text: "LP management service is currently unavailable.",
807
+ };
808
+ }
809
+
810
+ return await handleLpOperation(runtime, lp, userId, params);
811
+ } catch (error: unknown) {
812
+ const errorMessage =
813
+ error instanceof Error ? error.message : String(error);
814
+ if (error instanceof NoMatchingLpProtocolError) {
815
+ return {
816
+ success: false,
817
+ text: errorMessage,
818
+ };
819
+ }
820
+ console.error(`[LIQUIDITY] Error: ${errorMessage}`);
821
+ return {
822
+ success: false,
823
+ text: `Liquidity operation failed: ${errorMessage}`,
824
+ };
825
+ }
826
+ },
827
+ };