1
+ import {
2
+ Action ,
3
+ elizaLogger ,
4
+ generateText ,
5
+ HandlerCallback ,
6
+ IAgentRuntime ,
7
+ Memory ,
8
+ ModelClass ,
9
+ parseJSONObjectFromText ,
10
+ settings ,
11
+ State ,
12
+ } from "@elizaos/core" ;
13
+ import { PublicKey } from "@solana/web3.js" ;
14
+ import { DLMM , StrategyType } from "@meteora-ag/dlmm" ;
15
+ import { sendTransaction } from "../../utils/sendTransaction" ;
16
+ import { loadWallet } from "../../utils/loadWallet" ;
17
+
18
+ interface FetchedPosition {
19
+ poolAddress : string ;
20
+ positionPubKey : string ;
21
+ inRange : boolean ;
22
+ distanceFromActiveBinBps : number ;
23
+ binRange : number ;
24
+ }
25
+
26
+ interface ManagePositionsInput {
27
+ repositionThresholdBps : number ;
28
+ intervalSeconds : number ;
29
+ slippageToleranceBps : number ;
30
+ }
31
+
32
+ export const managePositions : Action = {
33
+ name : 'manage_meteora_positions' ,
34
+ similes : [ "AUTOMATE_METEORA_REBALANCING" , "AUTOMATE_METEORA_POSITIONS" ] ,
35
+ description : "Automatically manage Meteora positions by rebalancing when they drift from active bin" ,
36
+
37
+ validate : async ( runtime : IAgentRuntime , message : Memory ) : Promise < boolean > => {
38
+ const config = await extractAndValidateConfiguration ( message . content . text , runtime ) ;
39
+ return ! ! config ;
40
+ } ,
41
+
42
+ handler : async (
43
+ runtime : IAgentRuntime ,
44
+ message : Memory ,
45
+ state : State ,
46
+ params : { [ key : string ] : unknown } ,
47
+ callback ?: HandlerCallback
48
+ ) => {
49
+ elizaLogger . log ( "Start managing Meteora positions" ) ;
50
+
51
+ const { repositionThresholdBps, slippageToleranceBps } : ManagePositionsInput =
52
+ await extractAndValidateConfiguration ( message . content . text , runtime ) ;
53
+
54
+ const fetchedPositions = await extractFetchedPositions ( state . providers , runtime ) ;
55
+ const { signer : wallet } = await loadWallet ( runtime , true ) ;
56
+
57
+ await handleRepositioning (
58
+ fetchedPositions ,
59
+ repositionThresholdBps ,
60
+ wallet
61
+ ) ;
62
+
63
+ return true ;
64
+ } ,
65
+ examples : [ ]
66
+ } ;
67
+
68
+ async function handleRepositioning (
69
+ fetchedPositions : FetchedPosition [ ] ,
70
+ repositionThresholdBps : number ,
71
+ wallet : any
72
+ ) {
73
+ return await Promise . all (
74
+ fetchedPositions . map ( async ( position ) => {
75
+ if ( position . distanceFromActiveBinBps > repositionThresholdBps ) {
76
+ const dlmmPool = await DLMM . create (
77
+ wallet . connection ,
78
+ new PublicKey ( position . poolAddress )
79
+ ) ;
80
+
81
+ const activeBin = await dlmmPool . getActiveBin ( ) ;
82
+ const TOTAL_RANGE_INTERVAL = position . binRange ;
83
+
84
+ try {
85
+ // Remove existing liquidity
86
+ const { userPositions } = await dlmmPool . getPositionsByUserAndLbPair ( wallet . publicKey ) ;
87
+ const userPosition = userPositions . find ( p =>
88
+ p . publicKey . toString ( ) === position . positionPubKey
89
+ ) ;
90
+
91
+ if ( ! userPosition ) return null ;
92
+
93
+ const binIdsToRemove = userPosition . positionData . positionBinData . map ( bin => bin . binId ) ;
94
+ const removeLiquidityTx = await dlmmPool . removeLiquidity ( {
95
+ position : userPosition . publicKey ,
96
+ user : wallet . publicKey ,
97
+ binIds : binIdsToRemove ,
98
+ liquiditiesBpsToRemove : new Array ( binIdsToRemove . length ) . fill ( 10000 ) , // 100%
99
+ shouldClaimAndClose : true
100
+ } ) ;
101
+
102
+ await sendTransaction ( wallet . connection , removeLiquidityTx , wallet ) ;
103
+
104
+ // Create new position around active bin
105
+ const minBinId = activeBin . binId - TOTAL_RANGE_INTERVAL ;
106
+ const maxBinId = activeBin . binId + TOTAL_RANGE_INTERVAL ;
107
+
108
+ const newPosition = await dlmmPool . initializePositionAndAddLiquidityByStrategy ( {
109
+ positionPubKey : userPosition . publicKey ,
110
+ user : wallet . publicKey ,
111
+ totalXAmount : userPosition . positionData . totalXAmount ,
112
+ totalYAmount : userPosition . positionData . totalYAmount ,
113
+ strategy : {
114
+ maxBinId,
115
+ minBinId,
116
+ strategyType : StrategyType . SpotBalanced ,
117
+ } ,
118
+ } ) ;
119
+
120
+ await sendTransaction ( wallet . connection , newPosition , wallet ) ;
121
+
122
+ return {
123
+ oldPosition : position . positionPubKey ,
124
+ newPosition : userPosition . publicKey . toString ( )
125
+ } ;
126
+
127
+ } catch ( error ) {
128
+ elizaLogger . error ( `Error repositioning: ${ error } ` ) ;
129
+ return null ;
130
+ }
131
+ }
132
+ return null ;
133
+ } )
134
+ ) ;
135
+ }
136
+
137
+ // ... rest of the helper functions similar to Orca implementation ...
0 commit comments