@kamino-finance/klend-sdk 5.11.0 → 5.11.2-beta.0
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/dist/classes/action.d.ts +12 -2
- package/dist/classes/action.d.ts.map +1 -1
- package/dist/classes/action.js +245 -29
- package/dist/classes/action.js.map +1 -1
- package/dist/classes/obligation.d.ts +1 -1
- package/dist/classes/obligation.d.ts.map +1 -1
- package/dist/classes/obligation.js +1 -1
- package/dist/classes/obligation.js.map +1 -1
- package/dist/classes/vault.d.ts +1 -0
- package/dist/classes/vault.d.ts.map +1 -1
- package/dist/classes/vault.js +13 -10
- package/dist/classes/vault.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/accounts/VaultState.d.ts +3 -0
- package/dist/idl_codegen_kamino_vault/accounts/VaultState.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/accounts/VaultState.js +7 -1
- package/dist/idl_codegen_kamino_vault/accounts/VaultState.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/errors/custom.d.ts +25 -1
- package/dist/idl_codegen_kamino_vault/errors/custom.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/errors/custom.js +43 -1
- package/dist/idl_codegen_kamino_vault/errors/custom.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/initVault.d.ts +1 -0
- package/dist/idl_codegen_kamino_vault/instructions/initVault.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/initVault.js +1 -0
- package/dist/idl_codegen_kamino_vault/instructions/initVault.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.d.ts +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.js +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/VaultConfigField.d.ts +13 -0
- package/dist/idl_codegen_kamino_vault/types/VaultConfigField.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/VaultConfigField.js +25 -1
- package/dist/idl_codegen_kamino_vault/types/VaultConfigField.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/index.d.ts +2 -2
- package/dist/idl_codegen_kamino_vault/types/index.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/index.js.map +1 -1
- package/dist/lending_operations/repay_with_collateral_calcs.d.ts.map +1 -1
- package/dist/lending_operations/repay_with_collateral_calcs.js +9 -5
- package/dist/lending_operations/repay_with_collateral_calcs.js.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.d.ts +5 -0
- package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.js +26 -1
- package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
- package/dist/utils/constants.d.ts +1 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +2 -1
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/lookupTable.d.ts +27 -0
- package/dist/utils/lookupTable.d.ts.map +1 -1
- package/dist/utils/lookupTable.js +58 -0
- package/dist/utils/lookupTable.js.map +1 -1
- package/dist/utils/seeds.d.ts +11 -1
- package/dist/utils/seeds.d.ts.map +1 -1
- package/dist/utils/seeds.js +13 -3
- package/dist/utils/seeds.js.map +1 -1
- package/dist/utils/userMetadata.js +6 -6
- package/dist/utils/userMetadata.js.map +1 -1
- package/package.json +1 -1
- package/src/classes/action.ts +378 -49
- package/src/classes/obligation.ts +1 -1
- package/src/classes/vault.ts +10 -9
- package/src/idl_codegen_kamino_vault/accounts/VaultState.ts +9 -1
- package/src/idl_codegen_kamino_vault/errors/custom.ts +42 -0
- package/src/idl_codegen_kamino_vault/instructions/initVault.ts +2 -0
- package/src/idl_codegen_kamino_vault/instructions/updateReserveAllocation.ts +2 -2
- package/src/idl_codegen_kamino_vault/types/VaultConfigField.ts +30 -0
- package/src/idl_codegen_kamino_vault/types/index.ts +2 -0
- package/src/idl_kamino_vault.json +30 -3
- package/src/lending_operations/repay_with_collateral_calcs.ts +14 -5
- package/src/lending_operations/repay_with_collateral_operations.ts +63 -20
- package/src/utils/constants.ts +1 -0
- package/src/utils/lookupTable.ts +62 -0
- package/src/utils/seeds.ts +14 -4
- package/src/utils/userMetadata.ts +14 -14
- package/dist/classes/lut_utils.d.ts +0 -29
- package/dist/classes/lut_utils.d.ts.map +0 -1
- package/dist/classes/lut_utils.js +0 -62
- package/dist/classes/lut_utils.js.map +0 -1
- package/src/classes/lut_utils.ts +0 -63
package/src/classes/vault.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
SYSVAR_RENT_PUBKEY,
|
|
12
12
|
TransactionInstruction,
|
|
13
13
|
} from '@solana/web3.js';
|
|
14
|
-
import { NATIVE_MINT, TOKEN_PROGRAM_ID, unpackAccount } from '@solana/spl-token';
|
|
14
|
+
import { getAssociatedTokenAddressSync, NATIVE_MINT, TOKEN_PROGRAM_ID, unpackAccount } from '@solana/spl-token';
|
|
15
15
|
import {
|
|
16
16
|
getAssociatedTokenAddress,
|
|
17
17
|
getTransferWsolIxns,
|
|
@@ -82,7 +82,7 @@ import {
|
|
|
82
82
|
import { batchFetch, collToLamportsDecimal, ZERO } from '@kamino-finance/kliquidity-sdk';
|
|
83
83
|
import { FullBPSDecimal } from '@kamino-finance/kliquidity-sdk/dist/utils/CreationParameters';
|
|
84
84
|
import { FarmState } from '@kamino-finance/farms-sdk/dist';
|
|
85
|
-
import { getAccountsInLUT, initLookupTableIx } from '
|
|
85
|
+
import { getAccountsInLUT, initLookupTableIx } from '../utils/lookupTable';
|
|
86
86
|
import {
|
|
87
87
|
getFarmStakeIxs,
|
|
88
88
|
getFarmUnstakeAndWithdrawIxs,
|
|
@@ -100,6 +100,8 @@ const CTOKEN_VAULT_SEED = 'ctoken_vault';
|
|
|
100
100
|
const BASE_VAULT_AUTHORITY_SEED = 'authority';
|
|
101
101
|
const SHARES_SEED = 'shares';
|
|
102
102
|
|
|
103
|
+
export const INITIAL_DEPOSIT_LAMPORTS = 1000;
|
|
104
|
+
|
|
103
105
|
/**
|
|
104
106
|
* KaminoVaultClient is a class that provides a high-level interface to interact with the Kamino Vault program.
|
|
105
107
|
*/
|
|
@@ -195,6 +197,8 @@ export class KaminoVaultClient {
|
|
|
195
197
|
this._kaminoVaultProgramId
|
|
196
198
|
)[0];
|
|
197
199
|
|
|
200
|
+
const adminTokenAccount = getAssociatedTokenAddressSync(vaultConfig.tokenMint, vaultConfig.admin, false);
|
|
201
|
+
|
|
198
202
|
const initVaultAccounts: InitVaultAccounts = {
|
|
199
203
|
adminAuthority: vaultConfig.admin,
|
|
200
204
|
vaultState: vaultState.publicKey,
|
|
@@ -206,6 +210,7 @@ export class KaminoVaultClient {
|
|
|
206
210
|
rent: SYSVAR_RENT_PUBKEY,
|
|
207
211
|
tokenProgram: vaultConfig.tokenMintProgramId,
|
|
208
212
|
sharesTokenProgram: TOKEN_PROGRAM_ID,
|
|
213
|
+
adminTokenAccount,
|
|
209
214
|
};
|
|
210
215
|
const initVaultIx = initVault(initVaultAccounts, this._kaminoVaultProgramId);
|
|
211
216
|
|
|
@@ -288,7 +293,7 @@ export class KaminoVaultClient {
|
|
|
288
293
|
);
|
|
289
294
|
|
|
290
295
|
const updateReserveAllocationAccounts: UpdateReserveAllocationAccounts = {
|
|
291
|
-
|
|
296
|
+
signer: vaultState.adminAuthority,
|
|
292
297
|
vaultState: vault.address,
|
|
293
298
|
baseVaultAuthority: vaultState.baseVaultAuthority,
|
|
294
299
|
reserveCollateralMint: reserveState.collateral.mintPubkey,
|
|
@@ -1010,20 +1015,16 @@ export class KaminoVaultClient {
|
|
|
1010
1015
|
if (allReserves.length === 0) {
|
|
1011
1016
|
throw new Error('No reserves found for the vault, please select at least one reserve for the vault');
|
|
1012
1017
|
}
|
|
1013
|
-
|
|
1014
1018
|
const [allReservesStateMap, computedReservesAllocation] = await Promise.all([
|
|
1015
1019
|
this.loadVaultReserves(vaultState),
|
|
1016
1020
|
this.getVaultComputedReservesAllocation(vaultState),
|
|
1017
1021
|
]);
|
|
1018
|
-
|
|
1019
1022
|
const tokenProgram = await getAccountOwner(this.getConnection(), vaultState.tokenMint);
|
|
1020
1023
|
const [{ ata: _payerTokenAta, createAtaIx }] = createAtasIdempotent(payer, [
|
|
1021
1024
|
{ mint: vaultState.tokenMint, tokenProgram },
|
|
1022
1025
|
]);
|
|
1023
|
-
|
|
1024
1026
|
// compute total vault holdings and expected distribution based on weights
|
|
1025
1027
|
const curentVaultAllocations = this.getVaultAllocations(vaultState);
|
|
1026
|
-
|
|
1027
1028
|
const reservesToDisinvestFrom: PublicKey[] = [];
|
|
1028
1029
|
const reservesToInvestInto: PublicKey[] = [];
|
|
1029
1030
|
|
|
@@ -1528,8 +1529,8 @@ export class KaminoVaultClient {
|
|
|
1528
1529
|
|
|
1529
1530
|
let totalLeftToInvest = holdings.totalAUMIncludingFees.sub(holdings.pendingFees);
|
|
1530
1531
|
let currentAllocationSum = totalAllocation;
|
|
1531
|
-
const
|
|
1532
|
-
while (totalLeftToInvest.gt(
|
|
1532
|
+
const ONE = new Decimal(1);
|
|
1533
|
+
while (totalLeftToInvest.gt(ONE)) {
|
|
1533
1534
|
const totalLeftover = totalLeftToInvest;
|
|
1534
1535
|
for (const reserve of allReserves) {
|
|
1535
1536
|
const reserveWithWeight = initialVaultAllocations.get(reserve);
|
|
@@ -38,6 +38,7 @@ export interface VaultStateFields {
|
|
|
38
38
|
vaultFarm: PublicKey
|
|
39
39
|
creationTimestamp: BN
|
|
40
40
|
padding1: BN
|
|
41
|
+
allocationAdmin: PublicKey
|
|
41
42
|
padding2: Array<BN>
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -75,6 +76,7 @@ export interface VaultStateJSON {
|
|
|
75
76
|
vaultFarm: string
|
|
76
77
|
creationTimestamp: string
|
|
77
78
|
padding1: string
|
|
79
|
+
allocationAdmin: string
|
|
78
80
|
padding2: Array<string>
|
|
79
81
|
}
|
|
80
82
|
|
|
@@ -112,6 +114,7 @@ export class VaultState {
|
|
|
112
114
|
readonly vaultFarm: PublicKey
|
|
113
115
|
readonly creationTimestamp: BN
|
|
114
116
|
readonly padding1: BN
|
|
117
|
+
readonly allocationAdmin: PublicKey
|
|
115
118
|
readonly padding2: Array<BN>
|
|
116
119
|
|
|
117
120
|
static readonly discriminator = Buffer.from([
|
|
@@ -152,7 +155,8 @@ export class VaultState {
|
|
|
152
155
|
borsh.publicKey("vaultFarm"),
|
|
153
156
|
borsh.u64("creationTimestamp"),
|
|
154
157
|
borsh.u64("padding1"),
|
|
155
|
-
borsh.
|
|
158
|
+
borsh.publicKey("allocationAdmin"),
|
|
159
|
+
borsh.array(borsh.u128(), 242, "padding2"),
|
|
156
160
|
])
|
|
157
161
|
|
|
158
162
|
constructor(fields: VaultStateFields) {
|
|
@@ -191,6 +195,7 @@ export class VaultState {
|
|
|
191
195
|
this.vaultFarm = fields.vaultFarm
|
|
192
196
|
this.creationTimestamp = fields.creationTimestamp
|
|
193
197
|
this.padding1 = fields.padding1
|
|
198
|
+
this.allocationAdmin = fields.allocationAdmin
|
|
194
199
|
this.padding2 = fields.padding2
|
|
195
200
|
}
|
|
196
201
|
|
|
@@ -275,6 +280,7 @@ export class VaultState {
|
|
|
275
280
|
vaultFarm: dec.vaultFarm,
|
|
276
281
|
creationTimestamp: dec.creationTimestamp,
|
|
277
282
|
padding1: dec.padding1,
|
|
283
|
+
allocationAdmin: dec.allocationAdmin,
|
|
278
284
|
padding2: dec.padding2,
|
|
279
285
|
})
|
|
280
286
|
}
|
|
@@ -316,6 +322,7 @@ export class VaultState {
|
|
|
316
322
|
vaultFarm: this.vaultFarm.toString(),
|
|
317
323
|
creationTimestamp: this.creationTimestamp.toString(),
|
|
318
324
|
padding1: this.padding1.toString(),
|
|
325
|
+
allocationAdmin: this.allocationAdmin.toString(),
|
|
319
326
|
padding2: this.padding2.map((item) => item.toString()),
|
|
320
327
|
}
|
|
321
328
|
}
|
|
@@ -357,6 +364,7 @@ export class VaultState {
|
|
|
357
364
|
vaultFarm: new PublicKey(obj.vaultFarm),
|
|
358
365
|
creationTimestamp: new BN(obj.creationTimestamp),
|
|
359
366
|
padding1: new BN(obj.padding1),
|
|
367
|
+
allocationAdmin: new PublicKey(obj.allocationAdmin),
|
|
360
368
|
padding2: obj.padding2.map((item) => new BN(item)),
|
|
361
369
|
})
|
|
362
370
|
}
|
|
@@ -43,6 +43,9 @@ export type CustomError =
|
|
|
43
43
|
| ManagementFeeGreaterThanMaxAllowed
|
|
44
44
|
| VaultAUMZero
|
|
45
45
|
| MissingReserveForBatchRefresh
|
|
46
|
+
| MinWithdrawAmountTooBig
|
|
47
|
+
| InvestTooSoon
|
|
48
|
+
| WrongAdminOrAllocationAdmin
|
|
46
49
|
|
|
47
50
|
export class DepositAmountsZero extends Error {
|
|
48
51
|
static readonly code = 7000
|
|
@@ -528,6 +531,39 @@ export class MissingReserveForBatchRefresh extends Error {
|
|
|
528
531
|
}
|
|
529
532
|
}
|
|
530
533
|
|
|
534
|
+
export class MinWithdrawAmountTooBig extends Error {
|
|
535
|
+
static readonly code = 7044
|
|
536
|
+
readonly code = 7044
|
|
537
|
+
readonly name = "MinWithdrawAmountTooBig"
|
|
538
|
+
readonly msg = "Min withdraw amount is too big"
|
|
539
|
+
|
|
540
|
+
constructor(readonly logs?: string[]) {
|
|
541
|
+
super("7044: Min withdraw amount is too big")
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export class InvestTooSoon extends Error {
|
|
546
|
+
static readonly code = 7045
|
|
547
|
+
readonly code = 7045
|
|
548
|
+
readonly name = "InvestTooSoon"
|
|
549
|
+
readonly msg = "Invest is called too soon after last invest"
|
|
550
|
+
|
|
551
|
+
constructor(readonly logs?: string[]) {
|
|
552
|
+
super("7045: Invest is called too soon after last invest")
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
export class WrongAdminOrAllocationAdmin extends Error {
|
|
557
|
+
static readonly code = 7046
|
|
558
|
+
readonly code = 7046
|
|
559
|
+
readonly name = "WrongAdminOrAllocationAdmin"
|
|
560
|
+
readonly msg = "Wrong admin or allocation admin"
|
|
561
|
+
|
|
562
|
+
constructor(readonly logs?: string[]) {
|
|
563
|
+
super("7046: Wrong admin or allocation admin")
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
531
567
|
export function fromCode(code: number, logs?: string[]): CustomError | null {
|
|
532
568
|
switch (code) {
|
|
533
569
|
case 7000:
|
|
@@ -618,6 +654,12 @@ export function fromCode(code: number, logs?: string[]): CustomError | null {
|
|
|
618
654
|
return new VaultAUMZero(logs)
|
|
619
655
|
case 7043:
|
|
620
656
|
return new MissingReserveForBatchRefresh(logs)
|
|
657
|
+
case 7044:
|
|
658
|
+
return new MinWithdrawAmountTooBig(logs)
|
|
659
|
+
case 7045:
|
|
660
|
+
return new InvestTooSoon(logs)
|
|
661
|
+
case 7046:
|
|
662
|
+
return new WrongAdminOrAllocationAdmin(logs)
|
|
621
663
|
}
|
|
622
664
|
|
|
623
665
|
return null
|
|
@@ -11,6 +11,7 @@ export interface InitVaultAccounts {
|
|
|
11
11
|
tokenVault: PublicKey
|
|
12
12
|
baseTokenMint: PublicKey
|
|
13
13
|
sharesMint: PublicKey
|
|
14
|
+
adminTokenAccount: PublicKey
|
|
14
15
|
systemProgram: PublicKey
|
|
15
16
|
rent: PublicKey
|
|
16
17
|
tokenProgram: PublicKey
|
|
@@ -28,6 +29,7 @@ export function initVault(
|
|
|
28
29
|
{ pubkey: accounts.tokenVault, isSigner: false, isWritable: true },
|
|
29
30
|
{ pubkey: accounts.baseTokenMint, isSigner: false, isWritable: false },
|
|
30
31
|
{ pubkey: accounts.sharesMint, isSigner: false, isWritable: true },
|
|
32
|
+
{ pubkey: accounts.adminTokenAccount, isSigner: false, isWritable: true },
|
|
31
33
|
{ pubkey: accounts.systemProgram, isSigner: false, isWritable: false },
|
|
32
34
|
{ pubkey: accounts.rent, isSigner: false, isWritable: false },
|
|
33
35
|
{ pubkey: accounts.tokenProgram, isSigner: false, isWritable: false },
|
|
@@ -10,7 +10,7 @@ export interface UpdateReserveAllocationArgs {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export interface UpdateReserveAllocationAccounts {
|
|
13
|
-
|
|
13
|
+
signer: PublicKey
|
|
14
14
|
vaultState: PublicKey
|
|
15
15
|
baseVaultAuthority: PublicKey
|
|
16
16
|
reserveCollateralMint: PublicKey
|
|
@@ -29,7 +29,7 @@ export function updateReserveAllocation(
|
|
|
29
29
|
programId: PublicKey = PROGRAM_ID
|
|
30
30
|
) {
|
|
31
31
|
const keys: Array<AccountMeta> = [
|
|
32
|
-
{ pubkey: accounts.
|
|
32
|
+
{ pubkey: accounts.signer, isSigner: true, isWritable: true },
|
|
33
33
|
{ pubkey: accounts.vaultState, isSigner: false, isWritable: true },
|
|
34
34
|
{ pubkey: accounts.baseVaultAuthority, isSigner: false, isWritable: false },
|
|
35
35
|
{
|
|
@@ -256,6 +256,29 @@ export class Farm {
|
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
export interface AllocationAdminJSON {
|
|
260
|
+
kind: "AllocationAdmin"
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export class AllocationAdmin {
|
|
264
|
+
static readonly discriminator = 11
|
|
265
|
+
static readonly kind = "AllocationAdmin"
|
|
266
|
+
readonly discriminator = 11
|
|
267
|
+
readonly kind = "AllocationAdmin"
|
|
268
|
+
|
|
269
|
+
toJSON(): AllocationAdminJSON {
|
|
270
|
+
return {
|
|
271
|
+
kind: "AllocationAdmin",
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
toEncodable() {
|
|
276
|
+
return {
|
|
277
|
+
AllocationAdmin: {},
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
259
282
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
260
283
|
export function fromDecoded(obj: any): types.VaultConfigFieldKind {
|
|
261
284
|
if (typeof obj !== "object") {
|
|
@@ -295,6 +318,9 @@ export function fromDecoded(obj: any): types.VaultConfigFieldKind {
|
|
|
295
318
|
if ("Farm" in obj) {
|
|
296
319
|
return new Farm()
|
|
297
320
|
}
|
|
321
|
+
if ("AllocationAdmin" in obj) {
|
|
322
|
+
return new AllocationAdmin()
|
|
323
|
+
}
|
|
298
324
|
|
|
299
325
|
throw new Error("Invalid enum object")
|
|
300
326
|
}
|
|
@@ -336,6 +362,9 @@ export function fromJSON(
|
|
|
336
362
|
case "Farm": {
|
|
337
363
|
return new Farm()
|
|
338
364
|
}
|
|
365
|
+
case "AllocationAdmin": {
|
|
366
|
+
return new AllocationAdmin()
|
|
367
|
+
}
|
|
339
368
|
}
|
|
340
369
|
}
|
|
341
370
|
|
|
@@ -352,6 +381,7 @@ export function layout(property?: string) {
|
|
|
352
381
|
borsh.struct([], "Name"),
|
|
353
382
|
borsh.struct([], "LookupTable"),
|
|
354
383
|
borsh.struct([], "Farm"),
|
|
384
|
+
borsh.struct([], "AllocationAdmin"),
|
|
355
385
|
])
|
|
356
386
|
if (property !== undefined) {
|
|
357
387
|
return ret.replicate(property)
|
|
@@ -63,6 +63,7 @@ export type VaultConfigFieldKind =
|
|
|
63
63
|
| VaultConfigField.Name
|
|
64
64
|
| VaultConfigField.LookupTable
|
|
65
65
|
| VaultConfigField.Farm
|
|
66
|
+
| VaultConfigField.AllocationAdmin
|
|
66
67
|
export type VaultConfigFieldJSON =
|
|
67
68
|
| VaultConfigField.PerformanceFeeBpsJSON
|
|
68
69
|
| VaultConfigField.ManagementFeeBpsJSON
|
|
@@ -75,6 +76,7 @@ export type VaultConfigFieldJSON =
|
|
|
75
76
|
| VaultConfigField.NameJSON
|
|
76
77
|
| VaultConfigField.LookupTableJSON
|
|
77
78
|
| VaultConfigField.FarmJSON
|
|
79
|
+
| VaultConfigField.AllocationAdminJSON
|
|
78
80
|
|
|
79
81
|
export { VaultAllocation } from "./VaultAllocation"
|
|
80
82
|
export type {
|
|
@@ -35,6 +35,11 @@
|
|
|
35
35
|
"isMut": true,
|
|
36
36
|
"isSigner": false
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
"name": "adminTokenAccount",
|
|
40
|
+
"isMut": true,
|
|
41
|
+
"isSigner": false
|
|
42
|
+
},
|
|
38
43
|
{
|
|
39
44
|
"name": "systemProgram",
|
|
40
45
|
"isMut": false,
|
|
@@ -62,7 +67,7 @@
|
|
|
62
67
|
"name": "updateReserveAllocation",
|
|
63
68
|
"accounts": [
|
|
64
69
|
{
|
|
65
|
-
"name": "
|
|
70
|
+
"name": "signer",
|
|
66
71
|
"isMut": true,
|
|
67
72
|
"isSigner": true
|
|
68
73
|
},
|
|
@@ -996,12 +1001,16 @@
|
|
|
996
1001
|
"name": "padding1",
|
|
997
1002
|
"type": "u64"
|
|
998
1003
|
},
|
|
1004
|
+
{
|
|
1005
|
+
"name": "allocationAdmin",
|
|
1006
|
+
"type": "publicKey"
|
|
1007
|
+
},
|
|
999
1008
|
{
|
|
1000
1009
|
"name": "padding2",
|
|
1001
1010
|
"type": {
|
|
1002
1011
|
"array": [
|
|
1003
1012
|
"u128",
|
|
1004
|
-
|
|
1013
|
+
242
|
|
1005
1014
|
]
|
|
1006
1015
|
}
|
|
1007
1016
|
}
|
|
@@ -1874,6 +1883,9 @@
|
|
|
1874
1883
|
},
|
|
1875
1884
|
{
|
|
1876
1885
|
"name": "Farm"
|
|
1886
|
+
},
|
|
1887
|
+
{
|
|
1888
|
+
"name": "AllocationAdmin"
|
|
1877
1889
|
}
|
|
1878
1890
|
]
|
|
1879
1891
|
}
|
|
@@ -2160,6 +2172,21 @@
|
|
|
2160
2172
|
"code": 7043,
|
|
2161
2173
|
"name": "MissingReserveForBatchRefresh",
|
|
2162
2174
|
"msg": "Missing reserve for batch refresh"
|
|
2175
|
+
},
|
|
2176
|
+
{
|
|
2177
|
+
"code": 7044,
|
|
2178
|
+
"name": "MinWithdrawAmountTooBig",
|
|
2179
|
+
"msg": "Min withdraw amount is too big"
|
|
2180
|
+
},
|
|
2181
|
+
{
|
|
2182
|
+
"code": 7045,
|
|
2183
|
+
"name": "InvestTooSoon",
|
|
2184
|
+
"msg": "Invest is called too soon after last invest"
|
|
2185
|
+
},
|
|
2186
|
+
{
|
|
2187
|
+
"code": 7046,
|
|
2188
|
+
"name": "WrongAdminOrAllocationAdmin",
|
|
2189
|
+
"msg": "Wrong admin or allocation admin"
|
|
2163
2190
|
}
|
|
2164
2191
|
]
|
|
2165
|
-
}
|
|
2192
|
+
}
|
|
@@ -2,6 +2,7 @@ import Decimal from 'decimal.js';
|
|
|
2
2
|
import { KaminoMarket, KaminoObligation, KaminoReserve, numberToLamportsDecimal } from '../classes';
|
|
3
3
|
import { PublicKey } from '@solana/web3.js';
|
|
4
4
|
import { lamportsToDecimal } from '../classes/utils';
|
|
5
|
+
import { MaxWithdrawLtvCheck, getMaxWithdrawLtvCheck } from './repay_with_collateral_operations';
|
|
5
6
|
|
|
6
7
|
export function calcRepayAmountWithSlippage(
|
|
7
8
|
kaminoMarket: KaminoMarket,
|
|
@@ -102,6 +103,7 @@ export function calcMaxWithdrawCollateral(
|
|
|
102
103
|
.filter((p) => !p.reserveAddress.equals(borrow.reserveAddress))
|
|
103
104
|
.reduce((acc, b) => acc.add(b.marketValueRefreshed), new Decimal('0'));
|
|
104
105
|
}
|
|
106
|
+
const maxWithdrawLtvCheck = getMaxWithdrawLtvCheck(obligation);
|
|
105
107
|
|
|
106
108
|
let remainingDepositsValueWithLtv = new Decimal('0');
|
|
107
109
|
if (obligation.getDeposits().length > 1) {
|
|
@@ -109,8 +111,13 @@ export function calcMaxWithdrawCollateral(
|
|
|
109
111
|
.getDeposits()
|
|
110
112
|
.filter((p) => !p.reserveAddress.equals(deposit.reserveAddress))
|
|
111
113
|
.reduce((acc, d) => {
|
|
112
|
-
const { maxLtv } = obligation.getLtvForReserve(
|
|
113
|
-
|
|
114
|
+
const { maxLtv, liquidationLtv } = obligation.getLtvForReserve(
|
|
115
|
+
market,
|
|
116
|
+
market.getReserveByAddress(d.reserveAddress)!
|
|
117
|
+
);
|
|
118
|
+
const maxWithdrawLtv =
|
|
119
|
+
maxWithdrawLtvCheck === MaxWithdrawLtvCheck.LIQUIDATION_THRESHOLD ? liquidationLtv : maxLtv;
|
|
120
|
+
return acc.add(d.marketValueRefreshed.mul(maxWithdrawLtv));
|
|
114
121
|
}, new Decimal('0'));
|
|
115
122
|
}
|
|
116
123
|
|
|
@@ -123,16 +130,18 @@ export function calcMaxWithdrawCollateral(
|
|
|
123
130
|
repayingAllDebt: repayAmountLamports.gte(borrow.amount),
|
|
124
131
|
};
|
|
125
132
|
} else {
|
|
126
|
-
const { maxLtv: collMaxLtv } = obligation.getLtvForReserve(
|
|
133
|
+
const { maxLtv: collMaxLtv, liquidationLtv: collLiquidationLtv } = obligation.getLtvForReserve(
|
|
127
134
|
market,
|
|
128
135
|
market.getReserveByAddress(depositReserve.address)!
|
|
129
136
|
);
|
|
137
|
+
const maxWithdrawLtv =
|
|
138
|
+
maxWithdrawLtvCheck === MaxWithdrawLtvCheck.LIQUIDATION_THRESHOLD ? collLiquidationLtv : collMaxLtv;
|
|
130
139
|
const numerator = deposit.marketValueRefreshed
|
|
131
|
-
.mul(
|
|
140
|
+
.mul(maxWithdrawLtv)
|
|
132
141
|
.add(remainingDepositsValueWithLtv)
|
|
133
142
|
.sub(remainingBorrowsValue);
|
|
134
143
|
|
|
135
|
-
const denominator = depositReserve.getOracleMarketPrice().mul(
|
|
144
|
+
const denominator = depositReserve.getOracleMarketPrice().mul(maxWithdrawLtv);
|
|
136
145
|
const maxCollWithdrawAmount = numerator.div(denominator);
|
|
137
146
|
const withdrawableCollLamports = maxCollWithdrawAmount.mul(depositReserve.getMintFactor()).floor();
|
|
138
147
|
|
|
@@ -56,6 +56,11 @@ interface RepayWithCollSwapInputsProps<QuoteResponse> {
|
|
|
56
56
|
quoter: SwapQuoteProvider<QuoteResponse>;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
export enum MaxWithdrawLtvCheck {
|
|
60
|
+
MAX_LTV,
|
|
61
|
+
LIQUIDATION_THRESHOLD,
|
|
62
|
+
}
|
|
63
|
+
|
|
59
64
|
export async function getRepayWithCollSwapInputs<QuoteResponse>({
|
|
60
65
|
collTokenMint,
|
|
61
66
|
currentSlot,
|
|
@@ -212,8 +217,17 @@ export async function getRepayWithCollIxs<QuoteResponse>({
|
|
|
212
217
|
const { debtRepayAmountLamports, flashRepayAmountLamports, maxCollateralWithdrawLamports, swapQuote } = initialInputs;
|
|
213
218
|
const { inputAmountLamports: collSwapInLamports } = swapInputs;
|
|
214
219
|
|
|
215
|
-
const collReserve = kaminoMarket.getReserveByMint(collTokenMint)
|
|
216
|
-
|
|
220
|
+
const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
|
|
221
|
+
|
|
222
|
+
if (!collReserve) {
|
|
223
|
+
throw new Error(`Collateral reserve with mint ${collTokenMint} not found in market ${kaminoMarket.getAddress()}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
|
|
227
|
+
|
|
228
|
+
if (!debtReserve) {
|
|
229
|
+
throw new Error(`Debt reserve with mint ${debtTokenMint} not found in market ${kaminoMarket.getAddress()}`);
|
|
230
|
+
}
|
|
217
231
|
|
|
218
232
|
// the client should use these values to prevent this input, but the tx may succeed, so we don't want to fail
|
|
219
233
|
// there is also a chance that the tx will consume debt token from the user's ata which they would not expect
|
|
@@ -303,25 +317,48 @@ async function buildRepayWithCollateralIxs(
|
|
|
303
317
|
|
|
304
318
|
const requestElevationGroup = !isClosingPosition && obligation.state.elevationGroup !== 0;
|
|
305
319
|
|
|
320
|
+
const maxWithdrawLtvCheck = getMaxWithdrawLtvCheck(obligation);
|
|
321
|
+
|
|
306
322
|
// 3. Repay using the flash borrowed funds & withdraw collateral to swap and pay the flash loan
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
323
|
+
let repayAndWithdrawAction;
|
|
324
|
+
if (maxWithdrawLtvCheck === MaxWithdrawLtvCheck.MAX_LTV) {
|
|
325
|
+
repayAndWithdrawAction = await KaminoAction.buildRepayAndWithdrawTxns(
|
|
326
|
+
market,
|
|
327
|
+
isClosingPosition ? U64_MAX : debtRepayAmountLamports.toString(),
|
|
328
|
+
debtReserve.getLiquidityMint(),
|
|
329
|
+
isClosingPosition ? U64_MAX : collWithdrawLamports.toString(),
|
|
330
|
+
collReserve.getLiquidityMint(),
|
|
331
|
+
obligation.state.owner,
|
|
332
|
+
currentSlot,
|
|
333
|
+
obligation,
|
|
334
|
+
useV2Ixs,
|
|
335
|
+
0,
|
|
336
|
+
false,
|
|
337
|
+
requestElevationGroup,
|
|
338
|
+
undefined,
|
|
339
|
+
undefined,
|
|
340
|
+
referrer,
|
|
341
|
+
scopeRefresh
|
|
342
|
+
);
|
|
343
|
+
} else {
|
|
344
|
+
repayAndWithdrawAction = await KaminoAction.buildRepayAndWithdrawV2Txns(
|
|
345
|
+
market,
|
|
346
|
+
isClosingPosition ? U64_MAX : debtRepayAmountLamports.toString(),
|
|
347
|
+
debtReserve.getLiquidityMint(),
|
|
348
|
+
isClosingPosition ? U64_MAX : collWithdrawLamports.toString(),
|
|
349
|
+
collReserve.getLiquidityMint(),
|
|
350
|
+
obligation.state.owner,
|
|
351
|
+
currentSlot,
|
|
352
|
+
obligation,
|
|
353
|
+
0,
|
|
354
|
+
false,
|
|
355
|
+
requestElevationGroup,
|
|
356
|
+
undefined,
|
|
357
|
+
undefined,
|
|
358
|
+
referrer,
|
|
359
|
+
scopeRefresh
|
|
360
|
+
);
|
|
361
|
+
}
|
|
325
362
|
|
|
326
363
|
// 4. Swap collateral to debt to repay flash loan
|
|
327
364
|
const { preActionIxs, swapIxs } = swapQuoteIxs;
|
|
@@ -337,3 +374,9 @@ async function buildRepayWithCollateralIxs(
|
|
|
337
374
|
flashRepayIxn,
|
|
338
375
|
];
|
|
339
376
|
}
|
|
377
|
+
|
|
378
|
+
export const getMaxWithdrawLtvCheck = (obligation: KaminoObligation) => {
|
|
379
|
+
return obligation.refreshedStats.userTotalBorrowBorrowFactorAdjusted.gte(obligation.refreshedStats.borrowLimit)
|
|
380
|
+
? MaxWithdrawLtvCheck.LIQUIDATION_THRESHOLD
|
|
381
|
+
: MaxWithdrawLtvCheck.MAX_LTV;
|
|
382
|
+
};
|
package/src/utils/constants.ts
CHANGED
package/src/utils/lookupTable.ts
CHANGED
|
@@ -53,3 +53,65 @@ export const extendLookupTableIxs = (
|
|
|
53
53
|
|
|
54
54
|
return extendLookupIxs;
|
|
55
55
|
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* This method retuns an instruction that creates a lookup table, alongside the pubkey of the lookup table
|
|
59
|
+
* @param payer - the owner of the lookup table
|
|
60
|
+
* @param slot - the current slot
|
|
61
|
+
* @returns - the instruction to create the lookup table and its address
|
|
62
|
+
*/
|
|
63
|
+
export function initLookupTableIx(payer: PublicKey, slot: number): [TransactionInstruction, PublicKey] {
|
|
64
|
+
const [ixn, address] = AddressLookupTableProgram.createLookupTable({
|
|
65
|
+
authority: payer,
|
|
66
|
+
payer,
|
|
67
|
+
recentSlot: slot,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return [ixn, address];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* This method retuns an instruction that deactivates a lookup table, which is needed to close it
|
|
75
|
+
* @param payer - the owner of the lookup table
|
|
76
|
+
* @param lookupTable - the lookup table to deactivate
|
|
77
|
+
* @returns - the instruction to deactivate the lookup table
|
|
78
|
+
*/
|
|
79
|
+
export function deactivateLookupTableIx(payer: PublicKey, lookupTable: PublicKey): TransactionInstruction {
|
|
80
|
+
const ixn = AddressLookupTableProgram.deactivateLookupTable({
|
|
81
|
+
authority: payer,
|
|
82
|
+
lookupTable: lookupTable,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return ixn;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* This method returns an instruction that closes a lookup table. That lookup table needs to be disabled at least 500 blocks before closing it.
|
|
90
|
+
* @param payer - the owner of the lookup table
|
|
91
|
+
* @param lookupTable - the lookup table to close
|
|
92
|
+
* @returns - the instruction to close the lookup table
|
|
93
|
+
*/
|
|
94
|
+
/// this require the LUT to be deactivated at least 500 blocks before
|
|
95
|
+
export function closeLookupTableIx(payer: PublicKey, lookupTable: PublicKey): TransactionInstruction {
|
|
96
|
+
const ixn = AddressLookupTableProgram.closeLookupTable({
|
|
97
|
+
authority: payer,
|
|
98
|
+
recipient: payer,
|
|
99
|
+
lookupTable: lookupTable,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return ixn;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Returns the accounts in a lookup table
|
|
107
|
+
* @param lookupTable - lookup table to get the accounts from
|
|
108
|
+
* @returns - an array of accounts in the lookup table
|
|
109
|
+
*/
|
|
110
|
+
export async function getAccountsInLUT(connection: Connection, lookupTable: PublicKey): Promise<PublicKey[]> {
|
|
111
|
+
const lutState = await connection.getAddressLookupTable(lookupTable);
|
|
112
|
+
if (!lutState || !lutState.value) {
|
|
113
|
+
throw new Error(`Lookup table ${lookupTable} not found`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return lutState.value.state.addresses;
|
|
117
|
+
}
|
package/src/utils/seeds.ts
CHANGED
|
@@ -38,6 +38,10 @@ export const BASE_SEED_REFERRER_STATE = 'ref_state';
|
|
|
38
38
|
* Short url seed
|
|
39
39
|
*/
|
|
40
40
|
export const BASE_SEED_SHORT_URL = 'short_url';
|
|
41
|
+
/**
|
|
42
|
+
* Farm user state seed
|
|
43
|
+
*/
|
|
44
|
+
export const BASE_SEED_USER_STATE = 'user';
|
|
41
45
|
|
|
42
46
|
/**
|
|
43
47
|
* User farm state seed
|
|
@@ -188,9 +192,15 @@ export function shortUrlPda(shortUrl: string, programId: PublicKey = PROGRAM_ID)
|
|
|
188
192
|
return PublicKey.findProgramAddressSync([Buffer.from(BASE_SEED_SHORT_URL), Buffer.from(shortUrl)], programId);
|
|
189
193
|
}
|
|
190
194
|
|
|
191
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Returns the PDA for the obligation farm state
|
|
197
|
+
* @param farm
|
|
198
|
+
* @param obligation
|
|
199
|
+
* @returns pda
|
|
200
|
+
*/
|
|
201
|
+
export function obligationFarmStatePda(farm: PublicKey, obligation: PublicKey) {
|
|
192
202
|
return PublicKey.findProgramAddressSync(
|
|
193
|
-
[Buffer.from(
|
|
194
|
-
|
|
195
|
-
);
|
|
203
|
+
[Buffer.from(BASE_SEED_USER_STATE), farm.toBytes(), obligation.toBytes()],
|
|
204
|
+
farmsId
|
|
205
|
+
)[0];
|
|
196
206
|
}
|