@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,34 @@
1
+ // @ts-nocheck — legacy code from absorbed plugins (lp-manager, lpinfo, dexscreener, defi-news, birdeye); strict types pending cleanup
2
+ import type { IAgentRuntime, Plugin } from "@elizaos/core";
3
+ import {
4
+ createEvmLpProtocolProvider,
5
+ registerLpProtocolProvider,
6
+ } from "../../../../lp/services/LpManagementService.ts";
7
+ import { AerodromeLpService } from "./services/AerodromeLpService.ts";
8
+
9
+ export const aerodromePlugin: Plugin = {
10
+ name: "@elizaos/plugin-lp-manager/aerodrome",
11
+ description: "Aerodrome DEX liquidity pool management plugin for Base chain",
12
+ services: [AerodromeLpService],
13
+ actions: [],
14
+ providers: [],
15
+ init: async (_config: Record<string, string>, runtime: IAgentRuntime) => {
16
+ console.info("Aerodrome Plugin initialized");
17
+ const service =
18
+ runtime.getService<AerodromeLpService>(AerodromeLpService.serviceType) ??
19
+ (await AerodromeLpService.start(runtime));
20
+ await registerLpProtocolProvider(
21
+ runtime,
22
+ createEvmLpProtocolProvider({
23
+ dex: "aerodrome",
24
+ label: "Aerodrome",
25
+ service,
26
+ }),
27
+ );
28
+ },
29
+ };
30
+
31
+ export * from "./types.ts";
32
+ export { AerodromeLpService };
33
+
34
+ export default aerodromePlugin;
@@ -0,0 +1,558 @@
1
+ // @ts-nocheck — legacy code from absorbed plugins (lp-manager, lpinfo, dexscreener, defi-news, birdeye); strict types pending cleanup
2
+ import { type IAgentRuntime, logger, Service } from "@elizaos/core";
3
+ import {
4
+ type Address,
5
+ createPublicClient,
6
+ createWalletClient,
7
+ http,
8
+ type PublicClient,
9
+ type WalletClient,
10
+ } from "viem";
11
+ import { privateKeyToAccount } from "viem/accounts";
12
+ import { base } from "viem/chains";
13
+ import type {
14
+ EvmAddLiquidityParams,
15
+ EvmDex,
16
+ EvmPoolInfo,
17
+ EvmPositionDetails,
18
+ EvmRemoveLiquidityParams,
19
+ EvmTransactionResult,
20
+ IEvmLpService,
21
+ } from "../../../../../lp/types.ts";
22
+ import {
23
+ AERODROME_ADDRESSES,
24
+ AERODROME_FACTORY_ABI,
25
+ AERODROME_POOL_ABI,
26
+ AERODROME_ROUTER_ABI,
27
+ type AerodromePoolType,
28
+ ERC20_ABI,
29
+ } from "../types.ts";
30
+
31
+ const SUPPORTED_CHAIN_IDS = [8453]; // Base only
32
+
33
+ export class AerodromeLpService extends Service implements IEvmLpService {
34
+ public static readonly serviceType = "aerodrome-lp";
35
+ public readonly capabilityDescription =
36
+ "Provides Aerodrome DEX liquidity pool management on Base chain.";
37
+
38
+ private publicClient: PublicClient | null = null;
39
+ private walletClients: Map<string, WalletClient> = new Map();
40
+ private rpcUrl: string | null = null;
41
+
42
+ constructor(runtime?: IAgentRuntime) {
43
+ super(runtime);
44
+ if (runtime) {
45
+ this.initializeRpcUrl();
46
+ }
47
+ }
48
+
49
+ private initializeRpcUrl(): void {
50
+ const envKeys = ["BASE_RPC_URL", "EVM_PROVIDER_BASE"];
51
+ for (const key of envKeys) {
52
+ const rpcUrl = this.runtime.getSetting(key);
53
+ if (rpcUrl && typeof rpcUrl === "string") {
54
+ this.rpcUrl = rpcUrl;
55
+ break;
56
+ }
57
+ }
58
+ }
59
+
60
+ private getPublicClient(): PublicClient {
61
+ if (this.publicClient) return this.publicClient;
62
+
63
+ this.publicClient = createPublicClient({
64
+ chain: base,
65
+ transport: this.rpcUrl ? http(this.rpcUrl) : http(),
66
+ });
67
+
68
+ return this.publicClient;
69
+ }
70
+
71
+ private getWalletClient(privateKey: `0x${string}`): WalletClient {
72
+ const cacheKey = privateKey.slice(0, 10);
73
+ let client = this.walletClients.get(cacheKey);
74
+ if (client) return client;
75
+
76
+ const account = privateKeyToAccount(privateKey);
77
+
78
+ client = createWalletClient({
79
+ chain: base,
80
+ transport: this.rpcUrl ? http(this.rpcUrl) : http(),
81
+ account,
82
+ });
83
+
84
+ this.walletClients.set(cacheKey, client);
85
+ return client;
86
+ }
87
+
88
+ static async start(runtime: IAgentRuntime): Promise<AerodromeLpService> {
89
+ const service = new AerodromeLpService(runtime);
90
+ logger.info("[AerodromeLpService] started");
91
+ return service;
92
+ }
93
+
94
+ async stop(): Promise<void> {
95
+ this.publicClient = null;
96
+ this.walletClients.clear();
97
+ logger.info("[AerodromeLpService] stopped");
98
+ }
99
+
100
+ getDexName(): EvmDex {
101
+ return "aerodrome";
102
+ }
103
+
104
+ getSupportedChainIds(): number[] {
105
+ return SUPPORTED_CHAIN_IDS;
106
+ }
107
+
108
+ supportsChain(chainId: number): boolean {
109
+ return chainId === 8453;
110
+ }
111
+
112
+ async getPools(
113
+ chainId: number,
114
+ tokenA?: Address,
115
+ tokenB?: Address,
116
+ _feeTier?: number // Ignored for Aerodrome - uses stable/volatile instead
117
+ ): Promise<EvmPoolInfo[]> {
118
+ if (!this.supportsChain(chainId)) {
119
+ logger.warn(`[AerodromeLpService] Chain ${chainId} not supported`);
120
+ return [];
121
+ }
122
+
123
+ const addresses = AERODROME_ADDRESSES[8453];
124
+ const client = this.getPublicClient();
125
+ const pools: EvmPoolInfo[] = [];
126
+
127
+ if (tokenA && tokenB) {
128
+ // Check both volatile and stable pools
129
+ for (const stable of [false, true]) {
130
+ try {
131
+ const poolAddress = await client.readContract({
132
+ address: addresses.factory,
133
+ abi: AERODROME_FACTORY_ABI,
134
+ functionName: "getPool",
135
+ args: [tokenA, tokenB, stable],
136
+ });
137
+
138
+ if (poolAddress && poolAddress !== "0x0000000000000000000000000000000000000000") {
139
+ const poolInfo = await this.getPoolInfo(poolAddress as Address);
140
+ if (poolInfo) {
141
+ pools.push(poolInfo);
142
+ }
143
+ }
144
+ } catch (_error) {
145
+ logger.debug(
146
+ `[AerodromeLpService] No ${stable ? "stable" : "volatile"} pool found for ${tokenA}/${tokenB}`
147
+ );
148
+ }
149
+ }
150
+ }
151
+
152
+ return pools;
153
+ }
154
+
155
+ private async getPoolInfo(poolAddress: Address): Promise<EvmPoolInfo | null> {
156
+ const client = this.getPublicClient();
157
+
158
+ try {
159
+ const [token0, token1, stable, reserves] = await Promise.all([
160
+ client.readContract({
161
+ address: poolAddress,
162
+ abi: AERODROME_POOL_ABI,
163
+ functionName: "token0",
164
+ }),
165
+ client.readContract({
166
+ address: poolAddress,
167
+ abi: AERODROME_POOL_ABI,
168
+ functionName: "token1",
169
+ }),
170
+ client.readContract({
171
+ address: poolAddress,
172
+ abi: AERODROME_POOL_ABI,
173
+ functionName: "stable",
174
+ }),
175
+ client.readContract({
176
+ address: poolAddress,
177
+ abi: AERODROME_POOL_ABI,
178
+ functionName: "getReserves",
179
+ }),
180
+ ]);
181
+
182
+ const [symbol0, decimals0, symbol1, decimals1] = await Promise.all([
183
+ client
184
+ .readContract({
185
+ address: token0 as Address,
186
+ abi: ERC20_ABI,
187
+ functionName: "symbol",
188
+ })
189
+ .catch(() => "UNKNOWN"),
190
+ client
191
+ .readContract({
192
+ address: token0 as Address,
193
+ abi: ERC20_ABI,
194
+ functionName: "decimals",
195
+ })
196
+ .catch(() => 18),
197
+ client
198
+ .readContract({
199
+ address: token1 as Address,
200
+ abi: ERC20_ABI,
201
+ functionName: "symbol",
202
+ })
203
+ .catch(() => "UNKNOWN"),
204
+ client
205
+ .readContract({
206
+ address: token1 as Address,
207
+ abi: ERC20_ABI,
208
+ functionName: "decimals",
209
+ })
210
+ .catch(() => 18),
211
+ ]);
212
+
213
+ const poolType: AerodromePoolType = stable ? "stable" : "volatile";
214
+
215
+ const poolInfo: EvmPoolInfo = {
216
+ id: poolAddress,
217
+ dex: "aerodrome",
218
+ chainId: 8453,
219
+ chainName: "Base",
220
+ poolAddress,
221
+ tokenA: {
222
+ address: token0 as Address,
223
+ symbol: symbol0 as string,
224
+ decimals: Number(decimals0),
225
+ reserve: reserves[0].toString(),
226
+ },
227
+ tokenB: {
228
+ address: token1 as Address,
229
+ symbol: symbol1 as string,
230
+ decimals: Number(decimals1),
231
+ reserve: reserves[1].toString(),
232
+ },
233
+ fee: stable ? 0.0004 : 0.003, // 0.04% for stable, 0.3% for volatile
234
+ displayName: `${symbol0}/${symbol1} (${poolType})`,
235
+ metadata: {
236
+ poolType,
237
+ stable,
238
+ },
239
+ };
240
+
241
+ return poolInfo;
242
+ } catch (error) {
243
+ logger.error(`[AerodromeLpService] Error fetching pool info for ${poolAddress}:`, error);
244
+ return null;
245
+ }
246
+ }
247
+
248
+ async addLiquidity(params: EvmAddLiquidityParams): Promise<EvmTransactionResult> {
249
+ if (!this.supportsChain(params.chainId)) {
250
+ return {
251
+ success: false,
252
+ error: `Chain ${params.chainId} not supported. Aerodrome is only on Base (8453).`,
253
+ };
254
+ }
255
+
256
+ const addresses = AERODROME_ADDRESSES[8453];
257
+
258
+ try {
259
+ const publicClient = this.getPublicClient();
260
+ const walletClient = this.getWalletClient(params.wallet.privateKey);
261
+
262
+ const poolInfo = await this.getPoolInfo(params.poolAddress);
263
+ if (!poolInfo) {
264
+ return { success: false, error: "Pool not found" };
265
+ }
266
+
267
+ const stable = poolInfo.metadata?.stable === true;
268
+ const slippageMultiplier = BigInt(10000 - params.slippageBps);
269
+ const amountAMin = (params.tokenAAmount * slippageMultiplier) / 10000n;
270
+ const amountBMin = ((params.tokenBAmount ?? 0n) * slippageMultiplier) / 10000n;
271
+
272
+ // Approve tokens for router
273
+ await this.approveToken(
274
+ params.wallet.privateKey,
275
+ poolInfo.tokenA.address,
276
+ addresses.router,
277
+ params.tokenAAmount
278
+ );
279
+
280
+ if (params.tokenBAmount && params.tokenBAmount > 0n) {
281
+ await this.approveToken(
282
+ params.wallet.privateKey,
283
+ poolInfo.tokenB.address,
284
+ addresses.router,
285
+ params.tokenBAmount
286
+ );
287
+ }
288
+
289
+ const deadline = params.deadline ?? BigInt(Math.floor(Date.now() / 1000) + 1800);
290
+
291
+ const { request } = await publicClient.simulateContract({
292
+ address: addresses.router,
293
+ abi: AERODROME_ROUTER_ABI,
294
+ functionName: "addLiquidity",
295
+ args: [
296
+ poolInfo.tokenA.address,
297
+ poolInfo.tokenB.address,
298
+ stable,
299
+ params.tokenAAmount,
300
+ params.tokenBAmount ?? 0n,
301
+ amountAMin,
302
+ amountBMin,
303
+ params.wallet.address,
304
+ deadline,
305
+ ],
306
+ account: walletClient.account,
307
+ });
308
+
309
+ const hash = await walletClient.writeContract(request);
310
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
311
+
312
+ return {
313
+ success: receipt.status === "success",
314
+ transactionId: hash,
315
+ hash,
316
+ chainId: params.chainId,
317
+ blockNumber: receipt.blockNumber,
318
+ gasUsed: receipt.gasUsed,
319
+ data: {
320
+ poolAddress: params.poolAddress,
321
+ stable,
322
+ },
323
+ };
324
+ } catch (error) {
325
+ logger.error("[AerodromeLpService] Error adding liquidity:", error);
326
+ return {
327
+ success: false,
328
+ error: error instanceof Error ? error.message : "Unknown error adding liquidity",
329
+ };
330
+ }
331
+ }
332
+
333
+ async removeLiquidity(params: EvmRemoveLiquidityParams): Promise<EvmTransactionResult> {
334
+ if (!this.supportsChain(params.chainId)) {
335
+ return {
336
+ success: false,
337
+ error: `Chain ${params.chainId} not supported. Aerodrome is only on Base (8453).`,
338
+ };
339
+ }
340
+
341
+ const addresses = AERODROME_ADDRESSES[8453];
342
+
343
+ try {
344
+ const publicClient = this.getPublicClient();
345
+ const walletClient = this.getWalletClient(params.wallet.privateKey);
346
+
347
+ const poolInfo = await this.getPoolInfo(params.poolAddress);
348
+ if (!poolInfo) {
349
+ return { success: false, error: "Pool not found" };
350
+ }
351
+
352
+ const stable = poolInfo.metadata?.stable === true;
353
+
354
+ // Get LP token balance
355
+ let lpTokenAmount = params.lpTokenAmount;
356
+ if (!lpTokenAmount) {
357
+ const balance = await publicClient.readContract({
358
+ address: params.poolAddress,
359
+ abi: AERODROME_POOL_ABI,
360
+ functionName: "balanceOf",
361
+ args: [params.wallet.address],
362
+ });
363
+
364
+ if (params.percentageToRemove && params.percentageToRemove < 100) {
365
+ lpTokenAmount = ((balance as bigint) * BigInt(params.percentageToRemove)) / 100n;
366
+ } else {
367
+ lpTokenAmount = balance as bigint;
368
+ }
369
+ }
370
+
371
+ if (!lpTokenAmount || lpTokenAmount === 0n) {
372
+ return { success: false, error: "No LP tokens to remove" };
373
+ }
374
+
375
+ // Approve LP tokens for router
376
+ await this.approveToken(
377
+ params.wallet.privateKey,
378
+ params.poolAddress,
379
+ addresses.router,
380
+ lpTokenAmount
381
+ );
382
+
383
+ const _slippageMultiplier = BigInt(10000 - params.slippageBps);
384
+ const deadline = params.deadline ?? BigInt(Math.floor(Date.now() / 1000) + 1800);
385
+
386
+ const { request } = await publicClient.simulateContract({
387
+ address: addresses.router,
388
+ abi: AERODROME_ROUTER_ABI,
389
+ functionName: "removeLiquidity",
390
+ args: [
391
+ poolInfo.tokenA.address,
392
+ poolInfo.tokenB.address,
393
+ stable,
394
+ lpTokenAmount,
395
+ 0n, // amountAMin - will be calculated by slippage in real implementation
396
+ 0n, // amountBMin
397
+ params.wallet.address,
398
+ deadline,
399
+ ],
400
+ account: walletClient.account,
401
+ });
402
+
403
+ const hash = await walletClient.writeContract(request);
404
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
405
+
406
+ return {
407
+ success: receipt.status === "success",
408
+ transactionId: hash,
409
+ hash,
410
+ chainId: params.chainId,
411
+ blockNumber: receipt.blockNumber,
412
+ gasUsed: receipt.gasUsed,
413
+ };
414
+ } catch (error) {
415
+ logger.error("[AerodromeLpService] Error removing liquidity:", error);
416
+ return {
417
+ success: false,
418
+ error: error instanceof Error ? error.message : "Unknown error removing liquidity",
419
+ };
420
+ }
421
+ }
422
+
423
+ async getPositionDetails(
424
+ chainId: number,
425
+ owner: Address,
426
+ poolAddress: Address,
427
+ _tokenId?: bigint // Ignored for Aerodrome - uses LP tokens not NFTs
428
+ ): Promise<EvmPositionDetails | null> {
429
+ if (!this.supportsChain(chainId)) return null;
430
+
431
+ const client = this.getPublicClient();
432
+
433
+ try {
434
+ const poolInfo = await this.getPoolInfo(poolAddress);
435
+ if (!poolInfo) return null;
436
+
437
+ const lpBalance = await client.readContract({
438
+ address: poolAddress,
439
+ abi: AERODROME_POOL_ABI,
440
+ functionName: "balanceOf",
441
+ args: [owner],
442
+ });
443
+
444
+ if ((lpBalance as bigint) === 0n) return null;
445
+
446
+ const totalSupply = await client.readContract({
447
+ address: poolAddress,
448
+ abi: AERODROME_POOL_ABI,
449
+ functionName: "totalSupply",
450
+ });
451
+
452
+ // Calculate underlying token amounts based on LP share
453
+ const lpBalanceBigInt = lpBalance as bigint;
454
+ const totalSupplyBigInt = totalSupply as bigint;
455
+ const reserve0 = BigInt(poolInfo.tokenA.reserve ?? "0");
456
+ const reserve1 = BigInt(poolInfo.tokenB.reserve ?? "0");
457
+
458
+ const amount0 =
459
+ totalSupplyBigInt > 0n ? (reserve0 * lpBalanceBigInt) / totalSupplyBigInt : 0n;
460
+ const amount1 =
461
+ totalSupplyBigInt > 0n ? (reserve1 * lpBalanceBigInt) / totalSupplyBigInt : 0n;
462
+
463
+ return {
464
+ poolId: poolAddress,
465
+ dex: "aerodrome",
466
+ chainId: 8453,
467
+ owner,
468
+ lpTokenBalance: {
469
+ address: poolAddress,
470
+ balance: lpBalanceBigInt.toString(),
471
+ decimals: 18,
472
+ symbol: `AERO-LP-${poolInfo.tokenA.symbol}/${poolInfo.tokenB.symbol}`,
473
+ },
474
+ underlyingTokens: [
475
+ {
476
+ address: poolInfo.tokenA.address,
477
+ balance: amount0.toString(),
478
+ decimals: poolInfo.tokenA.decimals,
479
+ symbol: poolInfo.tokenA.symbol,
480
+ },
481
+ {
482
+ address: poolInfo.tokenB.address,
483
+ balance: amount1.toString(),
484
+ decimals: poolInfo.tokenB.decimals,
485
+ symbol: poolInfo.tokenB.symbol,
486
+ },
487
+ ],
488
+ metadata: poolInfo.metadata,
489
+ };
490
+ } catch (error) {
491
+ logger.error(`[AerodromeLpService] Error getting position details for ${owner}:`, error);
492
+ return null;
493
+ }
494
+ }
495
+
496
+ async getAllPositions(chainId: number, _owner: Address): Promise<EvmPositionDetails[]> {
497
+ if (!this.supportsChain(chainId)) return [];
498
+
499
+ // For Aerodrome, we would need to iterate through known pools
500
+ // This is a simplified implementation - in production, you'd want to
501
+ // track pools the user has interacted with or use an indexer
502
+ logger.info(`[AerodromeLpService] getAllPositions not fully implemented - need pool tracking`);
503
+ return [];
504
+ }
505
+
506
+ async getMarketData(poolAddresses: Address[]): Promise<Record<string, Partial<EvmPoolInfo>>> {
507
+ const result: Record<string, Partial<EvmPoolInfo>> = {};
508
+
509
+ for (const address of poolAddresses) {
510
+ try {
511
+ const poolInfo = await this.getPoolInfo(address);
512
+ if (poolInfo) {
513
+ result[address] = poolInfo;
514
+ }
515
+ } catch {
516
+ // Pool not found
517
+ }
518
+ }
519
+
520
+ return result;
521
+ }
522
+
523
+ private async approveToken(
524
+ privateKey: `0x${string}`,
525
+ tokenAddress: Address,
526
+ spenderAddress: Address,
527
+ amount: bigint
528
+ ): Promise<void> {
529
+ const publicClient = this.getPublicClient();
530
+ const walletClient = this.getWalletClient(privateKey);
531
+
532
+ const allowance = await publicClient.readContract({
533
+ address: tokenAddress,
534
+ abi: ERC20_ABI,
535
+ functionName: "allowance",
536
+ args: [walletClient.account?.address, spenderAddress],
537
+ });
538
+
539
+ if ((allowance as bigint) >= amount) {
540
+ return;
541
+ }
542
+
543
+ logger.info(`[AerodromeLpService] Approving ${tokenAddress} for ${spenderAddress}`);
544
+
545
+ const { request } = await publicClient.simulateContract({
546
+ address: tokenAddress,
547
+ abi: ERC20_ABI,
548
+ functionName: "approve",
549
+ args: [spenderAddress, amount],
550
+ account: walletClient.account,
551
+ });
552
+
553
+ const hash = await walletClient.writeContract(request);
554
+ await publicClient.waitForTransactionReceipt({ hash });
555
+
556
+ logger.info(`[AerodromeLpService] Token approved: ${hash}`);
557
+ }
558
+ }