@lavarage/sdk 4.5.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/index.ts ADDED
@@ -0,0 +1,685 @@
1
+ import { BN, Program, ProgramAccount } from '@coral-xyz/anchor'
2
+ import { Lavarage } from './idl/lavarage'
3
+ import { Lavarage as LavarageV2 } from './idl/lavaragev2'
4
+ import bs58 from 'bs58'
5
+ import { AddressLookupTableAccount, Keypair, PublicKey, SystemProgram, SYSVAR_CLOCK_PUBKEY, SYSVAR_INSTRUCTIONS_PUBKEY, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction } from '@solana/web3.js'
6
+ import { ASSOCIATED_TOKEN_PROGRAM_ID, createAssociatedTokenAccountInstruction, getAccount, getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID, TokenAccountNotFoundError, TokenInvalidAccountOwnerError } from '@solana/spl-token'
7
+
8
+
9
+ export function getPda(seed: Buffer | Buffer[], programId: PublicKey) {
10
+ const seedsBuffer = Array.isArray(seed) ? seed : [seed]
11
+
12
+ return PublicKey.findProgramAddressSync(seedsBuffer, programId)[0]
13
+ }
14
+
15
+ export function getPositionAccountPDA(lavarageProgram: Program<Lavarage> | Program<LavarageV2>, offer: ProgramAccount, seed: PublicKey) {
16
+ return getPda([Buffer.from('position'), lavarageProgram.provider.publicKey!.toBuffer(), offer.publicKey.toBuffer(), seed.toBuffer()], lavarageProgram.programId)
17
+ }
18
+
19
+ async function getTokenAccountOrCreateIfNotExists(lavarageProgram: Program<Lavarage> | Program<LavarageV2>, ownerPublicKey: PublicKey, tokenAddress: PublicKey) {
20
+ const associatedTokenAddress = getAssociatedTokenAddressSync(tokenAddress, ownerPublicKey, true, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID)
21
+
22
+ try {
23
+ const tokenAccount = await getAccount(lavarageProgram.provider.connection, associatedTokenAddress, 'finalized')
24
+ return { account: tokenAccount, instruction: null }
25
+ }
26
+ catch (error) {
27
+ if (error instanceof TokenAccountNotFoundError || error instanceof TokenInvalidAccountOwnerError) {
28
+ const instruction = createAssociatedTokenAccountInstruction(
29
+ lavarageProgram.provider.publicKey!,
30
+ associatedTokenAddress,
31
+ ownerPublicKey,
32
+ tokenAddress,
33
+ TOKEN_PROGRAM_ID,
34
+ ASSOCIATED_TOKEN_PROGRAM_ID,
35
+ )
36
+
37
+ return {
38
+ account: {
39
+ address: associatedTokenAddress,
40
+ },
41
+ instruction,
42
+ }
43
+ }
44
+ else {
45
+ console.error('Error in getTokenAccountOrCreateIfNotExists: ', error)
46
+
47
+ return { account: null, instruction: null }
48
+ }
49
+ }
50
+ }
51
+
52
+
53
+ export * from './idl/lavarage'
54
+ export * as IDLV2 from './idl/lavaragev2'
55
+
56
+ export const getOffers = (lavarageProgram: Program<Lavarage> | Program<LavarageV2>) => {
57
+ return lavarageProgram.account.pool.all()
58
+ }
59
+
60
+ export const getOpenPositions = (lavarageProgram: Program<Lavarage> | Program<LavarageV2>) => {
61
+ return lavarageProgram.account.position.all([{ dataSize: 178 },
62
+ {
63
+ memcmp: {
64
+ offset: 40,
65
+ bytes: bs58.encode(new Uint8Array(8)),
66
+ },
67
+ },])
68
+ }
69
+
70
+ export const getClosedPositions = async (lavarageProgram: Program<Lavarage> | Program<LavarageV2>) => {
71
+ const value = BigInt(9997)
72
+ const valueBuffer = Buffer.alloc(8)
73
+ valueBuffer.writeBigUInt64LE(value)
74
+ const value2 = BigInt(9998)
75
+ const valueBuffer2 = Buffer.alloc(8)
76
+ valueBuffer2.writeBigUInt64LE(value2)
77
+ return (await lavarageProgram.account.position.all([{ dataSize: 178 },
78
+ {
79
+ memcmp: {
80
+ offset: 40,
81
+ bytes: bs58.encode(Uint8Array.from(valueBuffer)),
82
+ },
83
+ },])).concat(await lavarageProgram.account.position.all([{ dataSize: 178 },
84
+ {
85
+ memcmp: {
86
+ offset: 40,
87
+ bytes: bs58.encode(Uint8Array.from(valueBuffer2)),
88
+ },
89
+ },]))
90
+ }
91
+
92
+ export const getLiquidatedPositions = (lavarageProgram: Program<Lavarage> | Program<LavarageV2>) => {
93
+ const value = BigInt(9999)
94
+ const valueBuffer = Buffer.alloc(8)
95
+ valueBuffer.writeBigUInt64LE(value)
96
+ return lavarageProgram.account.position.all([{ dataSize: 178 },
97
+ {
98
+ memcmp: {
99
+ offset: 40,
100
+ bytes: bs58.encode(Uint8Array.from(valueBuffer)),
101
+ },
102
+ },])
103
+ }
104
+
105
+ export const getAllPositions = (lavarageProgram: Program<Lavarage> | Program<LavarageV2>) => {
106
+ return lavarageProgram.account.position.all([{ dataSize: 178 },
107
+ ])
108
+ }
109
+
110
+ export const openTradeV1 = async (lavarageProgram: Program<Lavarage>, offer: ProgramAccount<{
111
+ nodeWallet: PublicKey,
112
+ interestRate: number,
113
+ collateralType: PublicKey,
114
+ }>, jupInstruction: {
115
+ instructions: {
116
+ setupInstructions: Record<string, unknown>[]
117
+ swapInstruction: Record<string, unknown>
118
+ addressLookupTableAddresses: string[]
119
+ }
120
+ }, marginSOL: BN, leverage: number, randomSeed: Keypair, partnerFeeRecipient?: PublicKey) => {
121
+ // assuming all token accounts are created prior
122
+ const positionAccount = getPositionAccountPDA(lavarageProgram, offer, randomSeed.publicKey)
123
+ const fromTokenAccount = await getTokenAccountOrCreateIfNotExists(lavarageProgram, lavarageProgram.provider.publicKey!, offer.account.collateralType)
124
+
125
+ const toTokenAccount = await getTokenAccountOrCreateIfNotExists(lavarageProgram, positionAccount, offer.account.collateralType)
126
+
127
+ const tokenAccountCreationTx = new Transaction()
128
+
129
+ if (fromTokenAccount.instruction) {
130
+ tokenAccountCreationTx.add(fromTokenAccount.instruction)
131
+ }
132
+
133
+ if (toTokenAccount.instruction) {
134
+ tokenAccountCreationTx.add(toTokenAccount.instruction)
135
+ }
136
+
137
+ const instructionsJup = jupInstruction.instructions
138
+
139
+ const { setupInstructions, swapInstruction: swapInstructionPayload, addressLookupTableAddresses } = instructionsJup
140
+
141
+ const deserializeInstruction = (instruction: any) => {
142
+ return new TransactionInstruction({
143
+ programId: new PublicKey(instruction.programId),
144
+
145
+ keys: instruction.accounts.map((key: any) => ({
146
+ pubkey: new PublicKey(key.pubkey),
147
+ isSigner: key.isSigner,
148
+ isWritable: key.isWritable,
149
+ })),
150
+ data: Buffer.from(instruction.data, 'base64'),
151
+ })
152
+ }
153
+
154
+ const getAddressLookupTableAccounts = async (keys: string[]): Promise<AddressLookupTableAccount[]> => {
155
+ const addressLookupTableAccountInfos = await lavarageProgram.provider.connection.getMultipleAccountsInfo(keys.map(key => new PublicKey(key)))
156
+
157
+ return addressLookupTableAccountInfos.reduce((acc, accountInfo, index) => {
158
+ const addressLookupTableAddress = keys[index]
159
+ if (accountInfo) {
160
+ const addressLookupTableAccount = new AddressLookupTableAccount({
161
+ key: new PublicKey(addressLookupTableAddress),
162
+ state: AddressLookupTableAccount.deserialize(Uint8Array.from(accountInfo.data)),
163
+ })
164
+ acc.push(addressLookupTableAccount)
165
+ }
166
+
167
+ return acc
168
+ }, new Array<AddressLookupTableAccount>())
169
+ }
170
+
171
+ const addressLookupTableAccounts: AddressLookupTableAccount[] = []
172
+
173
+ addressLookupTableAccounts.push(...(await getAddressLookupTableAccounts(addressLookupTableAddresses)))
174
+
175
+ const { blockhash } = await lavarageProgram.provider.connection.getLatestBlockhash('finalized')
176
+
177
+ const tradingOpenBorrowInstruction = await lavarageProgram.methods
178
+ .tradingOpenBorrow(new BN((marginSOL.toNumber()*leverage).toFixed(0)), marginSOL)
179
+ .accountsStrict({
180
+ nodeWallet: offer.account.nodeWallet,
181
+ instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
182
+ tradingPool: offer.publicKey,
183
+ positionAccount,
184
+ trader: lavarageProgram.provider.publicKey!,
185
+ systemProgram: SystemProgram.programId,
186
+ clock: SYSVAR_CLOCK_PUBKEY,
187
+ randomAccountAsId: randomSeed.publicKey.toBase58(),
188
+ feeReceipient: '6JfTobDvwuwZxZP6FR5JPmjdvQ4h4MovkEVH2FPsMSrF',
189
+ }).remainingAccounts(partnerFeeRecipient ? [{
190
+ pubkey: partnerFeeRecipient,
191
+ isSigner: false,
192
+ isWritable: true,
193
+ }] : [])
194
+ .instruction()
195
+
196
+ const openAddCollateralInstruction = await lavarageProgram.methods
197
+ .tradingOpenAddCollateral(offer.account.interestRate)
198
+ .accountsStrict({
199
+ tradingPool: offer.publicKey,
200
+ trader: lavarageProgram.provider.publicKey!,
201
+ mint: offer.account.collateralType,
202
+ toTokenAccount: toTokenAccount.account!.address,
203
+ systemProgram: SystemProgram.programId,
204
+ positionAccount,
205
+ randomAccountAsId: randomSeed.publicKey.toBase58(),
206
+ })
207
+ .instruction()
208
+
209
+ const jupiterIxs = [
210
+ ...setupInstructions.map(deserializeInstruction),
211
+ deserializeInstruction(swapInstructionPayload),
212
+ ]
213
+
214
+ const allInstructions = [
215
+ fromTokenAccount.instruction!,
216
+ toTokenAccount.instruction!,
217
+ tradingOpenBorrowInstruction!,
218
+ ...jupiterIxs,
219
+ openAddCollateralInstruction!,
220
+ ].filter(Boolean)
221
+
222
+ const messageV0 = new TransactionMessage({
223
+ payerKey: lavarageProgram.provider.publicKey!,
224
+ recentBlockhash: blockhash,
225
+ instructions: allInstructions,
226
+ }).compileToV0Message(addressLookupTableAccounts)
227
+
228
+ const tx = new VersionedTransaction(messageV0)
229
+
230
+ return tx
231
+ }
232
+
233
+ export const openTradeV2 = async (lavarageProgram: Program<LavarageV2>, offer: ProgramAccount<{
234
+ nodeWallet: PublicKey,
235
+ interestRate: number,
236
+ collateralType: PublicKey,
237
+ }>, jupInstruction: {
238
+ instructions: {
239
+ setupInstructions: Record<string, unknown>[]
240
+ swapInstruction: Record<string, unknown>
241
+ addressLookupTableAddresses: string[]
242
+ }
243
+ }, marginSOL: BN, leverage: number, randomSeed: Keypair, quoteToken: PublicKey, partnerFeeRecipient?: PublicKey) => {
244
+ // assuming all token accounts are created prior
245
+ const positionAccount = getPositionAccountPDA(lavarageProgram, offer, randomSeed.publicKey)
246
+ const fromTokenAccount = await getTokenAccountOrCreateIfNotExists(lavarageProgram, lavarageProgram.provider.publicKey!, offer.account.collateralType)
247
+
248
+ const toTokenAccount = await getTokenAccountOrCreateIfNotExists(lavarageProgram, positionAccount, offer.account.collateralType)
249
+
250
+ const tokenAccountCreationTx = new Transaction()
251
+
252
+ if (fromTokenAccount.instruction) {
253
+ tokenAccountCreationTx.add(fromTokenAccount.instruction)
254
+ }
255
+
256
+ if (toTokenAccount.instruction) {
257
+ tokenAccountCreationTx.add(toTokenAccount.instruction)
258
+ }
259
+
260
+ const instructionsJup = jupInstruction.instructions
261
+
262
+ const { setupInstructions, swapInstruction: swapInstructionPayload, addressLookupTableAddresses } = instructionsJup
263
+
264
+ const deserializeInstruction = (instruction: any) => {
265
+ return new TransactionInstruction({
266
+ programId: new PublicKey(instruction.programId),
267
+
268
+ keys: instruction.accounts.map((key: any) => ({
269
+ pubkey: new PublicKey(key.pubkey),
270
+ isSigner: key.isSigner,
271
+ isWritable: key.isWritable,
272
+ })),
273
+ data: Buffer.from(instruction.data, 'base64'),
274
+ })
275
+ }
276
+
277
+ const getAddressLookupTableAccounts = async (keys: string[]): Promise<AddressLookupTableAccount[]> => {
278
+ const addressLookupTableAccountInfos = await lavarageProgram.provider.connection.getMultipleAccountsInfo(keys.map(key => new PublicKey(key)))
279
+
280
+ return addressLookupTableAccountInfos.reduce((acc, accountInfo, index) => {
281
+ const addressLookupTableAddress = keys[index]
282
+ if (accountInfo) {
283
+ const addressLookupTableAccount = new AddressLookupTableAccount({
284
+ key: new PublicKey(addressLookupTableAddress),
285
+ state: AddressLookupTableAccount.deserialize(Uint8Array.from(accountInfo.data)),
286
+ })
287
+ acc.push(addressLookupTableAccount)
288
+ }
289
+
290
+ return acc
291
+ }, new Array<AddressLookupTableAccount>())
292
+ }
293
+
294
+ const addressLookupTableAccounts: AddressLookupTableAccount[] = []
295
+
296
+ addressLookupTableAccounts.push(...(await getAddressLookupTableAccounts(addressLookupTableAddresses)))
297
+
298
+ const { blockhash } = await lavarageProgram.provider.connection.getLatestBlockhash('finalized')
299
+
300
+ const tradingOpenBorrowInstruction = await lavarageProgram.methods
301
+ .tradingOpenBorrow(new BN((marginSOL.toNumber()*leverage).toFixed(0)), marginSOL)
302
+ .accountsStrict({
303
+ nodeWallet: offer.account.nodeWallet,
304
+ instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
305
+ tradingPool: offer.publicKey,
306
+ positionAccount,
307
+ trader: lavarageProgram.provider.publicKey!,
308
+ systemProgram: SystemProgram.programId,
309
+ clock: SYSVAR_CLOCK_PUBKEY,
310
+ randomAccountAsId: randomSeed.publicKey.toBase58(),
311
+ feeTokenAccount: getAssociatedTokenAddressSync(quoteToken, new PublicKey('6JfTobDvwuwZxZP6FR5JPmjdvQ4h4MovkEVH2FPsMSrF')),
312
+ toTokenAccount: getAssociatedTokenAddressSync(quoteToken, lavarageProgram.provider.publicKey!),
313
+ tokenProgram: TOKEN_PROGRAM_ID,
314
+ fromTokenAccount: getAssociatedTokenAddressSync(quoteToken, offer.account.nodeWallet),
315
+ }).remainingAccounts(partnerFeeRecipient ? [{
316
+ pubkey: partnerFeeRecipient,
317
+ isSigner: false,
318
+ isWritable: true,
319
+ }] : [])
320
+ .instruction()
321
+
322
+ const openAddCollateralInstruction = await lavarageProgram.methods
323
+ .tradingOpenAddCollateral(offer.account.interestRate)
324
+ .accountsStrict({
325
+ tradingPool: offer.publicKey,
326
+ trader: lavarageProgram.provider.publicKey!,
327
+ mint: offer.account.collateralType,
328
+ toTokenAccount: toTokenAccount.account!.address,
329
+ systemProgram: SystemProgram.programId,
330
+ positionAccount,
331
+ randomAccountAsId: randomSeed.publicKey.toBase58(),
332
+ })
333
+ .instruction()
334
+
335
+ const jupiterIxs = [
336
+ ...setupInstructions.map(deserializeInstruction),
337
+ deserializeInstruction(swapInstructionPayload),
338
+ ]
339
+
340
+ const allInstructions = [
341
+ fromTokenAccount.instruction!,
342
+ toTokenAccount.instruction!,
343
+ tradingOpenBorrowInstruction!,
344
+ ...jupiterIxs,
345
+ openAddCollateralInstruction!,
346
+ ].filter(Boolean)
347
+
348
+ const messageV0 = new TransactionMessage({
349
+ payerKey: lavarageProgram.provider.publicKey!,
350
+ recentBlockhash: blockhash,
351
+ instructions: allInstructions,
352
+ }).compileToV0Message(addressLookupTableAccounts)
353
+
354
+ const tx = new VersionedTransaction(messageV0)
355
+
356
+ return tx
357
+ }
358
+
359
+ export const closeTradeV1 = async (lavarageProgram: Program<Lavarage>, position: ProgramAccount<{
360
+ pool: PublicKey,
361
+ seed: PublicKey,
362
+ userPaid: BN,
363
+ amount: BN,
364
+ }>, offer: ProgramAccount<{
365
+ nodeWallet: PublicKey,
366
+ interestRate: number,
367
+ collateralType: PublicKey,
368
+ }>, jupInstruction: {
369
+ instructions?: {
370
+ setupInstructions: Record<string, unknown>[]
371
+ swapInstruction: Record<string, unknown>
372
+ cleanupInstruction: Record<string, unknown>
373
+ addressLookupTableAddresses: string[]
374
+ },
375
+ quoteResponse: any
376
+ }, partnerFeeRecipient?: PublicKey, profitFeeMarkup?: number) => {
377
+ if (position.account.pool.toBase58() != offer.publicKey.toBase58()) throw "Mismatch offer"
378
+ const pool = offer
379
+ const poolPubKey = offer.publicKey
380
+
381
+ const tokenAddressPubKey = new PublicKey(offer.account.collateralType)
382
+
383
+ const positionAccountPDA = position.publicKey
384
+
385
+ const fromTokenAccount = await getTokenAccountOrCreateIfNotExists(lavarageProgram, positionAccountPDA, tokenAddressPubKey)
386
+
387
+ const toTokenAccount = await getTokenAccountOrCreateIfNotExists(lavarageProgram, lavarageProgram.provider.publicKey!, tokenAddressPubKey)
388
+
389
+ const jupiterSellIx = jupInstruction!.instructions
390
+
391
+
392
+ const deserializeInstruction = (instruction: any) => {
393
+ return new TransactionInstruction({
394
+ programId: new PublicKey(instruction.programId),
395
+
396
+ keys: instruction.accounts.map((key: any) => ({
397
+ pubkey: new PublicKey(key.pubkey),
398
+ isSigner: key.isSigner,
399
+ isWritable: key.isWritable,
400
+ })),
401
+ data: Buffer.from(instruction.data, 'base64'),
402
+ })
403
+ }
404
+
405
+ const getAddressLookupTableAccounts = async (keys: string[]): Promise<AddressLookupTableAccount[]> => {
406
+ const addressLookupTableAccountInfos = await lavarageProgram.provider.connection.getMultipleAccountsInfo(keys.map(key => new PublicKey(key)))
407
+
408
+ return addressLookupTableAccountInfos.reduce((acc, accountInfo, index) => {
409
+ const addressLookupTableAddress = keys[index]
410
+ if (accountInfo) {
411
+ const addressLookupTableAccount = new AddressLookupTableAccount({
412
+ key: new PublicKey(addressLookupTableAddress),
413
+ state: AddressLookupTableAccount.deserialize(Uint8Array.from(accountInfo.data)),
414
+ })
415
+ acc.push(addressLookupTableAccount)
416
+ }
417
+
418
+ return acc
419
+ }, new Array<AddressLookupTableAccount>())
420
+ }
421
+
422
+ const addressLookupTableAccounts: AddressLookupTableAccount[] = []
423
+
424
+
425
+
426
+ const { blockhash } = await lavarageProgram.provider.connection.getLatestBlockhash('finalized')
427
+
428
+
429
+
430
+ const closePositionIx = await lavarageProgram.methods
431
+ .tradingCloseBorrowCollateral()
432
+ .accountsStrict({
433
+ tradingPool: poolPubKey,
434
+ instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
435
+ mint: offer.account.collateralType,
436
+ fromTokenAccount: fromTokenAccount.account!.address,
437
+ toTokenAccount: toTokenAccount.account!.address,
438
+ positionAccount: positionAccountPDA,
439
+ clock: SYSVAR_CLOCK_PUBKEY,
440
+ systemProgram: SystemProgram.programId,
441
+ trader: lavarageProgram.provider.publicKey!,
442
+ tokenProgram: TOKEN_PROGRAM_ID,
443
+ randomAccountAsId: position.account.seed,
444
+ })
445
+ .instruction()
446
+
447
+ let repaySolIx: TransactionInstruction | null = null
448
+ let jupiterIxs: TransactionInstruction[] = []
449
+ if (jupInstruction.instructions == null ) {
450
+ repaySolIx = await lavarageProgram.methods
451
+ .tradingCloseRepaySol(new BN(jupInstruction.quoteResponse.outAmount), new BN(9997))
452
+ .accountsStrict({
453
+ nodeWallet: pool.account.nodeWallet,
454
+ positionAccount: positionAccountPDA,
455
+ tradingPool: poolPubKey,
456
+ trader: lavarageProgram.provider.publicKey!,
457
+ systemProgram: SystemProgram.programId,
458
+ clock: SYSVAR_CLOCK_PUBKEY,
459
+ randomAccountAsId: position.account.seed,
460
+ feeReceipient: '6JfTobDvwuwZxZP6FR5JPmjdvQ4h4MovkEVH2FPsMSrF',
461
+ }).remainingAccounts(partnerFeeRecipient ? [{
462
+ pubkey: partnerFeeRecipient,
463
+ isSigner: false,
464
+ isWritable: true,
465
+ }] : [])
466
+ .instruction()
467
+ } else {
468
+ repaySolIx = await lavarageProgram.methods
469
+ .tradingCloseRepaySol(new BN(jupInstruction.quoteResponse.outAmount), new BN(9998))
470
+ .accountsStrict({
471
+ nodeWallet: pool.account.nodeWallet,
472
+ positionAccount: positionAccountPDA,
473
+ tradingPool: poolPubKey,
474
+ trader: lavarageProgram.provider.publicKey!,
475
+ systemProgram: SystemProgram.programId,
476
+ clock: SYSVAR_CLOCK_PUBKEY,
477
+ randomAccountAsId: position.account.seed,
478
+ feeReceipient: '6JfTobDvwuwZxZP6FR5JPmjdvQ4h4MovkEVH2FPsMSrF',
479
+ }).remainingAccounts(partnerFeeRecipient ? [{
480
+ pubkey: partnerFeeRecipient,
481
+ isSigner: false,
482
+ isWritable: true,
483
+ }] : [])
484
+ .instruction()
485
+ const { setupInstructions, swapInstruction: swapInstructionPayload, cleanupInstruction, addressLookupTableAddresses } = jupiterSellIx!
486
+ jupiterIxs = [
487
+ ...setupInstructions.map(deserializeInstruction),
488
+ deserializeInstruction(swapInstructionPayload),
489
+ deserializeInstruction(cleanupInstruction),
490
+ ]
491
+ addressLookupTableAccounts.push(...(await getAddressLookupTableAccounts(addressLookupTableAddresses)))
492
+ }
493
+ const profit = new BN(jupInstruction.quoteResponse.outAmount).sub(position.account.amount).sub(position.account.userPaid)
494
+ const allInstructions = [
495
+ toTokenAccount.instruction!,
496
+ closePositionIx,
497
+ ...jupiterIxs,
498
+ repaySolIx,
499
+ profitFeeMarkup && partnerFeeRecipient ? SystemProgram.transfer(
500
+ {
501
+ fromPubkey: lavarageProgram.provider.publicKey!,
502
+ toPubkey: partnerFeeRecipient!,
503
+ lamports: profit.toNumber() > 0 ? profit.mul(new BN(profitFeeMarkup * 1000)).div(new BN(1000)).toNumber() : 0
504
+ }
505
+ ) : null,
506
+ ].filter(i => !!i)
507
+
508
+ const messageV0 = new TransactionMessage({
509
+ payerKey: lavarageProgram.provider.publicKey!,
510
+ recentBlockhash: blockhash,
511
+ instructions: allInstructions,
512
+ }).compileToV0Message(addressLookupTableAccounts)
513
+
514
+ const tx = new VersionedTransaction(messageV0)
515
+
516
+ return tx
517
+ }
518
+
519
+ export const closeTradeV2 = async (lavarageProgram: Program<LavarageV2>, position: ProgramAccount<{
520
+ pool: PublicKey,
521
+ seed: PublicKey,
522
+ userPaid: BN,
523
+ amount: BN,
524
+ }>, offer: ProgramAccount<{
525
+ nodeWallet: PublicKey,
526
+ interestRate: number,
527
+ collateralType: PublicKey,
528
+ }>, jupInstruction: {
529
+ instructions?: {
530
+ setupInstructions: Record<string, unknown>[]
531
+ swapInstruction: Record<string, unknown>
532
+ cleanupInstruction: Record<string, unknown>
533
+ addressLookupTableAddresses: string[]
534
+ },
535
+ quoteResponse: any
536
+ }, quoteToken: PublicKey, partnerFeeRecipient?: PublicKey, profitFeeMarkup?: number) => {
537
+ if (position.account.pool.toBase58() != offer.publicKey.toBase58()) throw "Mismatch offer"
538
+ const pool = offer
539
+ const poolPubKey = offer.publicKey
540
+
541
+ const tokenAddressPubKey = new PublicKey(offer.account.collateralType)
542
+
543
+ const positionAccountPDA = position.publicKey
544
+
545
+ const fromTokenAccount = await getTokenAccountOrCreateIfNotExists(lavarageProgram, positionAccountPDA, tokenAddressPubKey)
546
+
547
+ const toTokenAccount = await getTokenAccountOrCreateIfNotExists(lavarageProgram, lavarageProgram.provider.publicKey!, tokenAddressPubKey)
548
+
549
+ const jupiterSellIx = jupInstruction!.instructions
550
+
551
+
552
+ const deserializeInstruction = (instruction: any) => {
553
+ return new TransactionInstruction({
554
+ programId: new PublicKey(instruction.programId),
555
+
556
+ keys: instruction.accounts.map((key: any) => ({
557
+ pubkey: new PublicKey(key.pubkey),
558
+ isSigner: key.isSigner,
559
+ isWritable: key.isWritable,
560
+ })),
561
+ data: Buffer.from(instruction.data, 'base64'),
562
+ })
563
+ }
564
+
565
+ const getAddressLookupTableAccounts = async (keys: string[]): Promise<AddressLookupTableAccount[]> => {
566
+ const addressLookupTableAccountInfos = await lavarageProgram.provider.connection.getMultipleAccountsInfo(keys.map(key => new PublicKey(key)))
567
+
568
+ return addressLookupTableAccountInfos.reduce((acc, accountInfo, index) => {
569
+ const addressLookupTableAddress = keys[index]
570
+ if (accountInfo) {
571
+ const addressLookupTableAccount = new AddressLookupTableAccount({
572
+ key: new PublicKey(addressLookupTableAddress),
573
+ state: AddressLookupTableAccount.deserialize(Uint8Array.from(accountInfo.data)),
574
+ })
575
+ acc.push(addressLookupTableAccount)
576
+ }
577
+
578
+ return acc
579
+ }, new Array<AddressLookupTableAccount>())
580
+ }
581
+
582
+ const addressLookupTableAccounts: AddressLookupTableAccount[] = []
583
+
584
+
585
+
586
+ const { blockhash } = await lavarageProgram.provider.connection.getLatestBlockhash('finalized')
587
+
588
+
589
+
590
+ const closePositionIx = await lavarageProgram.methods
591
+ .tradingCloseBorrowCollateral()
592
+ .accountsStrict({
593
+ tradingPool: poolPubKey,
594
+ instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
595
+ mint: offer.account.collateralType,
596
+ fromTokenAccount: fromTokenAccount.account!.address,
597
+ toTokenAccount: toTokenAccount.account!.address,
598
+ positionAccount: positionAccountPDA,
599
+ clock: SYSVAR_CLOCK_PUBKEY,
600
+ systemProgram: SystemProgram.programId,
601
+ trader: lavarageProgram.provider.publicKey!,
602
+ tokenProgram: TOKEN_PROGRAM_ID,
603
+ randomAccountAsId: position.account.seed,
604
+ })
605
+ .instruction()
606
+
607
+ let repaySolIx: TransactionInstruction | null = null
608
+ let jupiterIxs: TransactionInstruction[] = []
609
+ if (jupInstruction.instructions == null ) {
610
+ repaySolIx = await lavarageProgram.methods
611
+ .tradingCloseRepaySol(new BN(jupInstruction.quoteResponse.outAmount), new BN(9997))
612
+ .accountsStrict({
613
+ nodeWallet: pool.account.nodeWallet,
614
+ positionAccount: positionAccountPDA,
615
+ tradingPool: poolPubKey,
616
+ trader: lavarageProgram.provider.publicKey!,
617
+ systemProgram: SystemProgram.programId,
618
+ clock: SYSVAR_CLOCK_PUBKEY,
619
+ randomAccountAsId: position.account.seed,
620
+ feeTokenAccount: getAssociatedTokenAddressSync(quoteToken, new PublicKey('6JfTobDvwuwZxZP6FR5JPmjdvQ4h4MovkEVH2FPsMSrF')),
621
+ fromTokenAccount: getAssociatedTokenAddressSync(quoteToken, lavarageProgram.provider.publicKey!),
622
+ tokenProgram: TOKEN_PROGRAM_ID,
623
+ toTokenAccount: getAssociatedTokenAddressSync(quoteToken, pool.account.nodeWallet),
624
+ mint: quoteToken,
625
+ }).remainingAccounts(partnerFeeRecipient ? [{
626
+ pubkey: partnerFeeRecipient,
627
+ isSigner: false,
628
+ isWritable: true,
629
+ }] : [])
630
+ .instruction()
631
+ } else {
632
+ repaySolIx = await lavarageProgram.methods
633
+ .tradingCloseRepaySol(new BN(jupInstruction.quoteResponse.outAmount), new BN(9998))
634
+ .accountsStrict({
635
+ nodeWallet: pool.account.nodeWallet,
636
+ positionAccount: positionAccountPDA,
637
+ tradingPool: poolPubKey,
638
+ trader: lavarageProgram.provider.publicKey!,
639
+ systemProgram: SystemProgram.programId,
640
+ clock: SYSVAR_CLOCK_PUBKEY,
641
+ randomAccountAsId: position.account.seed,
642
+ feeTokenAccount: getAssociatedTokenAddressSync(quoteToken, new PublicKey('6JfTobDvwuwZxZP6FR5JPmjdvQ4h4MovkEVH2FPsMSrF')),
643
+ fromTokenAccount: getAssociatedTokenAddressSync(quoteToken, lavarageProgram.provider.publicKey!),
644
+ tokenProgram: TOKEN_PROGRAM_ID,
645
+ toTokenAccount: getAssociatedTokenAddressSync(quoteToken, pool.account.nodeWallet),
646
+ mint: quoteToken,
647
+ }).remainingAccounts(partnerFeeRecipient ? [{
648
+ pubkey: partnerFeeRecipient,
649
+ isSigner: false,
650
+ isWritable: true,
651
+ }] : [])
652
+ .instruction()
653
+ const { setupInstructions, swapInstruction: swapInstructionPayload, cleanupInstruction, addressLookupTableAddresses } = jupiterSellIx!
654
+ jupiterIxs = [
655
+ ...setupInstructions.map(deserializeInstruction),
656
+ deserializeInstruction(swapInstructionPayload),
657
+ deserializeInstruction(cleanupInstruction),
658
+ ]
659
+ addressLookupTableAccounts.push(...(await getAddressLookupTableAccounts(addressLookupTableAddresses)))
660
+ }
661
+ const profit = new BN(jupInstruction.quoteResponse.outAmount).sub(position.account.amount).sub(position.account.userPaid)
662
+ const allInstructions = [
663
+ toTokenAccount.instruction!,
664
+ closePositionIx,
665
+ ...jupiterIxs,
666
+ repaySolIx,
667
+ profitFeeMarkup && partnerFeeRecipient ? SystemProgram.transfer(
668
+ {
669
+ fromPubkey: lavarageProgram.provider.publicKey!,
670
+ toPubkey: partnerFeeRecipient!,
671
+ lamports: profit.toNumber() > 0 ? profit.mul(new BN(profitFeeMarkup * 1000)).div(new BN(1000)).toNumber() : 0
672
+ }
673
+ ) : null,
674
+ ].filter(i => !!i)
675
+
676
+ const messageV0 = new TransactionMessage({
677
+ payerKey: lavarageProgram.provider.publicKey!,
678
+ recentBlockhash: blockhash,
679
+ instructions: allInstructions,
680
+ }).compileToV0Message(addressLookupTableAccounts)
681
+
682
+ const tx = new VersionedTransaction(messageV0)
683
+
684
+ return tx
685
+ }