@elizaos/plugin-wallet 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +64 -0
- package/auto-enable.ts +76 -0
- package/dist/LpManagementService-BWrQ5-cO.mjs +353 -0
- package/dist/MockLpService-D_Apn4Fd.mjs +99 -0
- package/dist/aerodrome-CfnESC32.mjs +890 -0
- package/dist/chunk-hT5z_Zn9.mjs +35 -0
- package/dist/index.d.mts +34727 -0
- package/dist/index.mjs +21590 -0
- package/dist/lib/server-wallet-trade.d.mts +34 -0
- package/dist/lib/server-wallet-trade.mjs +306 -0
- package/dist/meteora-BPX39hZo.mjs +22640 -0
- package/dist/orca-Bybp1HXO.mjs +249 -0
- package/dist/pancakeswp-CkEXlXti.mjs +604 -0
- package/dist/plugin-ZO_MTyd0.mjs +529 -0
- package/dist/raydium-rfaM9yEf.mjs +539 -0
- package/dist/sdk/index.d.mts +32492 -0
- package/dist/sdk/index.mjs +6415 -0
- package/dist/types-D5252NZk.mjs +487 -0
- package/dist/uniswap-CReXgXVN.mjs +573 -0
- package/dist/wallet-action.d.mts +6 -0
- package/dist/wallet-action.mjs +820 -0
- package/package.json +152 -0
- package/src/actions/failure-codes.ts +79 -0
- package/src/actions/index.ts +1 -0
- package/src/analytics/birdeye/actions/wallet-search-address.ts +9 -0
- package/src/analytics/birdeye/birdeye-task.ts +175 -0
- package/src/analytics/birdeye/birdeye.ts +813 -0
- package/src/analytics/birdeye/constants.ts +74 -0
- package/src/analytics/birdeye/providers/agent-portfolio-provider.ts +18 -0
- package/src/analytics/birdeye/providers/market.ts +227 -0
- package/src/analytics/birdeye/providers/portfolio-factory.test.ts +138 -0
- package/src/analytics/birdeye/providers/portfolio-factory.ts +252 -0
- package/src/analytics/birdeye/providers/trending.ts +365 -0
- package/src/analytics/birdeye/providers/wallet.ts +14 -0
- package/src/analytics/birdeye/search-category.test.ts +207 -0
- package/src/analytics/birdeye/search-category.ts +506 -0
- package/src/analytics/birdeye/service.ts +992 -0
- package/src/analytics/birdeye/tasks/birdeye.ts +232 -0
- package/src/analytics/birdeye/types/api/common.ts +305 -0
- package/src/analytics/birdeye/types/api/defi.ts +220 -0
- package/src/analytics/birdeye/types/api/pair.ts +200 -0
- package/src/analytics/birdeye/types/api/search.ts +86 -0
- package/src/analytics/birdeye/types/api/token.ts +635 -0
- package/src/analytics/birdeye/types/api/trader.ts +76 -0
- package/src/analytics/birdeye/types/api/wallet.ts +181 -0
- package/src/analytics/birdeye/types/shared.ts +106 -0
- package/src/analytics/birdeye/utils.ts +700 -0
- package/src/analytics/dexscreener/errors.ts +28 -0
- package/src/analytics/dexscreener/index.ts +3 -0
- package/src/analytics/dexscreener/search-category.test.ts +49 -0
- package/src/analytics/dexscreener/search-category.ts +42 -0
- package/src/analytics/dexscreener/service.ts +595 -0
- package/src/analytics/dexscreener/types.ts +128 -0
- package/src/analytics/lpinfo/index.d.ts +7 -0
- package/src/analytics/lpinfo/index.ts +52 -0
- package/src/analytics/lpinfo/kamino/README.md +102 -0
- package/src/analytics/lpinfo/kamino/index.ts +24 -0
- package/src/analytics/lpinfo/kamino/providers/kaminoLiquidityProvider.ts +422 -0
- package/src/analytics/lpinfo/kamino/providers/kaminoPoolProvider.ts +365 -0
- package/src/analytics/lpinfo/kamino/providers/kaminoProvider.ts +496 -0
- package/src/analytics/lpinfo/kamino/services/kaminoLiquidityService.ts +1123 -0
- package/src/analytics/lpinfo/kamino/services/kaminoService.ts +758 -0
- package/src/analytics/lpinfo/steer/README.md +169 -0
- package/src/analytics/lpinfo/steer/index.ts +23 -0
- package/src/analytics/lpinfo/steer/providers/steerLiquidityProvider.ts +544 -0
- package/src/analytics/lpinfo/steer/services/steerLiquidityService.ts +1690 -0
- package/src/analytics/lpinfo/steer/steer-display-types.ts +99 -0
- package/src/analytics/news/index.ts +52 -0
- package/src/analytics/news/interfaces/types.ts +222 -0
- package/src/analytics/news/providers/defiNewsProvider.ts +734 -0
- package/src/analytics/news/services/newsDataService.ts +332 -0
- package/src/analytics/news/utils/formatters.ts +151 -0
- package/src/analytics/token-info/action.ts +240 -0
- package/src/analytics/token-info/index.ts +3 -0
- package/src/analytics/token-info/params.ts +215 -0
- package/src/analytics/token-info/providers.ts +681 -0
- package/src/analytics/token-info/service.ts +168 -0
- package/src/analytics/token-info/types.ts +74 -0
- package/src/audit/audit-log.ts +45 -0
- package/src/browser-shim/build-shim.ts +123 -0
- package/src/browser-shim/index.ts +5 -0
- package/src/browser-shim/shim.template.js +563 -0
- package/src/chains/evm/.github/workflows/npm-deploy.yml +112 -0
- package/src/chains/evm/LICENSE +21 -0
- package/src/chains/evm/README.md +106 -0
- package/src/chains/evm/actions/helpers.ts +147 -0
- package/src/chains/evm/actions/swap.ts +839 -0
- package/src/chains/evm/actions/transfer.ts +254 -0
- package/src/chains/evm/biome.json +61 -0
- package/src/chains/evm/bridge-router.ts +660 -0
- package/src/chains/evm/build.ts +89 -0
- package/src/chains/evm/chain-handler.ts +416 -0
- package/src/chains/evm/constants.ts +23 -0
- package/src/chains/evm/contracts/artifacts/OZGovernor.json +1707 -0
- package/src/chains/evm/contracts/artifacts/TimelockController.json +1007 -0
- package/src/chains/evm/contracts/artifacts/VoteToken.json +895 -0
- package/src/chains/evm/dex/aerodrome/index.ts +34 -0
- package/src/chains/evm/dex/aerodrome/services/AerodromeLpService.ts +558 -0
- package/src/chains/evm/dex/aerodrome/types.ts +318 -0
- package/src/chains/evm/dex/pancakeswp/index.ts +35 -0
- package/src/chains/evm/dex/pancakeswp/services/PancakeSwapV3LpService.ts +743 -0
- package/src/chains/evm/dex/pancakeswp/types.ts +65 -0
- package/src/chains/evm/dex/uniswap/index.ts +35 -0
- package/src/chains/evm/dex/uniswap/services/UniswapV3LpService.ts +759 -0
- package/src/chains/evm/dex/uniswap/types.ts +390 -0
- package/src/chains/evm/generated/specs/spec-helpers.ts +73 -0
- package/src/chains/evm/generated/specs/specs.ts +151 -0
- package/src/chains/evm/gov-router.ts +250 -0
- package/src/chains/evm/index.browser.ts +16 -0
- package/src/chains/evm/index.ts +31 -0
- package/src/chains/evm/prompts.ts +193 -0
- package/src/chains/evm/providers/get-balance.ts +123 -0
- package/src/chains/evm/providers/wallet.ts +715 -0
- package/src/chains/evm/routes/sign.ts +333 -0
- package/src/chains/evm/rpc-providers.ts +410 -0
- package/src/chains/evm/service.ts +140 -0
- package/src/chains/evm/templates/index.ts +10 -0
- package/src/chains/evm/types/index.ts +432 -0
- package/src/chains/evm/vitest.config.ts +18 -0
- package/src/chains/registry.ts +668 -0
- package/src/chains/solana/README.md +367 -0
- package/src/chains/wallet-action.ts +533 -0
- package/src/chains/wallet-router.test.ts +296 -0
- package/src/contracts.ts +65 -0
- package/src/core-augmentation.ts +10 -0
- package/src/index.ts +71 -0
- package/src/lib/server-wallet-trade.ts +192 -0
- package/src/lib/wallet-export-guard.ts +330 -0
- package/src/lp/actions/liquidity.ts +827 -0
- package/src/lp/e2e/real-token-tests.ts +428 -0
- package/src/lp/e2e/scenarios.ts +470 -0
- package/src/lp/e2e/test-utils.ts +145 -0
- package/src/lp/lp-manager-entry.ts +303 -0
- package/src/lp/services/ConcentratedLiquidityService.ts +120 -0
- package/src/lp/services/DexInteractionService.ts +226 -0
- package/src/lp/services/LpManagementService.test.ts +148 -0
- package/src/lp/services/LpManagementService.ts +632 -0
- package/src/lp/services/UserLpProfileService.ts +163 -0
- package/src/lp/services/VaultService.ts +153 -0
- package/src/lp/services/YieldOptimizationService.ts +344 -0
- package/src/lp/services/__tests__/MockLpService.ts +146 -0
- package/src/lp/tasks/LpAutoRebalanceTask.ts +117 -0
- package/src/lp/tasks/__tests__/LpAutoRebalanceTask.test.ts +370 -0
- package/src/lp/types.ts +582 -0
- package/src/lp/utils/solanaClient.ts +143 -0
- package/src/plugin.ts +125 -0
- package/src/policy/policy.ts +19 -0
- package/src/providers/canonical-provider.ts +27 -0
- package/src/providers/unified-wallet-provider.ts +79 -0
- package/src/register-routes.ts +11 -0
- package/src/routes/plugin.ts +47 -0
- package/src/routes/wallet-market-overview-route.ts +869 -0
- package/src/sdk/abi.ts +258 -0
- package/src/sdk/bridge/abis.ts +126 -0
- package/src/sdk/bridge/client.ts +518 -0
- package/src/sdk/bridge/index.ts +56 -0
- package/src/sdk/bridge/solana.ts +604 -0
- package/src/sdk/bridge/types.ts +202 -0
- package/src/sdk/convenience.ts +347 -0
- package/src/sdk/escrow/MutualStakeEscrow.ts +480 -0
- package/src/sdk/escrow/types.ts +64 -0
- package/src/sdk/escrow/verifiers.ts +73 -0
- package/src/sdk/identity/erc8004.ts +692 -0
- package/src/sdk/identity/reputation.ts +449 -0
- package/src/sdk/identity/uaid.ts +497 -0
- package/src/sdk/identity/validation.ts +372 -0
- package/src/sdk/index.ts +763 -0
- package/src/sdk/policy/SpendingPolicy.ts +260 -0
- package/src/sdk/policy/UptoBillingPolicy.ts +320 -0
- package/src/sdk/router/PaymentRouter.ts +215 -0
- package/src/sdk/router/index.ts +8 -0
- package/src/sdk/swap/SwapModule.ts +310 -0
- package/src/sdk/swap/abi.ts +117 -0
- package/src/sdk/swap/index.ts +34 -0
- package/src/sdk/swap/types.ts +135 -0
- package/src/sdk/tokens/decimals.ts +140 -0
- package/src/sdk/tokens/registry.ts +911 -0
- package/src/sdk/tokens/solana.ts +419 -0
- package/src/sdk/tokens/transfers.ts +327 -0
- package/src/sdk/types.ts +158 -0
- package/src/sdk/wallet-core.ts +115 -0
- package/src/sdk/x402/budget.ts +168 -0
- package/src/sdk/x402/chains/abstract/index.ts +280 -0
- package/src/sdk/x402/client.ts +320 -0
- package/src/sdk/x402/index.ts +46 -0
- package/src/sdk/x402/middleware.ts +92 -0
- package/src/sdk/x402/multi-asset.ts +144 -0
- package/src/sdk/x402/types.ts +156 -0
- package/src/services/wallet-backend-service.ts +328 -0
- package/src/types/wallet-router.ts +227 -0
- package/src/utils/intent-trajectory.ts +106 -0
- package/src/wallet/backend.ts +62 -0
- package/src/wallet/errors.ts +49 -0
- package/src/wallet/index.ts +27 -0
- package/src/wallet/local-eoa-backend.ts +201 -0
- package/src/wallet/pending.ts +60 -0
- package/src/wallet/select-backend.ts +47 -0
- package/src/wallet/steward-backend.ts +161 -0
- package/src/wallet-action.ts +1 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// @ts-nocheck — test-only mock for LP registry and E2E setup
|
|
2
|
+
import { type IAgentRuntime, Service } from "@elizaos/core";
|
|
3
|
+
import type {
|
|
4
|
+
AddLiquidityConfig,
|
|
5
|
+
ILpService,
|
|
6
|
+
LpPositionDetails,
|
|
7
|
+
PoolInfo,
|
|
8
|
+
RemoveLiquidityConfig,
|
|
9
|
+
TransactionResult,
|
|
10
|
+
} from "../../types.ts";
|
|
11
|
+
import {
|
|
12
|
+
createSolanaLpProtocolProvider,
|
|
13
|
+
getLpManagementService,
|
|
14
|
+
} from "../LpManagementService.ts";
|
|
15
|
+
|
|
16
|
+
export class MockLpService extends Service implements ILpService {
|
|
17
|
+
public readonly capabilityDescription =
|
|
18
|
+
"Test-only LP service for registry scenarios.";
|
|
19
|
+
private positions = new Map<string, LpPositionDetails[]>();
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
runtime: IAgentRuntime,
|
|
23
|
+
private readonly dexName = "mock-dex",
|
|
24
|
+
) {
|
|
25
|
+
super(runtime);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getDexName(): string {
|
|
29
|
+
return this.dexName;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async getPools(
|
|
33
|
+
tokenAMint?: string,
|
|
34
|
+
tokenBMint?: string,
|
|
35
|
+
): Promise<PoolInfo[]> {
|
|
36
|
+
const pools: PoolInfo[] = [
|
|
37
|
+
{
|
|
38
|
+
id: `${this.dexName}-sol-usdc`,
|
|
39
|
+
displayName: `${this.dexName} SOL/USDC`,
|
|
40
|
+
dex: this.dexName,
|
|
41
|
+
tokenA: {
|
|
42
|
+
mint: "So11111111111111111111111111111111111111112",
|
|
43
|
+
symbol: "SOL",
|
|
44
|
+
decimals: 9,
|
|
45
|
+
},
|
|
46
|
+
tokenB: {
|
|
47
|
+
mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
48
|
+
symbol: "USDC",
|
|
49
|
+
decimals: 6,
|
|
50
|
+
},
|
|
51
|
+
apr: 12,
|
|
52
|
+
tvl: 1000000,
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
if (!tokenAMint && !tokenBMint) return pools;
|
|
57
|
+
return pools.filter((pool) => {
|
|
58
|
+
const mints = [pool.tokenA?.mint, pool.tokenB?.mint];
|
|
59
|
+
return (
|
|
60
|
+
(!tokenAMint || mints.includes(tokenAMint)) &&
|
|
61
|
+
(!tokenBMint || mints.includes(tokenBMint))
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async addLiquidity(config: AddLiquidityConfig): Promise<TransactionResult> {
|
|
67
|
+
const owner = config.userVault?.publicKey?.toBase58?.() || "test-owner";
|
|
68
|
+
const position: LpPositionDetails = {
|
|
69
|
+
poolId: config.poolId,
|
|
70
|
+
dex: this.dexName,
|
|
71
|
+
lpTokenBalance: {
|
|
72
|
+
address: `lp-${config.poolId}`,
|
|
73
|
+
balance: "1000",
|
|
74
|
+
decimals: 6,
|
|
75
|
+
symbol: "LP",
|
|
76
|
+
},
|
|
77
|
+
underlyingTokens: [],
|
|
78
|
+
accruedFees: [],
|
|
79
|
+
rewards: [],
|
|
80
|
+
valueUsd: 100,
|
|
81
|
+
};
|
|
82
|
+
this.positions.set(owner, [...(this.positions.get(owner) || []), position]);
|
|
83
|
+
return {
|
|
84
|
+
success: true,
|
|
85
|
+
transactionId: `mock-open-${this.dexName}`,
|
|
86
|
+
data: { poolId: config.poolId },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async removeLiquidity(
|
|
91
|
+
config: RemoveLiquidityConfig,
|
|
92
|
+
): Promise<TransactionResult> {
|
|
93
|
+
const owner = config.userVault?.publicKey?.toBase58?.() || "test-owner";
|
|
94
|
+
this.positions.set(
|
|
95
|
+
owner,
|
|
96
|
+
(this.positions.get(owner) || []).filter(
|
|
97
|
+
(position) => position.poolId !== config.poolId,
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
transactionId: `mock-close-${this.dexName}`,
|
|
103
|
+
data: { poolId: config.poolId },
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async getLpPositionDetails(
|
|
108
|
+
owner: string,
|
|
109
|
+
poolOrPositionIdentifier: string,
|
|
110
|
+
): Promise<LpPositionDetails | null> {
|
|
111
|
+
return (
|
|
112
|
+
(this.positions.get(owner) || []).find(
|
|
113
|
+
(position) => position.poolId === poolOrPositionIdentifier,
|
|
114
|
+
) || null
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async getMarketDataForPools(
|
|
119
|
+
poolIds: string[],
|
|
120
|
+
): Promise<Record<string, Partial<PoolInfo>>> {
|
|
121
|
+
return Object.fromEntries(
|
|
122
|
+
poolIds.map((poolId) => [poolId, { apr: 12, tvl: 1000000 }]),
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async start(): Promise<void> {}
|
|
127
|
+
async stop(): Promise<void> {}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function registerMockDexServices(
|
|
131
|
+
runtime: IAgentRuntime,
|
|
132
|
+
): Promise<void> {
|
|
133
|
+
const registry = await getLpManagementService(runtime);
|
|
134
|
+
if (!registry) return;
|
|
135
|
+
|
|
136
|
+
for (const dex of ["raydium", "orca", "meteora"]) {
|
|
137
|
+
const service = new MockLpService(runtime, dex);
|
|
138
|
+
registry.registerProtocol(
|
|
139
|
+
createSolanaLpProtocolProvider({
|
|
140
|
+
dex,
|
|
141
|
+
label: dex,
|
|
142
|
+
service,
|
|
143
|
+
}),
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// @ts-nocheck — legacy code from absorbed plugins (lp-manager, lpinfo, dexscreener, defi-news, birdeye); strict types pending cleanup
|
|
2
|
+
import { type IAgentRuntime, logger } from "@elizaos/core";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* LpAutoRebalanceTask - Automated task for rebalancing LP positions
|
|
6
|
+
*
|
|
7
|
+
* This task monitors LP positions and triggers rebalancing when positions
|
|
8
|
+
* drift outside acceptable ranges.
|
|
9
|
+
*/
|
|
10
|
+
export class LpAutoRebalanceTask {
|
|
11
|
+
private runtime: IAgentRuntime | null = null;
|
|
12
|
+
private intervalId: ReturnType<typeof setInterval> | null = null;
|
|
13
|
+
private isRunning = false;
|
|
14
|
+
|
|
15
|
+
// Configuration
|
|
16
|
+
private checkIntervalMs = 60000; // Default: check every minute
|
|
17
|
+
private rebalanceThresholdBps = 500; // Default: 5% drift threshold
|
|
18
|
+
|
|
19
|
+
constructor(config?: {
|
|
20
|
+
checkIntervalMs?: number;
|
|
21
|
+
rebalanceThresholdBps?: number;
|
|
22
|
+
}) {
|
|
23
|
+
if (config?.checkIntervalMs) {
|
|
24
|
+
this.checkIntervalMs = config.checkIntervalMs;
|
|
25
|
+
}
|
|
26
|
+
if (config?.rebalanceThresholdBps) {
|
|
27
|
+
this.rebalanceThresholdBps = config.rebalanceThresholdBps;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Start the auto-rebalance task
|
|
33
|
+
*/
|
|
34
|
+
async start(runtime: IAgentRuntime): Promise<void> {
|
|
35
|
+
if (this.isRunning) {
|
|
36
|
+
logger.warn("[LpAutoRebalanceTask] Task is already running");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.runtime = runtime;
|
|
41
|
+
this.isRunning = true;
|
|
42
|
+
|
|
43
|
+
logger.info(
|
|
44
|
+
`[LpAutoRebalanceTask] Starting with interval ${this.checkIntervalMs}ms and threshold ${this.rebalanceThresholdBps}bps`,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Run initial check
|
|
48
|
+
await this.checkAndRebalance();
|
|
49
|
+
|
|
50
|
+
// Set up periodic checks
|
|
51
|
+
this.intervalId = setInterval(async () => {
|
|
52
|
+
await this.checkAndRebalance();
|
|
53
|
+
}, this.checkIntervalMs);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Stop the auto-rebalance task
|
|
58
|
+
*/
|
|
59
|
+
stop(): void {
|
|
60
|
+
if (this.intervalId) {
|
|
61
|
+
clearInterval(this.intervalId);
|
|
62
|
+
this.intervalId = null;
|
|
63
|
+
}
|
|
64
|
+
this.isRunning = false;
|
|
65
|
+
logger.info("[LpAutoRebalanceTask] Stopped");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Check positions and rebalance if needed
|
|
70
|
+
*/
|
|
71
|
+
private async checkAndRebalance(): Promise<void> {
|
|
72
|
+
if (!this.runtime) {
|
|
73
|
+
logger.error("[LpAutoRebalanceTask] Runtime not initialized");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
logger.debug("[LpAutoRebalanceTask] Checking positions...");
|
|
79
|
+
|
|
80
|
+
// Get DexInteractionService to check positions
|
|
81
|
+
const dexService = this.runtime.getService("dex-interaction");
|
|
82
|
+
if (!dexService) {
|
|
83
|
+
logger.debug(
|
|
84
|
+
"[LpAutoRebalanceTask] DexInteractionService not available",
|
|
85
|
+
);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// The actual rebalancing logic would be implemented here
|
|
90
|
+
// This is a placeholder that can be extended with real DEX integration
|
|
91
|
+
logger.debug("[LpAutoRebalanceTask] Position check complete");
|
|
92
|
+
} catch (error: unknown) {
|
|
93
|
+
logger.error(
|
|
94
|
+
"[LpAutoRebalanceTask] Error checking positions:",
|
|
95
|
+
error instanceof Error ? error.message : String(error),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get current status of the task
|
|
102
|
+
*/
|
|
103
|
+
getStatus(): {
|
|
104
|
+
isRunning: boolean;
|
|
105
|
+
checkIntervalMs: number;
|
|
106
|
+
rebalanceThresholdBps: number;
|
|
107
|
+
} {
|
|
108
|
+
return {
|
|
109
|
+
isRunning: this.isRunning,
|
|
110
|
+
checkIntervalMs: this.checkIntervalMs,
|
|
111
|
+
rebalanceThresholdBps: this.rebalanceThresholdBps,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Export a default instance for convenience
|
|
117
|
+
export const lpAutoRebalanceTask = new LpAutoRebalanceTask();
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
// @ts-nocheck — legacy code from absorbed plugins (lp-manager, lpinfo, dexscreener, defi-news, birdeye); strict types pending cleanup
|
|
2
|
+
/// <reference types="vitest/globals" />
|
|
3
|
+
import type { IAgentRuntime } from "@elizaos/core";
|
|
4
|
+
import { Keypair as SolanaKeypair } from "@solana/web3.js";
|
|
5
|
+
import {
|
|
6
|
+
beforeEach,
|
|
7
|
+
describe,
|
|
8
|
+
expect,
|
|
9
|
+
it,
|
|
10
|
+
type Mock,
|
|
11
|
+
type Mocked,
|
|
12
|
+
vi,
|
|
13
|
+
} from "vitest";
|
|
14
|
+
import type {
|
|
15
|
+
IDexInteractionService,
|
|
16
|
+
IUserLpProfileService,
|
|
17
|
+
IVaultService,
|
|
18
|
+
IYieldOptimizationService,
|
|
19
|
+
LpPositionDetails,
|
|
20
|
+
OptimizationOpportunity,
|
|
21
|
+
PoolInfo,
|
|
22
|
+
TokenBalance,
|
|
23
|
+
TransactionResult,
|
|
24
|
+
UserLpProfile,
|
|
25
|
+
} from "../../types.ts";
|
|
26
|
+
import { LpAutoRebalanceTask } from "../LpAutoRebalanceTask.ts";
|
|
27
|
+
|
|
28
|
+
// Extended transaction results (matching the types in LpAutoRebalanceTask)
|
|
29
|
+
interface RemoveLiquidityResult extends TransactionResult {
|
|
30
|
+
tokensReceived?: TokenBalance[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface AddLiquidityResult extends TransactionResult {
|
|
34
|
+
lpTokensReceived?: TokenBalance;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Create a mock runtime with getService method
|
|
38
|
+
const createMockRuntime = (services: Record<string, any>): IAgentRuntime => {
|
|
39
|
+
return {
|
|
40
|
+
getService: vi.fn((serviceName: string) => services[serviceName]),
|
|
41
|
+
} as any as IAgentRuntime;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const mockUserLpProfileService: Mocked<IUserLpProfileService> = {
|
|
45
|
+
ensureProfile: vi.fn(),
|
|
46
|
+
getProfile: vi.fn(),
|
|
47
|
+
updateProfile: vi.fn(),
|
|
48
|
+
addTrackedPosition: vi.fn(),
|
|
49
|
+
removeTrackedPosition: vi.fn(),
|
|
50
|
+
getTrackedPositions: vi.fn(),
|
|
51
|
+
getAllProfilesWithAutoRebalanceEnabled: vi.fn(),
|
|
52
|
+
start: vi.fn(),
|
|
53
|
+
stop: vi.fn(),
|
|
54
|
+
capabilityDescription: "Mock UserLpProfileService",
|
|
55
|
+
} as any;
|
|
56
|
+
|
|
57
|
+
const mockDexInteractionService: Mocked<IDexInteractionService> = {
|
|
58
|
+
registerDexService: vi.fn(),
|
|
59
|
+
getPools: vi.fn(),
|
|
60
|
+
addLiquidity: vi.fn(),
|
|
61
|
+
removeLiquidity: vi.fn(),
|
|
62
|
+
getLpPosition: vi.fn(),
|
|
63
|
+
getAllUserLpPositions: vi.fn(),
|
|
64
|
+
start: vi.fn(),
|
|
65
|
+
stop: vi.fn(),
|
|
66
|
+
capabilityDescription: "Mock DexInteractionService",
|
|
67
|
+
} as any;
|
|
68
|
+
|
|
69
|
+
const mockYieldOptimizationService: Mocked<IYieldOptimizationService> = {
|
|
70
|
+
fetchAllPoolData: vi.fn(),
|
|
71
|
+
findBestYieldOpportunities: vi.fn(),
|
|
72
|
+
calculateRebalanceCost: vi.fn(),
|
|
73
|
+
findBestYield: vi.fn(),
|
|
74
|
+
start: vi.fn(),
|
|
75
|
+
stop: vi.fn(),
|
|
76
|
+
capabilityDescription: "Mock YieldOptimizationService",
|
|
77
|
+
} as any;
|
|
78
|
+
|
|
79
|
+
const mockVaultService: Mocked<IVaultService> = {
|
|
80
|
+
createVault: vi.fn(),
|
|
81
|
+
getVaultKeypair: vi.fn(),
|
|
82
|
+
getVaultPublicKey: vi.fn(),
|
|
83
|
+
getBalances: vi.fn(),
|
|
84
|
+
exportPrivateKey: vi.fn(),
|
|
85
|
+
start: vi.fn(),
|
|
86
|
+
stop: vi.fn(),
|
|
87
|
+
capabilityDescription: "Mock VaultService",
|
|
88
|
+
} as any;
|
|
89
|
+
|
|
90
|
+
const testUserKeypair = SolanaKeypair.generate();
|
|
91
|
+
|
|
92
|
+
const testUserId1 = "rebalanceUser1";
|
|
93
|
+
const testUserProfile1: UserLpProfile = {
|
|
94
|
+
userId: testUserId1,
|
|
95
|
+
vaultPublicKey: testUserKeypair.publicKey.toBase58(),
|
|
96
|
+
encryptedSecretKey: "key1",
|
|
97
|
+
autoRebalanceConfig: {
|
|
98
|
+
enabled: true,
|
|
99
|
+
minGainThresholdPercent: 1,
|
|
100
|
+
maxSlippageBps: 50,
|
|
101
|
+
preferredDexes: ["meteora"],
|
|
102
|
+
},
|
|
103
|
+
version: 1,
|
|
104
|
+
createdAt: "now",
|
|
105
|
+
updatedAt: "now",
|
|
106
|
+
trackedPositions: [],
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Updated Mock Data with correct property names
|
|
110
|
+
const SOL_MINT = "So11111111111111111111111111111111111111112";
|
|
111
|
+
const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
112
|
+
|
|
113
|
+
const testLpPosition1: LpPositionDetails = {
|
|
114
|
+
poolId: "currentPool1",
|
|
115
|
+
dex: "orca",
|
|
116
|
+
valueUsd: 1000,
|
|
117
|
+
lpTokenBalance: {
|
|
118
|
+
address: "lpMint1",
|
|
119
|
+
balance: "100",
|
|
120
|
+
decimals: 6,
|
|
121
|
+
symbol: "ORCA_LP_SOL_USDC",
|
|
122
|
+
uiAmount: 0.0001,
|
|
123
|
+
},
|
|
124
|
+
underlyingTokens: [
|
|
125
|
+
{
|
|
126
|
+
address: SOL_MINT,
|
|
127
|
+
balance: "10",
|
|
128
|
+
decimals: 9,
|
|
129
|
+
symbol: "SOL",
|
|
130
|
+
uiAmount: 0.00000001,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
address: USDC_MINT,
|
|
134
|
+
balance: "500",
|
|
135
|
+
decimals: 6,
|
|
136
|
+
symbol: "USDC",
|
|
137
|
+
uiAmount: 0.0005,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const targetPool1: PoolInfo = {
|
|
143
|
+
id: "targetPoolGood",
|
|
144
|
+
dex: "meteora",
|
|
145
|
+
tokenA: { mint: SOL_MINT, symbol: "SOL", decimals: 9 },
|
|
146
|
+
tokenB: { mint: USDC_MINT, symbol: "USDC", decimals: 6 },
|
|
147
|
+
apr: 0.25,
|
|
148
|
+
displayName: "Good Yield Pool (SOL/USDC)",
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const goodOpportunity: OptimizationOpportunity = {
|
|
152
|
+
sourcePosition: testLpPosition1,
|
|
153
|
+
targetPool: targetPool1,
|
|
154
|
+
estimatedNewYield: 25,
|
|
155
|
+
currentYield: 10,
|
|
156
|
+
netGainPercent: 5,
|
|
157
|
+
actions: ["Withdraw from orca", "Deposit to meteora"],
|
|
158
|
+
estimatedCostToMoveLamports: "5000",
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
describe("LpAutoRebalanceTask", () => {
|
|
162
|
+
let task: LpAutoRebalanceTask;
|
|
163
|
+
let mockRuntime: IAgentRuntime;
|
|
164
|
+
|
|
165
|
+
beforeEach(async () => {
|
|
166
|
+
vi.clearAllMocks();
|
|
167
|
+
|
|
168
|
+
// Create mock runtime with services
|
|
169
|
+
mockRuntime = createMockRuntime({
|
|
170
|
+
UserLpProfileService: mockUserLpProfileService,
|
|
171
|
+
"dex-interaction": mockDexInteractionService,
|
|
172
|
+
YieldOptimizationService: mockYieldOptimizationService,
|
|
173
|
+
VaultService: mockVaultService,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Create task and initialize it
|
|
177
|
+
task = new LpAutoRebalanceTask();
|
|
178
|
+
await task.onReady(mockRuntime);
|
|
179
|
+
|
|
180
|
+
// Setup default mock behaviors
|
|
181
|
+
(
|
|
182
|
+
mockUserLpProfileService.getAllProfilesWithAutoRebalanceEnabled as Mock
|
|
183
|
+
).mockResolvedValue([testUserProfile1]);
|
|
184
|
+
(mockDexInteractionService.getAllUserLpPositions as Mock).mockResolvedValue(
|
|
185
|
+
[testLpPosition1],
|
|
186
|
+
);
|
|
187
|
+
(
|
|
188
|
+
mockYieldOptimizationService.findBestYieldOpportunities as Mock
|
|
189
|
+
).mockResolvedValue([goodOpportunity]);
|
|
190
|
+
(mockVaultService.getVaultKeypair as Mock).mockImplementation(
|
|
191
|
+
async (userId, secret) => {
|
|
192
|
+
if (
|
|
193
|
+
userId === testUserId1 &&
|
|
194
|
+
secret === testUserProfile1.encryptedSecretKey
|
|
195
|
+
) {
|
|
196
|
+
return testUserKeypair;
|
|
197
|
+
}
|
|
198
|
+
console.error(
|
|
199
|
+
`MockVaultService.getVaultKeypair called with unexpected args: userId=${userId}, secret=${secret}`,
|
|
200
|
+
);
|
|
201
|
+
return null;
|
|
202
|
+
},
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const removeResult: RemoveLiquidityResult = {
|
|
206
|
+
success: true,
|
|
207
|
+
tokensReceived: testLpPosition1.underlyingTokens,
|
|
208
|
+
};
|
|
209
|
+
(mockDexInteractionService.removeLiquidity as Mock).mockResolvedValue(
|
|
210
|
+
removeResult,
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const addResult: AddLiquidityResult = {
|
|
214
|
+
success: true,
|
|
215
|
+
transactionId: "txAddLiq",
|
|
216
|
+
};
|
|
217
|
+
(mockDexInteractionService.addLiquidity as Mock).mockResolvedValue(
|
|
218
|
+
addResult,
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe("execute", () => {
|
|
223
|
+
it("should fetch enabled profiles and process them", async () => {
|
|
224
|
+
await task.execute();
|
|
225
|
+
expect(
|
|
226
|
+
mockUserLpProfileService.getAllProfilesWithAutoRebalanceEnabled,
|
|
227
|
+
).toHaveBeenCalled();
|
|
228
|
+
expect(
|
|
229
|
+
mockYieldOptimizationService.findBestYieldOpportunities,
|
|
230
|
+
).toHaveBeenCalledWith(testUserId1, [testLpPosition1], []);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("should handle errors when processing a user profile", async () => {
|
|
234
|
+
const userProfile2 = { ...testUserProfile1, userId: "rebalanceUser2" };
|
|
235
|
+
(
|
|
236
|
+
mockUserLpProfileService.getAllProfilesWithAutoRebalanceEnabled as Mock
|
|
237
|
+
).mockResolvedValue([testUserProfile1, userProfile2]);
|
|
238
|
+
(mockDexInteractionService.getAllUserLpPositions as Mock)
|
|
239
|
+
.mockRejectedValueOnce(new Error("Failed to get positions for user1"))
|
|
240
|
+
.mockResolvedValueOnce([]); // For user2, no positions
|
|
241
|
+
|
|
242
|
+
await task.execute();
|
|
243
|
+
// Should continue processing other users despite error
|
|
244
|
+
expect(
|
|
245
|
+
mockDexInteractionService.getAllUserLpPositions,
|
|
246
|
+
).toHaveBeenCalledTimes(2);
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe("processUserProfile", () => {
|
|
251
|
+
it("should execute rebalance if a good opportunity is found and meets criteria", async () => {
|
|
252
|
+
// The task should find opportunities and execute rebalance
|
|
253
|
+
await task.execute();
|
|
254
|
+
|
|
255
|
+
expect(
|
|
256
|
+
mockYieldOptimizationService.findBestYieldOpportunities,
|
|
257
|
+
).toHaveBeenCalledWith(testUserProfile1.userId, [testLpPosition1], []);
|
|
258
|
+
expect(mockVaultService.getVaultKeypair).toHaveBeenCalledWith(
|
|
259
|
+
testUserProfile1.userId,
|
|
260
|
+
testUserProfile1.encryptedSecretKey,
|
|
261
|
+
);
|
|
262
|
+
expect(mockDexInteractionService.removeLiquidity).toHaveBeenCalledTimes(
|
|
263
|
+
1,
|
|
264
|
+
);
|
|
265
|
+
expect(mockDexInteractionService.addLiquidity).toHaveBeenCalledTimes(1);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("should handle rebalance execution failures", async () => {
|
|
269
|
+
// Make addLiquidity fail
|
|
270
|
+
const addResult: AddLiquidityResult = {
|
|
271
|
+
success: false,
|
|
272
|
+
error: "DEX add liquidity failed",
|
|
273
|
+
};
|
|
274
|
+
(mockDexInteractionService.addLiquidity as Mock).mockResolvedValue(
|
|
275
|
+
addResult,
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
// Execute should complete without throwing
|
|
279
|
+
await task.execute();
|
|
280
|
+
|
|
281
|
+
// Verify error was handled gracefully
|
|
282
|
+
expect(mockDexInteractionService.removeLiquidity).toHaveBeenCalled();
|
|
283
|
+
expect(mockDexInteractionService.addLiquidity).toHaveBeenCalled();
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("should not execute if opportunity does not meet criteria", async () => {
|
|
287
|
+
// Create opportunity with low net gain
|
|
288
|
+
const lowGainOpp = { ...goodOpportunity, netGainPercent: 0.5 }; // Below 1% threshold
|
|
289
|
+
(
|
|
290
|
+
mockYieldOptimizationService.findBestYieldOpportunities as Mock
|
|
291
|
+
).mockResolvedValue([lowGainOpp]);
|
|
292
|
+
|
|
293
|
+
await task.execute();
|
|
294
|
+
|
|
295
|
+
// Should not execute rebalance
|
|
296
|
+
expect(mockDexInteractionService.removeLiquidity).not.toHaveBeenCalled();
|
|
297
|
+
expect(mockDexInteractionService.addLiquidity).not.toHaveBeenCalled();
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("should not execute if target DEX is not in preferred list", async () => {
|
|
301
|
+
// Create opportunity with non-preferred DEX
|
|
302
|
+
const nonPreferredDexOpp = {
|
|
303
|
+
...goodOpportunity,
|
|
304
|
+
targetPool: { ...targetPool1, dex: "raydium" },
|
|
305
|
+
};
|
|
306
|
+
(
|
|
307
|
+
mockYieldOptimizationService.findBestYieldOpportunities as Mock
|
|
308
|
+
).mockResolvedValue([nonPreferredDexOpp]);
|
|
309
|
+
|
|
310
|
+
await task.execute();
|
|
311
|
+
|
|
312
|
+
expect(mockDexInteractionService.removeLiquidity).not.toHaveBeenCalled();
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
describe("executeRebalance edge cases", () => {
|
|
317
|
+
it("should handle remove liquidity failures", async () => {
|
|
318
|
+
const removeResult: RemoveLiquidityResult = {
|
|
319
|
+
success: false,
|
|
320
|
+
error: "Remove failed",
|
|
321
|
+
};
|
|
322
|
+
(mockDexInteractionService.removeLiquidity as Mock).mockResolvedValue(
|
|
323
|
+
removeResult,
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
await task.execute();
|
|
327
|
+
|
|
328
|
+
// Should not proceed to add liquidity
|
|
329
|
+
expect(mockDexInteractionService.addLiquidity).not.toHaveBeenCalled();
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it("should handle token mismatch scenarios", async () => {
|
|
333
|
+
const mismatchedTokens: TokenBalance[] = [
|
|
334
|
+
{
|
|
335
|
+
address: "otherToken1",
|
|
336
|
+
balance: "1",
|
|
337
|
+
decimals: 6,
|
|
338
|
+
uiAmount: 0.000001,
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
address: "otherToken2",
|
|
342
|
+
balance: "1",
|
|
343
|
+
decimals: 6,
|
|
344
|
+
uiAmount: 0.000001,
|
|
345
|
+
},
|
|
346
|
+
];
|
|
347
|
+
const removeResult: RemoveLiquidityResult = {
|
|
348
|
+
success: true,
|
|
349
|
+
tokensReceived: mismatchedTokens,
|
|
350
|
+
};
|
|
351
|
+
(mockDexInteractionService.removeLiquidity as Mock).mockResolvedValue(
|
|
352
|
+
removeResult,
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
await task.execute();
|
|
356
|
+
|
|
357
|
+
// Should not proceed to add liquidity due to token mismatch
|
|
358
|
+
expect(mockDexInteractionService.addLiquidity).not.toHaveBeenCalled();
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("should handle null vault keypair", async () => {
|
|
362
|
+
(mockVaultService.getVaultKeypair as Mock).mockResolvedValue(null);
|
|
363
|
+
|
|
364
|
+
await task.execute();
|
|
365
|
+
|
|
366
|
+
// Should not proceed with rebalance
|
|
367
|
+
expect(mockDexInteractionService.removeLiquidity).not.toHaveBeenCalled();
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
});
|