@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,743 @@
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
+ type Chain,
6
+ createPublicClient,
7
+ createWalletClient,
8
+ http,
9
+ maxUint128,
10
+ type PublicClient,
11
+ type WalletClient,
12
+ } from "viem";
13
+ import { privateKeyToAccount } from "viem/accounts";
14
+ import * as viemChains from "viem/chains";
15
+ import type {
16
+ EvmAddLiquidityParams,
17
+ EvmDex,
18
+ EvmPoolInfo,
19
+ EvmPositionDetails,
20
+ EvmRemoveLiquidityParams,
21
+ EvmTransactionResult,
22
+ IEvmLpService,
23
+ } from "../../../../../lp/types.ts";
24
+ import {
25
+ ERC20_ABI,
26
+ PANCAKESWAP_V3_ADDRESSES,
27
+ PANCAKESWAP_V3_FACTORY_ABI,
28
+ PANCAKESWAP_V3_FEE_TIERS,
29
+ PANCAKESWAP_V3_POOL_ABI,
30
+ PANCAKESWAP_V3_POSITION_MANAGER_ABI,
31
+ type PancakeSwapV3FeeTier,
32
+ } from "../types.ts";
33
+
34
+ const SUPPORTED_CHAIN_IDS = [56, 1, 42161, 8453]; // BSC, Ethereum, Arbitrum, Base
35
+
36
+ function getViemChain(chainId: number): Chain {
37
+ const chainMap: Record<number, Chain> = {
38
+ 1: viemChains.mainnet,
39
+ 56: viemChains.bsc,
40
+ 42161: viemChains.arbitrum,
41
+ 8453: viemChains.base,
42
+ };
43
+ const chain = chainMap[chainId];
44
+ if (!chain) {
45
+ throw new Error(`Unsupported chain ID: ${chainId}`);
46
+ }
47
+ return chain;
48
+ }
49
+
50
+ interface PancakeSwapPosition {
51
+ tokenId: bigint;
52
+ nonce: bigint;
53
+ operator: Address;
54
+ token0: Address;
55
+ token1: Address;
56
+ fee: PancakeSwapV3FeeTier;
57
+ tickLower: number;
58
+ tickUpper: number;
59
+ liquidity: bigint;
60
+ feeGrowthInside0LastX128: bigint;
61
+ feeGrowthInside1LastX128: bigint;
62
+ tokensOwed0: bigint;
63
+ tokensOwed1: bigint;
64
+ }
65
+
66
+ export class PancakeSwapV3LpService extends Service implements IEvmLpService {
67
+ public static readonly serviceType = "pancakeswap-v3-lp";
68
+ public readonly capabilityDescription =
69
+ "Provides PancakeSwap V3 liquidity pool management for EVM chains.";
70
+
71
+ private publicClients: Map<number, PublicClient> = new Map();
72
+ private walletClients: Map<number, WalletClient> = new Map();
73
+ private rpcUrls: Map<number, string> = new Map();
74
+
75
+ constructor(runtime?: IAgentRuntime) {
76
+ super(runtime);
77
+ if (runtime) {
78
+ this.initializeRpcUrls();
79
+ }
80
+ }
81
+
82
+ private initializeRpcUrls(): void {
83
+ const rpcSettings: Record<number, string[]> = {
84
+ 1: ["ETHEREUM_RPC_URL", "ETH_RPC_URL", "EVM_PROVIDER_MAINNET"],
85
+ 56: ["BSC_RPC_URL", "BNB_RPC_URL", "EVM_PROVIDER_BSC"],
86
+ 42161: ["ARBITRUM_RPC_URL", "EVM_PROVIDER_ARBITRUM"],
87
+ 8453: ["BASE_RPC_URL", "EVM_PROVIDER_BASE"],
88
+ };
89
+
90
+ for (const [chainId, envKeys] of Object.entries(rpcSettings)) {
91
+ for (const key of envKeys) {
92
+ const rpcUrl = this.runtime.getSetting(key);
93
+ if (rpcUrl && typeof rpcUrl === "string") {
94
+ this.rpcUrls.set(Number(chainId), rpcUrl);
95
+ break;
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ private getPublicClient(chainId: number): PublicClient {
102
+ let client = this.publicClients.get(chainId);
103
+ if (client) return client;
104
+
105
+ const rpcUrl = this.rpcUrls.get(chainId);
106
+ const chain = getViemChain(chainId);
107
+
108
+ client = createPublicClient({
109
+ chain,
110
+ transport: rpcUrl ? http(rpcUrl) : http(),
111
+ });
112
+
113
+ this.publicClients.set(chainId, client);
114
+ return client;
115
+ }
116
+
117
+ private getWalletClient(chainId: number, privateKey: `0x${string}`): WalletClient {
118
+ const cacheKey = chainId;
119
+ let client = this.walletClients.get(cacheKey);
120
+ if (client) return client;
121
+
122
+ const rpcUrl = this.rpcUrls.get(chainId);
123
+ const chain = getViemChain(chainId);
124
+ const account = privateKeyToAccount(privateKey);
125
+
126
+ client = createWalletClient({
127
+ chain,
128
+ transport: rpcUrl ? http(rpcUrl) : http(),
129
+ account,
130
+ });
131
+
132
+ this.walletClients.set(cacheKey, client);
133
+ return client;
134
+ }
135
+
136
+ static async start(runtime: IAgentRuntime): Promise<PancakeSwapV3LpService> {
137
+ const service = new PancakeSwapV3LpService(runtime);
138
+ logger.info("[PancakeSwapV3LpService] started");
139
+ return service;
140
+ }
141
+
142
+ async stop(): Promise<void> {
143
+ this.publicClients.clear();
144
+ this.walletClients.clear();
145
+ logger.info("[PancakeSwapV3LpService] stopped");
146
+ }
147
+
148
+ getDexName(): EvmDex {
149
+ return "pancakeswap";
150
+ }
151
+
152
+ getSupportedChainIds(): number[] {
153
+ return SUPPORTED_CHAIN_IDS.filter((chainId) => PANCAKESWAP_V3_ADDRESSES[chainId] !== undefined);
154
+ }
155
+
156
+ supportsChain(chainId: number): boolean {
157
+ return this.getSupportedChainIds().includes(chainId);
158
+ }
159
+
160
+ async getPools(
161
+ chainId: number,
162
+ tokenA?: Address,
163
+ tokenB?: Address,
164
+ feeTier?: number
165
+ ): Promise<EvmPoolInfo[]> {
166
+ if (!this.supportsChain(chainId)) {
167
+ logger.warn(`[PancakeSwapV3LpService] Chain ${chainId} not supported`);
168
+ return [];
169
+ }
170
+
171
+ const addresses = PANCAKESWAP_V3_ADDRESSES[chainId];
172
+ if (!addresses) return [];
173
+
174
+ const client = this.getPublicClient(chainId);
175
+ const pools: EvmPoolInfo[] = [];
176
+
177
+ if (tokenA && tokenB) {
178
+ const feeTiers = feeTier
179
+ ? [feeTier as PancakeSwapV3FeeTier]
180
+ : Object.values(PANCAKESWAP_V3_FEE_TIERS);
181
+
182
+ for (const fee of feeTiers) {
183
+ try {
184
+ const poolAddress = await client.readContract({
185
+ address: addresses.factory,
186
+ abi: PANCAKESWAP_V3_FACTORY_ABI,
187
+ functionName: "getPool",
188
+ args: [tokenA, tokenB, fee],
189
+ });
190
+
191
+ if (poolAddress && poolAddress !== "0x0000000000000000000000000000000000000000") {
192
+ const poolInfo = await this.getPoolInfo(chainId, poolAddress as Address);
193
+ if (poolInfo) {
194
+ pools.push(poolInfo);
195
+ }
196
+ }
197
+ } catch (_error) {
198
+ logger.debug(
199
+ `[PancakeSwapV3LpService] No pool found for ${tokenA}/${tokenB} at fee ${fee}`
200
+ );
201
+ }
202
+ }
203
+ }
204
+
205
+ return pools;
206
+ }
207
+
208
+ private async getPoolInfo(chainId: number, poolAddress: Address): Promise<EvmPoolInfo | null> {
209
+ const client = this.getPublicClient(chainId);
210
+ const chain = getViemChain(chainId);
211
+
212
+ try {
213
+ const [token0, token1, fee, tickSpacing, _liquidity, slot0] = await Promise.all([
214
+ client.readContract({
215
+ address: poolAddress,
216
+ abi: PANCAKESWAP_V3_POOL_ABI,
217
+ functionName: "token0",
218
+ }),
219
+ client.readContract({
220
+ address: poolAddress,
221
+ abi: PANCAKESWAP_V3_POOL_ABI,
222
+ functionName: "token1",
223
+ }),
224
+ client.readContract({
225
+ address: poolAddress,
226
+ abi: PANCAKESWAP_V3_POOL_ABI,
227
+ functionName: "fee",
228
+ }),
229
+ client.readContract({
230
+ address: poolAddress,
231
+ abi: PANCAKESWAP_V3_POOL_ABI,
232
+ functionName: "tickSpacing",
233
+ }),
234
+ client.readContract({
235
+ address: poolAddress,
236
+ abi: PANCAKESWAP_V3_POOL_ABI,
237
+ functionName: "liquidity",
238
+ }),
239
+ client.readContract({
240
+ address: poolAddress,
241
+ abi: PANCAKESWAP_V3_POOL_ABI,
242
+ functionName: "slot0",
243
+ }),
244
+ ]);
245
+
246
+ const [symbol0, decimals0, symbol1, decimals1] = await Promise.all([
247
+ client
248
+ .readContract({
249
+ address: token0 as Address,
250
+ abi: ERC20_ABI,
251
+ functionName: "symbol",
252
+ })
253
+ .catch(() => "UNKNOWN"),
254
+ client
255
+ .readContract({
256
+ address: token0 as Address,
257
+ abi: ERC20_ABI,
258
+ functionName: "decimals",
259
+ })
260
+ .catch(() => 18),
261
+ client
262
+ .readContract({
263
+ address: token1 as Address,
264
+ abi: ERC20_ABI,
265
+ functionName: "symbol",
266
+ })
267
+ .catch(() => "UNKNOWN"),
268
+ client
269
+ .readContract({
270
+ address: token1 as Address,
271
+ abi: ERC20_ABI,
272
+ functionName: "decimals",
273
+ })
274
+ .catch(() => 18),
275
+ ]);
276
+
277
+ const poolInfo: EvmPoolInfo = {
278
+ id: poolAddress,
279
+ dex: "pancakeswap",
280
+ chainId,
281
+ chainName: chain.name,
282
+ poolAddress,
283
+ tokenA: {
284
+ address: token0 as Address,
285
+ symbol: symbol0 as string,
286
+ decimals: Number(decimals0),
287
+ },
288
+ tokenB: {
289
+ address: token1 as Address,
290
+ symbol: symbol1 as string,
291
+ decimals: Number(decimals1),
292
+ },
293
+ feeTier: Number(fee),
294
+ tickSpacing: Number(tickSpacing),
295
+ currentTick: Number(slot0[1]),
296
+ sqrtPriceX96: slot0[0] as bigint,
297
+ fee: Number(fee) / 1_000_000,
298
+ displayName: `${symbol0}/${symbol1} (${Number(fee) / 10000}%)`,
299
+ };
300
+
301
+ return poolInfo;
302
+ } catch (error) {
303
+ logger.error(`[PancakeSwapV3LpService] Error fetching pool info for ${poolAddress}:`, error);
304
+ return null;
305
+ }
306
+ }
307
+
308
+ async addLiquidity(params: EvmAddLiquidityParams): Promise<EvmTransactionResult> {
309
+ if (!this.supportsChain(params.chainId)) {
310
+ return { success: false, error: `Chain ${params.chainId} not supported` };
311
+ }
312
+
313
+ const addresses = PANCAKESWAP_V3_ADDRESSES[params.chainId];
314
+ if (!addresses) {
315
+ return {
316
+ success: false,
317
+ error: "PancakeSwap V3 not deployed on this chain",
318
+ };
319
+ }
320
+
321
+ try {
322
+ const publicClient = this.getPublicClient(params.chainId);
323
+ const walletClient = this.getWalletClient(params.chainId, params.wallet.privateKey);
324
+
325
+ const poolInfo = await this.getPoolInfo(params.chainId, params.poolAddress);
326
+ if (!poolInfo) {
327
+ return { success: false, error: "Pool not found" };
328
+ }
329
+
330
+ const slippageMultiplier = BigInt(10000 - params.slippageBps);
331
+ const amount0Min = (params.tokenAAmount * slippageMultiplier) / 10000n;
332
+ const amount1Min = ((params.tokenBAmount ?? 0n) * slippageMultiplier) / 10000n;
333
+
334
+ // Approve tokens
335
+ await this.approveToken(
336
+ params.chainId,
337
+ params.wallet.privateKey,
338
+ poolInfo.tokenA.address,
339
+ addresses.nonfungiblePositionManager,
340
+ params.tokenAAmount
341
+ );
342
+
343
+ if (params.tokenBAmount && params.tokenBAmount > 0n) {
344
+ await this.approveToken(
345
+ params.chainId,
346
+ params.wallet.privateKey,
347
+ poolInfo.tokenB.address,
348
+ addresses.nonfungiblePositionManager,
349
+ params.tokenBAmount
350
+ );
351
+ }
352
+
353
+ const deadline = params.deadline ?? BigInt(Math.floor(Date.now() / 1000) + 1800);
354
+ const tickLower = params.tickLower ?? poolInfo.currentTick! - 1000;
355
+ const tickUpper = params.tickUpper ?? poolInfo.currentTick! + 1000;
356
+ const tickSpacing = poolInfo.tickSpacing ?? 50;
357
+ const alignedTickLower = Math.floor(tickLower / tickSpacing) * tickSpacing;
358
+ const alignedTickUpper = Math.ceil(tickUpper / tickSpacing) * tickSpacing;
359
+
360
+ const mintParams = {
361
+ token0: poolInfo.tokenA.address,
362
+ token1: poolInfo.tokenB.address,
363
+ fee: poolInfo.feeTier!,
364
+ tickLower: alignedTickLower,
365
+ tickUpper: alignedTickUpper,
366
+ amount0Desired: params.tokenAAmount,
367
+ amount1Desired: params.tokenBAmount ?? 0n,
368
+ amount0Min,
369
+ amount1Min,
370
+ recipient: params.wallet.address,
371
+ deadline,
372
+ };
373
+
374
+ const { request } = await publicClient.simulateContract({
375
+ address: addresses.nonfungiblePositionManager,
376
+ abi: PANCAKESWAP_V3_POSITION_MANAGER_ABI,
377
+ functionName: "mint",
378
+ args: [mintParams],
379
+ account: walletClient.account,
380
+ });
381
+
382
+ const hash = await walletClient.writeContract(request);
383
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
384
+
385
+ return {
386
+ success: receipt.status === "success",
387
+ transactionId: hash,
388
+ hash,
389
+ chainId: params.chainId,
390
+ blockNumber: receipt.blockNumber,
391
+ gasUsed: receipt.gasUsed,
392
+ data: {
393
+ poolAddress: params.poolAddress,
394
+ tickLower: alignedTickLower,
395
+ tickUpper: alignedTickUpper,
396
+ },
397
+ };
398
+ } catch (error) {
399
+ logger.error("[PancakeSwapV3LpService] Error adding liquidity:", error);
400
+ return {
401
+ success: false,
402
+ error: error instanceof Error ? error.message : "Unknown error adding liquidity",
403
+ };
404
+ }
405
+ }
406
+
407
+ async removeLiquidity(params: EvmRemoveLiquidityParams): Promise<EvmTransactionResult> {
408
+ if (!this.supportsChain(params.chainId)) {
409
+ return { success: false, error: `Chain ${params.chainId} not supported` };
410
+ }
411
+
412
+ const addresses = PANCAKESWAP_V3_ADDRESSES[params.chainId];
413
+ if (!addresses) {
414
+ return {
415
+ success: false,
416
+ error: "PancakeSwap V3 not deployed on this chain",
417
+ };
418
+ }
419
+
420
+ if (!params.tokenId) {
421
+ return {
422
+ success: false,
423
+ error: "Position token ID required for PancakeSwap V3",
424
+ };
425
+ }
426
+
427
+ try {
428
+ const publicClient = this.getPublicClient(params.chainId);
429
+ const walletClient = this.getWalletClient(params.chainId, params.wallet.privateKey);
430
+
431
+ const position = await this.getPositionFromContract(params.chainId, params.tokenId);
432
+ if (!position) {
433
+ return { success: false, error: "Position not found" };
434
+ }
435
+
436
+ let liquidityToRemove = position.liquidity;
437
+ if (params.percentageToRemove && params.percentageToRemove < 100) {
438
+ liquidityToRemove = (position.liquidity * BigInt(params.percentageToRemove)) / 100n;
439
+ }
440
+
441
+ const deadline = params.deadline ?? BigInt(Math.floor(Date.now() / 1000) + 1800);
442
+
443
+ const decreaseParams = {
444
+ tokenId: params.tokenId,
445
+ liquidity: liquidityToRemove,
446
+ amount0Min: 0n,
447
+ amount1Min: 0n,
448
+ deadline,
449
+ };
450
+
451
+ const { request: decreaseRequest } = await publicClient.simulateContract({
452
+ address: addresses.nonfungiblePositionManager,
453
+ abi: PANCAKESWAP_V3_POSITION_MANAGER_ABI,
454
+ functionName: "decreaseLiquidity",
455
+ args: [decreaseParams],
456
+ account: walletClient.account,
457
+ });
458
+
459
+ const decreaseHash = await walletClient.writeContract(decreaseRequest);
460
+ await publicClient.waitForTransactionReceipt({ hash: decreaseHash });
461
+
462
+ const collectParams = {
463
+ tokenId: params.tokenId,
464
+ recipient: params.wallet.address,
465
+ amount0Max: maxUint128,
466
+ amount1Max: maxUint128,
467
+ };
468
+
469
+ const { request: collectRequest } = await publicClient.simulateContract({
470
+ address: addresses.nonfungiblePositionManager,
471
+ abi: PANCAKESWAP_V3_POSITION_MANAGER_ABI,
472
+ functionName: "collect",
473
+ args: [collectParams],
474
+ account: walletClient.account,
475
+ });
476
+
477
+ const collectHash = await walletClient.writeContract(collectRequest);
478
+ const receipt = await publicClient.waitForTransactionReceipt({
479
+ hash: collectHash,
480
+ });
481
+
482
+ if (params.percentageToRemove === 100 || !params.percentageToRemove) {
483
+ try {
484
+ const { request: burnRequest } = await publicClient.simulateContract({
485
+ address: addresses.nonfungiblePositionManager,
486
+ abi: PANCAKESWAP_V3_POSITION_MANAGER_ABI,
487
+ functionName: "burn",
488
+ args: [params.tokenId],
489
+ account: walletClient.account,
490
+ });
491
+ await walletClient.writeContract(burnRequest);
492
+ } catch (burnError) {
493
+ logger.debug("[PancakeSwapV3LpService] Could not burn position NFT:", burnError);
494
+ }
495
+ }
496
+
497
+ return {
498
+ success: receipt.status === "success",
499
+ transactionId: collectHash,
500
+ hash: collectHash,
501
+ chainId: params.chainId,
502
+ blockNumber: receipt.blockNumber,
503
+ gasUsed: receipt.gasUsed,
504
+ };
505
+ } catch (error) {
506
+ logger.error("[PancakeSwapV3LpService] Error removing liquidity:", error);
507
+ return {
508
+ success: false,
509
+ error: error instanceof Error ? error.message : "Unknown error removing liquidity",
510
+ };
511
+ }
512
+ }
513
+
514
+ private async getPositionFromContract(
515
+ chainId: number,
516
+ tokenId: bigint
517
+ ): Promise<PancakeSwapPosition | null> {
518
+ const addresses = PANCAKESWAP_V3_ADDRESSES[chainId];
519
+ if (!addresses) return null;
520
+
521
+ const client = this.getPublicClient(chainId);
522
+
523
+ try {
524
+ const result = await client.readContract({
525
+ address: addresses.nonfungiblePositionManager,
526
+ abi: PANCAKESWAP_V3_POSITION_MANAGER_ABI,
527
+ functionName: "positions",
528
+ args: [tokenId],
529
+ });
530
+
531
+ return {
532
+ tokenId,
533
+ nonce: result[0],
534
+ operator: result[1] as Address,
535
+ token0: result[2] as Address,
536
+ token1: result[3] as Address,
537
+ fee: result[4] as PancakeSwapV3FeeTier,
538
+ tickLower: result[5],
539
+ tickUpper: result[6],
540
+ liquidity: result[7],
541
+ feeGrowthInside0LastX128: result[8],
542
+ feeGrowthInside1LastX128: result[9],
543
+ tokensOwed0: result[10],
544
+ tokensOwed1: result[11],
545
+ };
546
+ } catch (error) {
547
+ logger.error(`[PancakeSwapV3LpService] Error fetching position ${tokenId}:`, error);
548
+ return null;
549
+ }
550
+ }
551
+
552
+ async getPositionDetails(
553
+ chainId: number,
554
+ owner: Address,
555
+ poolAddress: Address,
556
+ tokenId?: bigint
557
+ ): Promise<EvmPositionDetails | null> {
558
+ if (!this.supportsChain(chainId)) return null;
559
+
560
+ if (tokenId) {
561
+ const position = await this.getPositionFromContract(chainId, tokenId);
562
+ if (!position) return null;
563
+
564
+ const client = this.getPublicClient(chainId);
565
+
566
+ const [symbol0, decimals0, symbol1, decimals1] = await Promise.all([
567
+ client
568
+ .readContract({
569
+ address: position.token0,
570
+ abi: ERC20_ABI,
571
+ functionName: "symbol",
572
+ })
573
+ .catch(() => "UNKNOWN"),
574
+ client
575
+ .readContract({
576
+ address: position.token0,
577
+ abi: ERC20_ABI,
578
+ functionName: "decimals",
579
+ })
580
+ .catch(() => 18),
581
+ client
582
+ .readContract({
583
+ address: position.token1,
584
+ abi: ERC20_ABI,
585
+ functionName: "symbol",
586
+ })
587
+ .catch(() => "UNKNOWN"),
588
+ client
589
+ .readContract({
590
+ address: position.token1,
591
+ abi: ERC20_ABI,
592
+ functionName: "decimals",
593
+ })
594
+ .catch(() => 18),
595
+ ]);
596
+
597
+ return {
598
+ poolId: poolAddress,
599
+ dex: "pancakeswap",
600
+ chainId,
601
+ owner,
602
+ tokenId: position.tokenId,
603
+ tickLower: position.tickLower,
604
+ tickUpper: position.tickUpper,
605
+ liquidity: position.liquidity,
606
+ lpTokenBalance: {
607
+ address: poolAddress,
608
+ balance: position.liquidity.toString(),
609
+ decimals: 0,
610
+ symbol: `PCS-V3-${symbol0}/${symbol1}`,
611
+ },
612
+ underlyingTokens: [
613
+ {
614
+ address: position.token0,
615
+ balance: position.tokensOwed0.toString(),
616
+ decimals: Number(decimals0),
617
+ symbol: symbol0 as string,
618
+ },
619
+ {
620
+ address: position.token1,
621
+ balance: position.tokensOwed1.toString(),
622
+ decimals: Number(decimals1),
623
+ symbol: symbol1 as string,
624
+ },
625
+ ],
626
+ };
627
+ }
628
+
629
+ const positions = await this.getAllPositions(chainId, owner);
630
+ return positions.find((p) => p.poolId.toLowerCase() === poolAddress.toLowerCase()) ?? null;
631
+ }
632
+
633
+ async getAllPositions(chainId: number, owner: Address): Promise<EvmPositionDetails[]> {
634
+ if (!this.supportsChain(chainId)) return [];
635
+
636
+ const addresses = PANCAKESWAP_V3_ADDRESSES[chainId];
637
+ if (!addresses) return [];
638
+
639
+ const client = this.getPublicClient(chainId);
640
+ const positions: EvmPositionDetails[] = [];
641
+
642
+ try {
643
+ const balance = await client.readContract({
644
+ address: addresses.nonfungiblePositionManager,
645
+ abi: PANCAKESWAP_V3_POSITION_MANAGER_ABI,
646
+ functionName: "balanceOf",
647
+ args: [owner],
648
+ });
649
+
650
+ for (let i = 0; i < Number(balance); i++) {
651
+ const tokenId = await client.readContract({
652
+ address: addresses.nonfungiblePositionManager,
653
+ abi: PANCAKESWAP_V3_POSITION_MANAGER_ABI,
654
+ functionName: "tokenOfOwnerByIndex",
655
+ args: [owner, BigInt(i)],
656
+ });
657
+
658
+ const position = await this.getPositionFromContract(chainId, tokenId as bigint);
659
+ if (position && position.liquidity > 0n) {
660
+ const poolAddress = await client.readContract({
661
+ address: addresses.factory,
662
+ abi: PANCAKESWAP_V3_FACTORY_ABI,
663
+ functionName: "getPool",
664
+ args: [position.token0, position.token1, position.fee],
665
+ });
666
+
667
+ if (poolAddress) {
668
+ const details = await this.getPositionDetails(
669
+ chainId,
670
+ owner,
671
+ poolAddress as Address,
672
+ tokenId as bigint
673
+ );
674
+ if (details) {
675
+ positions.push(details);
676
+ }
677
+ }
678
+ }
679
+ }
680
+ } catch (error) {
681
+ logger.error("[PancakeSwapV3LpService] Error fetching all positions:", error);
682
+ }
683
+
684
+ return positions;
685
+ }
686
+
687
+ async getMarketData(poolAddresses: Address[]): Promise<Record<string, Partial<EvmPoolInfo>>> {
688
+ const result: Record<string, Partial<EvmPoolInfo>> = {};
689
+
690
+ for (const address of poolAddresses) {
691
+ for (const chainId of this.getSupportedChainIds()) {
692
+ try {
693
+ const poolInfo = await this.getPoolInfo(chainId, address);
694
+ if (poolInfo) {
695
+ result[address] = poolInfo;
696
+ break;
697
+ }
698
+ } catch {
699
+ // Pool not on this chain
700
+ }
701
+ }
702
+ }
703
+
704
+ return result;
705
+ }
706
+
707
+ private async approveToken(
708
+ chainId: number,
709
+ privateKey: `0x${string}`,
710
+ tokenAddress: Address,
711
+ spenderAddress: Address,
712
+ amount: bigint
713
+ ): Promise<void> {
714
+ const publicClient = this.getPublicClient(chainId);
715
+ const walletClient = this.getWalletClient(chainId, privateKey);
716
+
717
+ const allowance = await publicClient.readContract({
718
+ address: tokenAddress,
719
+ abi: ERC20_ABI,
720
+ functionName: "allowance",
721
+ args: [walletClient.account?.address, spenderAddress],
722
+ });
723
+
724
+ if ((allowance as bigint) >= amount) {
725
+ return;
726
+ }
727
+
728
+ logger.info(`[PancakeSwapV3LpService] Approving ${tokenAddress} for ${spenderAddress}`);
729
+
730
+ const { request } = await publicClient.simulateContract({
731
+ address: tokenAddress,
732
+ abi: ERC20_ABI,
733
+ functionName: "approve",
734
+ args: [spenderAddress, amount],
735
+ account: walletClient.account,
736
+ });
737
+
738
+ const hash = await walletClient.writeContract(request);
739
+ await publicClient.waitForTransactionReceipt({ hash });
740
+
741
+ logger.info(`[PancakeSwapV3LpService] Token approved: ${hash}`);
742
+ }
743
+ }