@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,163 @@
|
|
|
1
|
+
// @ts-nocheck — legacy code from absorbed plugins (lp-manager, lpinfo, dexscreener, defi-news, birdeye); strict types pending cleanup
|
|
2
|
+
import { type IAgentRuntime, Service } from "@elizaos/core";
|
|
3
|
+
import type {
|
|
4
|
+
IUserLpProfileService,
|
|
5
|
+
TrackedLpPosition,
|
|
6
|
+
TrackedLpPositionInput,
|
|
7
|
+
UserLpProfile,
|
|
8
|
+
} from "../types.ts";
|
|
9
|
+
|
|
10
|
+
export class UserLpProfileService
|
|
11
|
+
extends Service
|
|
12
|
+
implements IUserLpProfileService
|
|
13
|
+
{
|
|
14
|
+
public static readonly serviceType = "UserLpProfileService";
|
|
15
|
+
public readonly capabilityDescription =
|
|
16
|
+
"Manages user profiles and preferences for LP management.";
|
|
17
|
+
private profiles: Map<string, UserLpProfile> = new Map();
|
|
18
|
+
|
|
19
|
+
async start(_runtime?: IAgentRuntime): Promise<void> {
|
|
20
|
+
// No-op
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async stop(_runtime?: IAgentRuntime): Promise<void> {
|
|
24
|
+
// No-op
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Static methods required by ElizaOS Service architecture
|
|
28
|
+
static async start(runtime: IAgentRuntime): Promise<UserLpProfileService> {
|
|
29
|
+
const service = new UserLpProfileService(runtime);
|
|
30
|
+
await service.start(runtime);
|
|
31
|
+
return service;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static async stop(_runtime: IAgentRuntime): Promise<void> {
|
|
35
|
+
// No cleanup needed for static stop
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public async ensureProfile(
|
|
39
|
+
userId: string,
|
|
40
|
+
vaultPublicKey: string,
|
|
41
|
+
encryptedSecretKey: string,
|
|
42
|
+
initialConfig?: Partial<UserLpProfile["autoRebalanceConfig"]>,
|
|
43
|
+
): Promise<UserLpProfile> {
|
|
44
|
+
const profile = await this.getProfile(userId);
|
|
45
|
+
if (profile) {
|
|
46
|
+
// Update existing profile if vault details or config have changed
|
|
47
|
+
const updates: Partial<UserLpProfile> = {};
|
|
48
|
+
if (profile.vaultPublicKey !== vaultPublicKey) {
|
|
49
|
+
updates.vaultPublicKey = vaultPublicKey;
|
|
50
|
+
}
|
|
51
|
+
if (profile.encryptedSecretKey !== encryptedSecretKey) {
|
|
52
|
+
updates.encryptedSecretKey = encryptedSecretKey;
|
|
53
|
+
}
|
|
54
|
+
if (initialConfig) {
|
|
55
|
+
updates.autoRebalanceConfig = {
|
|
56
|
+
...profile.autoRebalanceConfig,
|
|
57
|
+
...initialConfig,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (Object.keys(updates).length > 0) {
|
|
61
|
+
return this.updateProfile(userId, updates);
|
|
62
|
+
}
|
|
63
|
+
return profile;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const newProfile: UserLpProfile = {
|
|
67
|
+
userId,
|
|
68
|
+
vaultPublicKey,
|
|
69
|
+
encryptedSecretKey,
|
|
70
|
+
autoRebalanceConfig: {
|
|
71
|
+
enabled: false,
|
|
72
|
+
minGainThresholdPercent: 1.0,
|
|
73
|
+
maxSlippageBps: 50,
|
|
74
|
+
cycleIntervalHours: 24,
|
|
75
|
+
...initialConfig,
|
|
76
|
+
},
|
|
77
|
+
trackedPositions: [],
|
|
78
|
+
createdAt: new Date().toISOString(),
|
|
79
|
+
updatedAt: new Date().toISOString(),
|
|
80
|
+
version: 1,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
this.profiles.set(userId, newProfile);
|
|
84
|
+
return newProfile;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public async getProfile(userId: string): Promise<UserLpProfile | null> {
|
|
88
|
+
return this.profiles.get(userId) || null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public async updateProfile(
|
|
92
|
+
userId: string,
|
|
93
|
+
updates: Partial<Omit<UserLpProfile, "userId" | "createdAt" | "version">>,
|
|
94
|
+
): Promise<UserLpProfile> {
|
|
95
|
+
const profile = await this.getProfile(userId);
|
|
96
|
+
if (!profile) {
|
|
97
|
+
throw new Error("User profile not found.");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Handle autoRebalanceConfig merging
|
|
101
|
+
const finalUpdates = { ...updates };
|
|
102
|
+
if (updates.autoRebalanceConfig && profile.autoRebalanceConfig) {
|
|
103
|
+
finalUpdates.autoRebalanceConfig = {
|
|
104
|
+
...profile.autoRebalanceConfig,
|
|
105
|
+
...updates.autoRebalanceConfig,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const updatedProfile: UserLpProfile = {
|
|
110
|
+
...profile,
|
|
111
|
+
...finalUpdates,
|
|
112
|
+
updatedAt: new Date().toISOString(),
|
|
113
|
+
version: (profile.version || 1) + 1,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
this.profiles.set(userId, updatedProfile);
|
|
117
|
+
return updatedProfile;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public async addTrackedPosition(
|
|
121
|
+
userId: string,
|
|
122
|
+
position: TrackedLpPositionInput,
|
|
123
|
+
): Promise<UserLpProfile> {
|
|
124
|
+
const profile = await this.getProfile(userId);
|
|
125
|
+
if (!profile) {
|
|
126
|
+
throw new Error("User profile not found.");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const newPosition: TrackedLpPosition = {
|
|
130
|
+
...position,
|
|
131
|
+
trackedAt: new Date().toISOString(),
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const updatedPositions = [...(profile.trackedPositions || []), newPosition];
|
|
135
|
+
return this.updateProfile(userId, { trackedPositions: updatedPositions });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public async removeTrackedPosition(
|
|
139
|
+
userId: string,
|
|
140
|
+
positionIdentifier: string,
|
|
141
|
+
): Promise<UserLpProfile> {
|
|
142
|
+
const profile = await this.getProfile(userId);
|
|
143
|
+
if (!profile) {
|
|
144
|
+
throw new Error("User profile not found.");
|
|
145
|
+
}
|
|
146
|
+
const updatedPositions = (profile.trackedPositions || []).filter(
|
|
147
|
+
(p) => p.positionIdentifier !== positionIdentifier,
|
|
148
|
+
);
|
|
149
|
+
return this.updateProfile(userId, { trackedPositions: updatedPositions });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public async getTrackedPositions(
|
|
153
|
+
userId: string,
|
|
154
|
+
): Promise<TrackedLpPosition[]> {
|
|
155
|
+
const profile = await this.getProfile(userId);
|
|
156
|
+
return profile?.trackedPositions || [];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async getAllProfilesWithAutoRebalanceEnabled(): Promise<UserLpProfile[]> {
|
|
160
|
+
const allProfiles = Array.from(this.profiles.values());
|
|
161
|
+
return allProfiles.filter((p) => p.autoRebalanceConfig.enabled);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// @ts-nocheck — legacy code from absorbed plugins (lp-manager, lpinfo, dexscreener, defi-news, birdeye); strict types pending cleanup
|
|
2
|
+
import { type IAgentRuntime, Service } from "@elizaos/core";
|
|
3
|
+
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
|
4
|
+
import {
|
|
5
|
+
type Connection,
|
|
6
|
+
Keypair,
|
|
7
|
+
LAMPORTS_PER_SOL,
|
|
8
|
+
PublicKey,
|
|
9
|
+
} from "@solana/web3.js";
|
|
10
|
+
import type { IVaultService, TokenBalance } from "../types.ts";
|
|
11
|
+
import { getConnection } from "../utils/solanaClient.ts";
|
|
12
|
+
|
|
13
|
+
export class VaultService extends Service implements IVaultService {
|
|
14
|
+
public static readonly serviceType = "VaultService";
|
|
15
|
+
public readonly capabilityDescription =
|
|
16
|
+
"Manages secure vaults for user cryptographic keys.";
|
|
17
|
+
|
|
18
|
+
private connection!: Connection;
|
|
19
|
+
// Simple in-memory cache for vault public keys - in production, use proper storage
|
|
20
|
+
private vaultCache: Map<string, string> = new Map();
|
|
21
|
+
|
|
22
|
+
static async start(runtime: IAgentRuntime): Promise<VaultService> {
|
|
23
|
+
const service = new VaultService();
|
|
24
|
+
await service.start(runtime);
|
|
25
|
+
return service;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static async stop(_runtime: IAgentRuntime): Promise<void> {
|
|
29
|
+
// No cleanup needed for static stop
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async start(runtime: IAgentRuntime): Promise<void> {
|
|
33
|
+
// Initialize connection
|
|
34
|
+
this.connection = getConnection(runtime);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async stop(): Promise<void> {
|
|
38
|
+
// No-op
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public async createVault(
|
|
42
|
+
userId: string,
|
|
43
|
+
): Promise<{ publicKey: string; secretKeyEncrypted: string }> {
|
|
44
|
+
const keypair = Keypair.generate();
|
|
45
|
+
const publicKey = keypair.publicKey.toBase58();
|
|
46
|
+
const secretKeyEncrypted = Buffer.from(keypair.secretKey).toString("hex");
|
|
47
|
+
|
|
48
|
+
// Cache the public key
|
|
49
|
+
this.vaultCache.set(userId, publicKey);
|
|
50
|
+
|
|
51
|
+
return { publicKey, secretKeyEncrypted };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public async getVaultKeypair(
|
|
55
|
+
userId: string,
|
|
56
|
+
encryptedSecretKey: string,
|
|
57
|
+
): Promise<Keypair> {
|
|
58
|
+
try {
|
|
59
|
+
const secretKey = Buffer.from(encryptedSecretKey, "hex");
|
|
60
|
+
if (secretKey.length !== 64) {
|
|
61
|
+
throw new Error("Invalid secret key length.");
|
|
62
|
+
}
|
|
63
|
+
return Keypair.fromSecretKey(new Uint8Array(secretKey));
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error(
|
|
66
|
+
`Failed to create Keypair from secret for user ${userId}:`,
|
|
67
|
+
error,
|
|
68
|
+
);
|
|
69
|
+
throw new Error("Could not derive Keypair from the provided secret.");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public async getVaultPublicKey(userId: string): Promise<string | null> {
|
|
74
|
+
// Check cache first
|
|
75
|
+
const cached = this.vaultCache.get(userId);
|
|
76
|
+
if (cached) {
|
|
77
|
+
return cached;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// In a real implementation, this would fetch from persistent storage
|
|
81
|
+
// For now, return null if not in cache
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public async getBalances(publicKey: string): Promise<TokenBalance[]> {
|
|
86
|
+
try {
|
|
87
|
+
const pubKey = new PublicKey(publicKey);
|
|
88
|
+
const balances: TokenBalance[] = [];
|
|
89
|
+
|
|
90
|
+
// Get SOL balance
|
|
91
|
+
const solBalance = await this.connection.getBalance(pubKey);
|
|
92
|
+
balances.push({
|
|
93
|
+
address: "SOL",
|
|
94
|
+
balance: solBalance.toString(),
|
|
95
|
+
decimals: 9,
|
|
96
|
+
uiAmount: solBalance / LAMPORTS_PER_SOL,
|
|
97
|
+
name: "Solana",
|
|
98
|
+
symbol: "SOL",
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Get SPL token accounts
|
|
102
|
+
const tokenAccounts = await this.connection.getParsedTokenAccountsByOwner(
|
|
103
|
+
pubKey,
|
|
104
|
+
{
|
|
105
|
+
programId: TOKEN_PROGRAM_ID,
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
for (const account of tokenAccounts.value) {
|
|
110
|
+
const parsedInfo = account.account.data.parsed.info;
|
|
111
|
+
const tokenBalance = parsedInfo.tokenAmount;
|
|
112
|
+
|
|
113
|
+
balances.push({
|
|
114
|
+
address: parsedInfo.mint,
|
|
115
|
+
balance: tokenBalance.amount,
|
|
116
|
+
decimals: tokenBalance.decimals,
|
|
117
|
+
uiAmount: tokenBalance.uiAmount,
|
|
118
|
+
// Note: symbol and name would need to be fetched from token metadata
|
|
119
|
+
// For now, we'll leave them undefined
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return balances;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error("Error fetching balances:", error);
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Failed to fetch balances for ${publicKey}: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public async exportPrivateKey(
|
|
133
|
+
userId: string,
|
|
134
|
+
encryptedSecretKey: string,
|
|
135
|
+
confirmationToken: string,
|
|
136
|
+
): Promise<string> {
|
|
137
|
+
// In a real implementation, you would verify the confirmationToken
|
|
138
|
+
// For now, we'll do a simple check
|
|
139
|
+
if (!confirmationToken || confirmationToken.length < 6) {
|
|
140
|
+
throw new Error("Invalid confirmation token");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const keypair = await this.getVaultKeypair(userId, encryptedSecretKey);
|
|
145
|
+
// Return base58 encoded private key
|
|
146
|
+
const bs58 = await import("bs58");
|
|
147
|
+
return bs58.default.encode(keypair.secretKey);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error("Error exporting private key:", error);
|
|
150
|
+
throw new Error("Failed to export private key");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
// @ts-nocheck — legacy code from absorbed plugins (lp-manager, lpinfo, dexscreener, defi-news, birdeye); strict types pending cleanup
|
|
2
|
+
import { type IAgentRuntime, Service } from "@elizaos/core";
|
|
3
|
+
import { LAMPORTS_PER_SOL } from "@solana/web3.js"; // For SOL price placeholder
|
|
4
|
+
import type {
|
|
5
|
+
IDexInteractionService,
|
|
6
|
+
IUserLpProfileService,
|
|
7
|
+
LpPositionDetails,
|
|
8
|
+
OptimizationOpportunity,
|
|
9
|
+
PoolInfo,
|
|
10
|
+
TokenBalance,
|
|
11
|
+
} from "../types.ts";
|
|
12
|
+
import type { DexInteractionService } from "./DexInteractionService.ts";
|
|
13
|
+
import type { UserLpProfileService } from "./UserLpProfileService.ts";
|
|
14
|
+
|
|
15
|
+
// Placeholder constants - should be configurable or dynamically fetched
|
|
16
|
+
const _AVG_SOL_TX_FEE_LAMPORTS = BigInt(5000); // Average fee for a simple Solana transaction
|
|
17
|
+
const AVG_SWAP_TX_FEE_LAMPORTS = BigInt(10000); // Potentially higher for swaps involving more accounts/CUs
|
|
18
|
+
const AVG_LP_ADD_REMOVE_TX_FEE_LAMPORTS = BigInt(15000); // LP operations can be more complex
|
|
19
|
+
const PLACEHOLDER_SOL_PRICE_USD = 150; // Placeholder for SOL price in USD
|
|
20
|
+
const _PLACEHOLDER_SWAP_FEE_BPS = 30; // Placeholder for swap fee in basis points (0.3%)
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Interface for the YieldOptimizationService.
|
|
24
|
+
* This service is responsible for fetching data about available LP pools,
|
|
25
|
+
* finding better yield opportunities, and calculating the costs of rebalancing.
|
|
26
|
+
*/
|
|
27
|
+
export interface IYieldOptimizationService extends Service {
|
|
28
|
+
/**
|
|
29
|
+
* Fetches comprehensive data for all relevant pools across all supported DEXs.
|
|
30
|
+
* This data is used as the basis for finding optimization opportunities.
|
|
31
|
+
* @returns A promise that resolves to an array of PoolInfo objects.
|
|
32
|
+
*/
|
|
33
|
+
fetchAllPoolData(): Promise<PoolInfo[]>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Analyzes current LP positions and idle assets to find better yield opportunities.
|
|
37
|
+
* @param userId - The user's ID
|
|
38
|
+
* @param currentPositions - An array of the user's current LpPositionDetails.
|
|
39
|
+
* @param idleAssets - An array of the user's idle TokenBalance that could be deployed.
|
|
40
|
+
* @returns A promise that resolves to an array of OptimizationOpportunity objects.
|
|
41
|
+
*/
|
|
42
|
+
findBestYieldOpportunities(
|
|
43
|
+
userId: string,
|
|
44
|
+
currentPositions: LpPositionDetails[],
|
|
45
|
+
idleAssets: TokenBalance[],
|
|
46
|
+
): Promise<OptimizationOpportunity[]>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Calculates the estimated cost of moving liquidity from one position/pool to another.
|
|
50
|
+
* This includes transaction fees, swap fees (if tokens need to be swapped), and potential slippage.
|
|
51
|
+
* @param fromPosition - The user's current LpPositionDetails (if rebalancing an existing position).
|
|
52
|
+
* @param toPool - The target PoolInfo to move liquidity to.
|
|
53
|
+
* @param solPriceUsd - The current SOL price in USD
|
|
54
|
+
* @param amountToMoveLamports - Optional. The specific amount of LP value (in SOL or stablecoin equivalent) to move.
|
|
55
|
+
* @returns A promise that resolves to an object detailing the costs.
|
|
56
|
+
*/
|
|
57
|
+
calculateRebalanceCost(
|
|
58
|
+
fromPosition: LpPositionDetails | null,
|
|
59
|
+
toPool: PoolInfo,
|
|
60
|
+
solPriceUsd: number,
|
|
61
|
+
amountToMoveLamports?: string,
|
|
62
|
+
underlyingTokensToMove?: TokenBalance[], // Specific tokens being moved, if known (e.g. after withdrawal)
|
|
63
|
+
): Promise<{
|
|
64
|
+
costSolLamports: string;
|
|
65
|
+
costUsd?: number;
|
|
66
|
+
steps: string[];
|
|
67
|
+
error?: string;
|
|
68
|
+
}>;
|
|
69
|
+
|
|
70
|
+
findBestYield(
|
|
71
|
+
userId: string,
|
|
72
|
+
currentTokenA: string,
|
|
73
|
+
currentTokenB: string,
|
|
74
|
+
): Promise<OptimizationOpportunity[]>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export class YieldOptimizationService
|
|
78
|
+
extends Service
|
|
79
|
+
implements IYieldOptimizationService
|
|
80
|
+
{
|
|
81
|
+
public static readonly serviceType = "YieldOptimizationService";
|
|
82
|
+
public readonly capabilityDescription =
|
|
83
|
+
"Finds and evaluates yield optimization opportunities across DEXs.";
|
|
84
|
+
|
|
85
|
+
private dexInteractionService!: IDexInteractionService;
|
|
86
|
+
private userLpProfileService!: IUserLpProfileService;
|
|
87
|
+
|
|
88
|
+
// Static methods required by ElizaOS Service architecture
|
|
89
|
+
static async start(
|
|
90
|
+
runtime: IAgentRuntime,
|
|
91
|
+
): Promise<YieldOptimizationService> {
|
|
92
|
+
const service = new YieldOptimizationService(runtime);
|
|
93
|
+
await service.start(runtime);
|
|
94
|
+
return service;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static async stop(_runtime: IAgentRuntime): Promise<void> {
|
|
98
|
+
// No cleanup needed for static stop
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async start(runtime: IAgentRuntime): Promise<void> {
|
|
102
|
+
const dexInteractionService =
|
|
103
|
+
runtime.getService<DexInteractionService>("dex-interaction");
|
|
104
|
+
const userLpProfileService = runtime.getService<UserLpProfileService>(
|
|
105
|
+
"UserLpProfileService",
|
|
106
|
+
);
|
|
107
|
+
if (!dexInteractionService || !userLpProfileService) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
"Required services for YieldOptimizationService not available.",
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
this.dexInteractionService = dexInteractionService;
|
|
113
|
+
this.userLpProfileService = userLpProfileService;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async stop(): Promise<void> {
|
|
117
|
+
// No-op
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async fetchAllPoolData(): Promise<PoolInfo[]> {
|
|
121
|
+
const pools = await this.dexInteractionService.getPools();
|
|
122
|
+
return pools;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async calculateRebalanceCost(
|
|
126
|
+
fromPositionOrNull: LpPositionDetails | null,
|
|
127
|
+
targetPool: PoolInfo,
|
|
128
|
+
solPriceUsd: number,
|
|
129
|
+
_valueOfLpTokensToMoveLamports?: string,
|
|
130
|
+
underlyingTokensAvailable?: TokenBalance[],
|
|
131
|
+
): Promise<{
|
|
132
|
+
costSolLamports: string;
|
|
133
|
+
costUsd?: number;
|
|
134
|
+
steps: string[];
|
|
135
|
+
error?: string;
|
|
136
|
+
}> {
|
|
137
|
+
const steps: string[] = [];
|
|
138
|
+
let totalEstimatedCostLamports = BigInt(0);
|
|
139
|
+
|
|
140
|
+
if (fromPositionOrNull) {
|
|
141
|
+
steps.push(
|
|
142
|
+
`1. Withdraw from ${fromPositionOrNull.dex} pool: ${fromPositionOrNull.poolId}`,
|
|
143
|
+
);
|
|
144
|
+
totalEstimatedCostLamports += AVG_LP_ADD_REMOVE_TX_FEE_LAMPORTS;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let needsSwap = false;
|
|
148
|
+
if (underlyingTokensAvailable && underlyingTokensAvailable.length === 2) {
|
|
149
|
+
const targetTokenA = targetPool.tokenA.mint;
|
|
150
|
+
const targetTokenB = targetPool.tokenB.mint;
|
|
151
|
+
const hasTokenA = underlyingTokensAvailable.find(
|
|
152
|
+
(t) => t.address === targetTokenA,
|
|
153
|
+
);
|
|
154
|
+
const hasTokenB = underlyingTokensAvailable.find(
|
|
155
|
+
(t) => t.address === targetTokenB,
|
|
156
|
+
);
|
|
157
|
+
if (!hasTokenA || !hasTokenB) {
|
|
158
|
+
needsSwap = true;
|
|
159
|
+
}
|
|
160
|
+
} else if (fromPositionOrNull) {
|
|
161
|
+
const sourceTokens = fromPositionOrNull.underlyingTokens
|
|
162
|
+
.map((t: TokenBalance) => t.address)
|
|
163
|
+
.sort();
|
|
164
|
+
const targetTokens = [
|
|
165
|
+
targetPool.tokenA.mint,
|
|
166
|
+
targetPool.tokenB.mint,
|
|
167
|
+
].sort();
|
|
168
|
+
if (sourceTokens.join(",") !== targetTokens.join(",")) {
|
|
169
|
+
needsSwap = true;
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
needsSwap = true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (needsSwap) {
|
|
176
|
+
steps.push(
|
|
177
|
+
`2. (Potentially) Swap tokens to match ${targetPool.tokenA.symbol || "TokenA"}/${targetPool.tokenB.symbol || "TokenB"}`,
|
|
178
|
+
);
|
|
179
|
+
totalEstimatedCostLamports += AVG_SWAP_TX_FEE_LAMPORTS;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
steps.push(`3. Deposit to ${targetPool.dex} pool: ${targetPool.id}`);
|
|
183
|
+
totalEstimatedCostLamports += AVG_LP_ADD_REMOVE_TX_FEE_LAMPORTS;
|
|
184
|
+
|
|
185
|
+
const costSolLamportsStr = totalEstimatedCostLamports.toString();
|
|
186
|
+
const costUsd =
|
|
187
|
+
(Number(totalEstimatedCostLamports) / Number(LAMPORTS_PER_SOL)) *
|
|
188
|
+
solPriceUsd;
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
costSolLamports: costSolLamportsStr,
|
|
192
|
+
costUsd: parseFloat(costUsd.toFixed(2)),
|
|
193
|
+
steps,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async findBestYieldOpportunities(
|
|
198
|
+
userId: string,
|
|
199
|
+
currentPositions: LpPositionDetails[],
|
|
200
|
+
idleAssets: TokenBalance[],
|
|
201
|
+
): Promise<OptimizationOpportunity[]> {
|
|
202
|
+
const userProfile = await this.userLpProfileService.getProfile(userId);
|
|
203
|
+
if (!userProfile) {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
const allAvailablePools = await this.fetchAllPoolData();
|
|
207
|
+
const opportunities: OptimizationOpportunity[] = [];
|
|
208
|
+
const solPriceUsdForCosting = PLACEHOLDER_SOL_PRICE_USD;
|
|
209
|
+
|
|
210
|
+
for (const position of currentPositions) {
|
|
211
|
+
const { underlyingTokens } = position;
|
|
212
|
+
const currentYield =
|
|
213
|
+
(position.metadata?.apy as number) ||
|
|
214
|
+
(position.metadata?.apr as number) ||
|
|
215
|
+
0;
|
|
216
|
+
|
|
217
|
+
for (const targetPool of allAvailablePools) {
|
|
218
|
+
if (
|
|
219
|
+
targetPool.id === position.poolId &&
|
|
220
|
+
targetPool.dex === position.dex
|
|
221
|
+
)
|
|
222
|
+
continue;
|
|
223
|
+
|
|
224
|
+
const sourceMints = underlyingTokens.map(
|
|
225
|
+
(t: TokenBalance) => t.address,
|
|
226
|
+
);
|
|
227
|
+
const targetMints = [targetPool.tokenA.mint, targetPool.tokenB.mint];
|
|
228
|
+
const canPotentiallyFormPair = sourceMints.some((sm: string) =>
|
|
229
|
+
targetMints.includes(sm),
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
if (canPotentiallyFormPair) {
|
|
233
|
+
const estimatedNewYield = targetPool.apy || targetPool.apr || 0;
|
|
234
|
+
if (estimatedNewYield > currentYield) {
|
|
235
|
+
const costDetails = await this.calculateRebalanceCost(
|
|
236
|
+
position,
|
|
237
|
+
targetPool,
|
|
238
|
+
solPriceUsdForCosting,
|
|
239
|
+
undefined,
|
|
240
|
+
underlyingTokens,
|
|
241
|
+
);
|
|
242
|
+
const positionValueUsd = position.valueUsd || 1;
|
|
243
|
+
const costInYieldTerms =
|
|
244
|
+
(costDetails.costUsd || 0) / positionValueUsd;
|
|
245
|
+
|
|
246
|
+
const netGainPercent =
|
|
247
|
+
(estimatedNewYield - currentYield - costInYieldTerms) * 100;
|
|
248
|
+
|
|
249
|
+
if (
|
|
250
|
+
netGainPercent >
|
|
251
|
+
userProfile.autoRebalanceConfig.minGainThresholdPercent
|
|
252
|
+
) {
|
|
253
|
+
opportunities.push({
|
|
254
|
+
sourcePosition: position,
|
|
255
|
+
sourcePool: {
|
|
256
|
+
id: position.poolId,
|
|
257
|
+
dex: position.dex,
|
|
258
|
+
tokenA: {
|
|
259
|
+
mint: position.underlyingTokens[0].address,
|
|
260
|
+
symbol: position.underlyingTokens[0].symbol,
|
|
261
|
+
decimals: position.underlyingTokens[0].decimals,
|
|
262
|
+
},
|
|
263
|
+
tokenB: {
|
|
264
|
+
mint: position.underlyingTokens[1].address,
|
|
265
|
+
symbol: position.underlyingTokens[1].symbol,
|
|
266
|
+
decimals: position.underlyingTokens[1].decimals,
|
|
267
|
+
},
|
|
268
|
+
apr: currentYield,
|
|
269
|
+
},
|
|
270
|
+
targetPool,
|
|
271
|
+
estimatedNewYield: estimatedNewYield * 100,
|
|
272
|
+
currentYield: currentYield * 100,
|
|
273
|
+
estimatedCostToMoveLamports: costDetails.costSolLamports,
|
|
274
|
+
estimatedCostToMoveUsd: costDetails.costUsd,
|
|
275
|
+
netGainPercent: parseFloat(netGainPercent.toFixed(2)),
|
|
276
|
+
reason: `Potential ${netGainPercent.toFixed(2)}% net APY increase. Current: ${(currentYield * 100).toFixed(2)}%, New: ${(estimatedNewYield * 100).toFixed(2)}%`,
|
|
277
|
+
actions: costDetails.steps,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (idleAssets.length > 0) {
|
|
286
|
+
for (const pool of allAvailablePools) {
|
|
287
|
+
if (
|
|
288
|
+
(pool.apr || 0) <=
|
|
289
|
+
userProfile.autoRebalanceConfig.minGainThresholdPercent / 100
|
|
290
|
+
)
|
|
291
|
+
continue;
|
|
292
|
+
const canFormFromIdle = idleAssets.some(
|
|
293
|
+
(asset) =>
|
|
294
|
+
asset.address === pool.tokenA.mint ||
|
|
295
|
+
asset.address === pool.tokenB.mint,
|
|
296
|
+
);
|
|
297
|
+
if (canFormFromIdle) {
|
|
298
|
+
const costDetails = await this.calculateRebalanceCost(
|
|
299
|
+
null,
|
|
300
|
+
pool,
|
|
301
|
+
solPriceUsdForCosting,
|
|
302
|
+
undefined,
|
|
303
|
+
idleAssets,
|
|
304
|
+
);
|
|
305
|
+
if (
|
|
306
|
+
(pool.apr || 0) * 100 >
|
|
307
|
+
userProfile.autoRebalanceConfig.minGainThresholdPercent
|
|
308
|
+
) {
|
|
309
|
+
opportunities.push({
|
|
310
|
+
targetPool: pool,
|
|
311
|
+
estimatedNewYield: (pool.apr || 0) * 100,
|
|
312
|
+
currentYield: 0,
|
|
313
|
+
estimatedCostToMoveLamports: costDetails.costSolLamports,
|
|
314
|
+
estimatedCostToMoveUsd: costDetails.costUsd,
|
|
315
|
+
netGainPercent:
|
|
316
|
+
(pool.apr || 0) * 100 -
|
|
317
|
+
((costDetails.costUsd || 0) / 1000) * 100,
|
|
318
|
+
reason: `Deploy idle assets to ${pool.displayName || pool.id} with APR of ${((pool.apr || 0) * 100).toFixed(2)}%`,
|
|
319
|
+
actions: costDetails.steps.filter(
|
|
320
|
+
(s) => !s.toLowerCase().includes("withdraw"),
|
|
321
|
+
),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
opportunities.sort(
|
|
329
|
+
(a, b) => (b.netGainPercent || 0) - (a.netGainPercent || 0),
|
|
330
|
+
);
|
|
331
|
+
return opportunities;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
public async findBestYield(
|
|
335
|
+
userId: string,
|
|
336
|
+
currentTokenA: string,
|
|
337
|
+
currentTokenB: string,
|
|
338
|
+
): Promise<OptimizationOpportunity[]> {
|
|
339
|
+
console.log(
|
|
340
|
+
`Finding best yield for user ${userId} with tokens ${currentTokenA} and ${currentTokenB}`,
|
|
341
|
+
);
|
|
342
|
+
return [];
|
|
343
|
+
}
|
|
344
|
+
}
|