@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/.DS_Store +0 -0
- package/.gitignore +2 -0
- package/idl/lavarage.ts +2511 -0
- package/idl/lavaragev2.ts +2664 -0
- package/index.ts +685 -0
- package/package.json +30 -0
- package/tsconfig.json +108 -0
- package/tsup.config.ts +10 -0
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
|
+
}
|