1+ import { FastifyPluginAsync , FastifyInstance } from 'fastify'
2+ import { Gamma } from '../gamma'
3+ import { Solana , BASE_FEE } from '../../../chains/solana/solana'
4+ import { logger } from '../../../services/logger'
5+ import {
6+ AddLiquidityRequest ,
7+ AddLiquidityResponse ,
8+ AddLiquidityRequestType ,
9+ AddLiquidityResponseType ,
10+ QuoteLiquidityResponseType ,
11+ } from '../../../schemas/amm-schema'
12+ import { Percent , PoolInfo , PoolKeys , TxVersion } from 'goosefx-amm-sdk'
13+ import { quoteLiquidity } from './quoteLiquidity'
14+ import Decimal from 'decimal.js'
15+ import BN from 'bn.js'
16+ import { VersionedTransaction , Transaction } from '@solana/web3.js'
17+
18+ async function createAddLiquidityTransaction (
19+ gamma : Gamma ,
20+ poolInfo : PoolInfo ,
21+ poolKeys : PoolKeys ,
22+ baseTokenAmountAdded : number ,
23+ quoteTokenAmountAdded : number ,
24+ baseLimited : boolean ,
25+ slippage : Percent ,
26+ computeBudgetConfig : { units : number ; microLamports : number }
27+ ) : Promise < VersionedTransaction | Transaction > {
28+ const inputAmount = new BN (
29+ new Decimal ( baseLimited ? baseTokenAmountAdded : quoteTokenAmountAdded )
30+ . mul ( 10 ** ( baseLimited ? poolInfo . mintA . decimals : poolInfo . mintB . decimals ) )
31+ . toFixed ( 0 )
32+ ) ;
33+ const response = await gamma . client . cpmm . addLiquidity ( {
34+ poolInfo : poolInfo ,
35+ poolKeys : poolKeys ,
36+ inputAmount,
37+ slippage,
38+ baseSpecified : baseLimited ,
39+ txVersion : TxVersion . V0 ,
40+ computeBudgetConfig,
41+ } )
42+ return response . transaction
43+ }
44+
45+ async function addLiquidity (
46+ _fastify : FastifyInstance ,
47+ network : string ,
48+ walletAddress : string ,
49+ poolAddress : string ,
50+ baseTokenAmount : number ,
51+ quoteTokenAmount : number ,
52+ slippagePct ?: number
53+ ) : Promise < AddLiquidityResponseType > {
54+ const solana = await Solana . getInstance ( network )
55+ const gamma = await Gamma . getInstance ( network )
56+ const wallet = await solana . getWallet ( walletAddress ) ;
57+
58+ const { poolInfo, poolKeys } = await gamma . client . cpmm . getPoolInfoFromRpc ( poolAddress )
59+
60+ const { baseLimited, baseTokenAmountMax, quoteTokenAmountMax } = await quoteLiquidity (
61+ _fastify ,
62+ network ,
63+ poolAddress ,
64+ baseTokenAmount ,
65+ quoteTokenAmount ,
66+ slippagePct
67+ ) as QuoteLiquidityResponseType ;
68+
69+ const baseTokenAmountAdded = baseLimited ? baseTokenAmount : baseTokenAmountMax ;
70+ const quoteTokenAmountAdded = baseLimited ? quoteTokenAmount : quoteTokenAmountMax ;
71+
72+ logger . info ( `Adding liquidity to Gamma...` ) ;
73+ const COMPUTE_UNITS = 600000
74+ const slippage = new Percent (
75+ Math . floor ( ( ( slippagePct === 0 ? 0 : slippagePct || gamma . getSlippagePct ( 'amm' ) ) * 100 ) / 10000 )
76+ ) ;
77+
78+ let currentPriorityFee = ( await solana . estimateGas ( ) * 1e9 ) - BASE_FEE
79+ while ( currentPriorityFee <= solana . config . maxPriorityFee * 1e9 ) {
80+ const priorityFeePerCU = Math . floor ( currentPriorityFee * 1e6 / COMPUTE_UNITS )
81+
82+ const transaction = await createAddLiquidityTransaction (
83+ gamma ,
84+ poolInfo ,
85+ poolKeys ,
86+ baseTokenAmountAdded ,
87+ quoteTokenAmountAdded ,
88+ baseLimited ,
89+ slippage ,
90+ {
91+ units : COMPUTE_UNITS ,
92+ microLamports : priorityFeePerCU ,
93+ }
94+ )
95+ console . log ( 'transaction' , transaction ) ;
96+
97+ if ( transaction instanceof VersionedTransaction ) {
98+ ( transaction as VersionedTransaction ) . sign ( [ wallet ] ) ;
99+ } else {
100+ const txAsTransaction = transaction as Transaction ;
101+ const { blockhash, lastValidBlockHeight } = await solana . connection . getLatestBlockhash ( ) ;
102+ txAsTransaction . recentBlockhash = blockhash ;
103+ txAsTransaction . lastValidBlockHeight = lastValidBlockHeight ;
104+ txAsTransaction . feePayer = wallet . publicKey ;
105+ txAsTransaction . sign ( wallet ) ;
106+ }
107+
108+ await solana . simulateTransaction ( transaction ) ;
109+
110+ console . log ( 'signed transaction' , transaction ) ;
111+
112+ const { confirmed, signature, txData } = await solana . sendAndConfirmRawTransaction ( transaction ) ;
113+ if ( confirmed && txData ) {
114+ const { baseTokenBalanceChange, quoteTokenBalanceChange } =
115+ await solana . extractPairBalanceChangesAndFee (
116+ signature ,
117+ await solana . getToken ( poolInfo . mintA . address ) ,
118+ await solana . getToken ( poolInfo . mintB . address ) ,
119+ wallet . publicKey . toBase58 ( )
120+ ) ;
121+ return {
122+ signature,
123+ fee : txData . meta . fee / 1e9 ,
124+ baseTokenAmountAdded : baseTokenBalanceChange ,
125+ quoteTokenAmountAdded : quoteTokenBalanceChange ,
126+ }
127+ }
128+ currentPriorityFee = currentPriorityFee * solana . config . priorityFeeMultiplier
129+ logger . info ( `Increasing max priority fee to ${ ( currentPriorityFee / 1e9 ) . toFixed ( 6 ) } SOL` ) ;
130+ }
131+ throw new Error ( `Add liquidity failed after reaching max priority fee of ${ ( solana . config . maxPriorityFee / 1e9 ) . toFixed ( 6 ) } SOL` ) ;
132+ }
133+
134+ export const addLiquidityRoute : FastifyPluginAsync = async ( fastify ) => {
135+ // Get first wallet address for example
136+ const solana = await Solana . getInstance ( 'mainnet-beta' ) ;
137+ let firstWalletAddress = '<solana-wallet-address>' ;
138+
139+ const foundWallet = await solana . getFirstWalletAddress ( ) ;
140+ if ( foundWallet ) {
141+ firstWalletAddress = foundWallet ;
142+ } else {
143+ logger . debug ( 'No wallets found for examples in schema' ) ;
144+ }
145+
146+ // Update schema example
147+ AddLiquidityRequest . properties . walletAddress . examples = [ firstWalletAddress ] ;
148+
149+ fastify . post < {
150+ Body : AddLiquidityRequestType
151+ Reply : AddLiquidityResponseType
152+ } > (
153+ '/add-liquidity' ,
154+ {
155+ schema : {
156+ description : 'Add liquidity to a Gamma AMM/CPMM pool' ,
157+ tags : [ 'gamma/amm' ] ,
158+ body : {
159+ ...AddLiquidityRequest ,
160+ properties : {
161+ ...AddLiquidityRequest . properties ,
162+ network : { type : 'string' , default : 'mainnet-beta' } ,
163+ poolAddress : { type : 'string' , examples : [ 'Hjm1F98vgVdN7Y9L46KLqcZZWyTKS9tj9ybYKJcXnSng' ] } , // SOL-USDC
164+ slippagePct : { type : 'number' , examples : [ 1 ] } ,
165+ baseTokenAmount : { type : 'number' , examples : [ 1 ] } ,
166+ quoteTokenAmount : { type : 'number' , examples : [ 1 ] } ,
167+ }
168+ } ,
169+ response : {
170+ 200 : AddLiquidityResponse
171+ } ,
172+ }
173+ } ,
174+ async ( request ) => {
175+ try {
176+ const {
177+ network,
178+ walletAddress,
179+ poolAddress,
180+ baseTokenAmount,
181+ quoteTokenAmount,
182+ slippagePct
183+ } = request . body
184+
185+ return await addLiquidity (
186+ fastify ,
187+ network || 'mainnet-beta' ,
188+ walletAddress ,
189+ poolAddress ,
190+ baseTokenAmount ,
191+ quoteTokenAmount ,
192+ slippagePct
193+ )
194+ } catch ( e ) {
195+ logger . error ( e )
196+ throw fastify . httpErrors . internalServerError ( 'Internal server error' )
197+ }
198+ }
199+ )
200+ }
201+
202+ export default addLiquidityRoute
203+
0 commit comments