@kamino-finance/klend-sdk 7.3.1 → 7.3.3
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/manager.d.ts +0 -2
- package/dist/classes/manager.d.ts.map +1 -1
- package/dist/classes/manager.js +6 -8
- package/dist/classes/manager.js.map +1 -1
- package/dist/classes/market.d.ts.map +1 -1
- package/dist/classes/market.js +16 -6
- package/dist/classes/market.js.map +1 -1
- package/dist/classes/reserve.d.ts +2 -1
- package/dist/classes/reserve.d.ts.map +1 -1
- package/dist/classes/reserve.js +11 -5
- package/dist/classes/reserve.js.map +1 -1
- package/dist/classes/shared.d.ts +1 -0
- package/dist/classes/shared.d.ts.map +1 -1
- package/dist/classes/shared.js.map +1 -1
- package/dist/classes/vault.d.ts +0 -4
- package/dist/classes/vault.d.ts.map +1 -1
- package/dist/classes/vault.js +8 -264
- package/dist/classes/vault.js.map +1 -1
- package/dist/utils/readCdnData.d.ts +25 -0
- package/dist/utils/readCdnData.d.ts.map +1 -0
- package/dist/utils/readCdnData.js +29 -0
- package/dist/utils/readCdnData.js.map +1 -0
- package/package.json +1 -1
- package/src/classes/manager.ts +7 -23
- package/src/classes/market.ts +24 -6
- package/src/classes/reserve.ts +13 -5
- package/src/classes/shared.ts +1 -0
- package/src/classes/vault.ts +9 -393
- package/src/utils/readCdnData.ts +50 -0
package/src/classes/manager.ts
CHANGED
|
@@ -104,6 +104,7 @@ import type { AccountInfoBase, AccountInfoWithJsonData, AccountInfoWithPubkey }
|
|
|
104
104
|
import { arrayElementConfigItems, ConfigUpdater } from './configItems';
|
|
105
105
|
import { OracleMappings } from '@kamino-finance/scope-sdk/dist/@codegen/scope/accounts';
|
|
106
106
|
import { getReserveFarmRewardsAPY as getReserveFarmRewardsAPYUtils, ReserveIncentives } from '../utils/farmUtils';
|
|
107
|
+
import { fetchKaminoCdnData } from '../utils/readCdnData';
|
|
107
108
|
|
|
108
109
|
const base58Decoder = getBase58Decoder();
|
|
109
110
|
|
|
@@ -585,16 +586,6 @@ export class KaminoManager {
|
|
|
585
586
|
return this._vaultClient.depositIxs(user, vault, tokenAmount, vaultReservesMap, farmState);
|
|
586
587
|
}
|
|
587
588
|
|
|
588
|
-
async buyVaultSharesIxs(
|
|
589
|
-
user: TransactionSigner,
|
|
590
|
-
vault: KaminoVault,
|
|
591
|
-
tokenAmount: Decimal,
|
|
592
|
-
vaultReservesMap?: Map<Address, KaminoReserve>,
|
|
593
|
-
farmState?: FarmState
|
|
594
|
-
): Promise<DepositIxs> {
|
|
595
|
-
return this._vaultClient.buySharesIxs(user, vault, tokenAmount, vaultReservesMap, farmState);
|
|
596
|
-
}
|
|
597
|
-
|
|
598
589
|
/**
|
|
599
590
|
* This function creates instructions to stake the shares in the vault farm if the vault has a farm
|
|
600
591
|
* @param user - user to stake
|
|
@@ -728,17 +719,6 @@ export class KaminoManager {
|
|
|
728
719
|
return this._vaultClient.withdrawIxs(user, vault, shareAmount, slot, vaultReservesMap, farmState);
|
|
729
720
|
}
|
|
730
721
|
|
|
731
|
-
async sellVaultSharesIxs(
|
|
732
|
-
user: TransactionSigner,
|
|
733
|
-
vault: KaminoVault,
|
|
734
|
-
shareAmount: Decimal,
|
|
735
|
-
slot: Slot,
|
|
736
|
-
vaultReservesMap?: Map<Address, KaminoReserve>,
|
|
737
|
-
farmState?: FarmState
|
|
738
|
-
): Promise<WithdrawIxs> {
|
|
739
|
-
return this._vaultClient.sellSharesIxs(user, vault, shareAmount, slot, vaultReservesMap, farmState);
|
|
740
|
-
}
|
|
741
|
-
|
|
742
722
|
/**
|
|
743
723
|
* This method withdraws all the pending fees from the vault to the owner's token ATA
|
|
744
724
|
* @param vault - vault for which the admin withdraws the pending fees
|
|
@@ -901,7 +881,10 @@ export class KaminoManager {
|
|
|
901
881
|
const allReserves = reservePairs.map(([, reserve]) => reserve);
|
|
902
882
|
|
|
903
883
|
// Get all oracle accounts
|
|
904
|
-
const allOracleAccounts = await
|
|
884
|
+
const [allOracleAccounts, cdnResourcesData] = await Promise.all([
|
|
885
|
+
getAllOracleAccounts(this.getRpc(), allReserves),
|
|
886
|
+
fetchKaminoCdnData(),
|
|
887
|
+
]);
|
|
905
888
|
// Group reserves by market
|
|
906
889
|
const marketToReserve = new Map<Address, ReserveWithAddress[]>();
|
|
907
890
|
for (const [reserveAddress, reserveState] of reservePairs) {
|
|
@@ -940,7 +923,8 @@ export class KaminoManager {
|
|
|
940
923
|
state,
|
|
941
924
|
oracle,
|
|
942
925
|
this.getRpc(),
|
|
943
|
-
this.recentSlotDurationMs
|
|
926
|
+
this.recentSlotDurationMs,
|
|
927
|
+
cdnResourcesData
|
|
944
928
|
);
|
|
945
929
|
reservesByAddress.set(kaminoReserve.address, kaminoReserve);
|
|
946
930
|
});
|
package/src/classes/market.ts
CHANGED
|
@@ -56,6 +56,7 @@ import { ObligationZP } from '../@codegen/klend/zero_padding';
|
|
|
56
56
|
import { checkDefined } from '../utils/validations';
|
|
57
57
|
import { Buffer } from 'buffer';
|
|
58
58
|
import { ReserveStatus } from '../@codegen/klend/types';
|
|
59
|
+
import { fetchKaminoCdnData } from '../utils/readCdnData';
|
|
59
60
|
|
|
60
61
|
export type KaminoMarketRpcApi = GetAccountInfoApi &
|
|
61
62
|
GetMultipleAccountsApi &
|
|
@@ -479,7 +480,10 @@ export class KaminoMarket {
|
|
|
479
480
|
state: reserveAccount,
|
|
480
481
|
};
|
|
481
482
|
});
|
|
482
|
-
const reservesAndOracles = await
|
|
483
|
+
const [reservesAndOracles, cdnResourcesData] = await Promise.all([
|
|
484
|
+
getTokenOracleData(this.getRpc(), deserializedReserves, oracleAccounts),
|
|
485
|
+
fetchKaminoCdnData(),
|
|
486
|
+
]);
|
|
483
487
|
const kaminoReserves = new Map<Address, KaminoReserve>();
|
|
484
488
|
reservesAndOracles.forEach(([reserve, oracle], index) => {
|
|
485
489
|
if (!oracle) {
|
|
@@ -494,7 +498,8 @@ export class KaminoMarket {
|
|
|
494
498
|
reserve,
|
|
495
499
|
oracle,
|
|
496
500
|
this.rpc,
|
|
497
|
-
this.recentSlotDurationMs
|
|
501
|
+
this.recentSlotDurationMs,
|
|
502
|
+
cdnResourcesData
|
|
498
503
|
);
|
|
499
504
|
kaminoReserves.set(kaminoReserve.address, kaminoReserve);
|
|
500
505
|
});
|
|
@@ -1712,7 +1717,10 @@ export async function getReservesForMarket(
|
|
|
1712
1717
|
state: reserveAccount,
|
|
1713
1718
|
};
|
|
1714
1719
|
});
|
|
1715
|
-
const reservesAndOracles = await
|
|
1720
|
+
const [reservesAndOracles, cdnResourcesData] = await Promise.all([
|
|
1721
|
+
getTokenOracleData(rpc, deserializedReserves, oracleAccounts),
|
|
1722
|
+
fetchKaminoCdnData(),
|
|
1723
|
+
]);
|
|
1716
1724
|
const reservesByAddress = new Map<Address, KaminoReserve>();
|
|
1717
1725
|
reservesAndOracles.forEach(([reserve, oracle], index) => {
|
|
1718
1726
|
if (reserve.config.status === ReserveStatus.Obsolete.discriminator) {
|
|
@@ -1725,7 +1733,14 @@ export async function getReservesForMarket(
|
|
|
1725
1733
|
}) reserve in market ${reserve.lendingMarket}`
|
|
1726
1734
|
);
|
|
1727
1735
|
}
|
|
1728
|
-
const kaminoReserve = KaminoReserve.initialize(
|
|
1736
|
+
const kaminoReserve = KaminoReserve.initialize(
|
|
1737
|
+
reserves[index].pubkey,
|
|
1738
|
+
reserve,
|
|
1739
|
+
oracle,
|
|
1740
|
+
rpc,
|
|
1741
|
+
recentSlotDurationMs,
|
|
1742
|
+
cdnResourcesData
|
|
1743
|
+
);
|
|
1729
1744
|
reservesByAddress.set(kaminoReserve.address, kaminoReserve);
|
|
1730
1745
|
});
|
|
1731
1746
|
return reservesByAddress;
|
|
@@ -1743,7 +1758,10 @@ export async function getSingleReserve(
|
|
|
1743
1758
|
if (reserve === null) {
|
|
1744
1759
|
throw new Error(`Reserve account ${reservePk} does not exist`);
|
|
1745
1760
|
}
|
|
1746
|
-
const reservesAndOracles = await
|
|
1761
|
+
const [reservesAndOracles, cdnResourcesData] = await Promise.all([
|
|
1762
|
+
getTokenOracleData(rpc, [{ address: reservePk, state: reserve }], oracleAccounts),
|
|
1763
|
+
fetchKaminoCdnData(),
|
|
1764
|
+
]);
|
|
1747
1765
|
const [, oracle] = reservesAndOracles[0];
|
|
1748
1766
|
|
|
1749
1767
|
if (!oracle) {
|
|
@@ -1753,7 +1771,7 @@ export async function getSingleReserve(
|
|
|
1753
1771
|
}`
|
|
1754
1772
|
);
|
|
1755
1773
|
}
|
|
1756
|
-
return KaminoReserve.initialize(reservePk, reserve, oracle, rpc, recentSlotDurationMs);
|
|
1774
|
+
return KaminoReserve.initialize(reservePk, reserve, oracle, rpc, recentSlotDurationMs, cdnResourcesData);
|
|
1757
1775
|
}
|
|
1758
1776
|
|
|
1759
1777
|
export function getReservesActive(reserves: Map<Address, KaminoReserve>): Map<Address, KaminoReserve> {
|
package/src/classes/reserve.ts
CHANGED
|
@@ -57,6 +57,7 @@ import { SYSVAR_RENT_ADDRESS } from '@solana/sysvars';
|
|
|
57
57
|
import { noopSigner } from '../utils/signer';
|
|
58
58
|
import { getRewardPerTimeUnitSecond } from './farm_utils';
|
|
59
59
|
import { Scope, ScopeEntryMetadata } from '@kamino-finance/scope-sdk';
|
|
60
|
+
import { fetchKaminoCdnData, KaminoCdnData } from '../utils/readCdnData';
|
|
60
61
|
|
|
61
62
|
export type KaminoReserveRpcApi = GetProgramAccountsApi & GetAccountInfoApi & GetMultipleAccountsApi;
|
|
62
63
|
|
|
@@ -97,10 +98,11 @@ export class KaminoReserve {
|
|
|
97
98
|
state: Reserve,
|
|
98
99
|
tokenOraclePrice: TokenOracleData,
|
|
99
100
|
rpc: Rpc<KaminoReserveRpcApi>,
|
|
100
|
-
recentSlotDurationMs: number
|
|
101
|
+
recentSlotDurationMs: number,
|
|
102
|
+
cdnResourcesData?: KaminoCdnData
|
|
101
103
|
): KaminoReserve {
|
|
102
104
|
const reserve = new KaminoReserve(state, address, tokenOraclePrice, rpc, recentSlotDurationMs);
|
|
103
|
-
reserve.stats = reserve.formatReserveData(state);
|
|
105
|
+
reserve.stats = reserve.formatReserveData(state, cdnResourcesData?.deprecatedAssets ?? []);
|
|
104
106
|
return reserve;
|
|
105
107
|
}
|
|
106
108
|
|
|
@@ -779,13 +781,16 @@ export class KaminoReserve {
|
|
|
779
781
|
}
|
|
780
782
|
|
|
781
783
|
async load(tokenOraclePrice: TokenOracleData) {
|
|
782
|
-
const parsedData = await
|
|
784
|
+
const [parsedData, cdnResourcesData] = await Promise.all([
|
|
785
|
+
Reserve.fetch(this.rpc, this.address),
|
|
786
|
+
fetchKaminoCdnData(),
|
|
787
|
+
]);
|
|
783
788
|
if (!parsedData) {
|
|
784
789
|
throw Error(`Unable to parse data of reserve ${this.symbol}`);
|
|
785
790
|
}
|
|
786
791
|
this.state = parsedData;
|
|
787
792
|
this.tokenOraclePrice = tokenOraclePrice;
|
|
788
|
-
this.stats = this.formatReserveData(parsedData);
|
|
793
|
+
this.stats = this.formatReserveData(parsedData, cdnResourcesData?.deprecatedAssets ?? []);
|
|
789
794
|
}
|
|
790
795
|
|
|
791
796
|
totalSupplyAPY(currentSlot: Slot) {
|
|
@@ -874,7 +879,7 @@ export class KaminoReserve {
|
|
|
874
879
|
return { apy: aprToApy(apr, 365), apr };
|
|
875
880
|
}
|
|
876
881
|
|
|
877
|
-
private formatReserveData(parsedData: ReserveFields): ReserveDataType {
|
|
882
|
+
private formatReserveData(parsedData: ReserveFields, deprecatedAssets: string[]): ReserveDataType {
|
|
878
883
|
const mintTotalSupply = new Decimal(parsedData.collateral.mintTotalSupply.toString()).div(this.getMintFactor());
|
|
879
884
|
let reserveStatus = ReserveStatus.Active;
|
|
880
885
|
switch (parsedData.config.status) {
|
|
@@ -888,6 +893,8 @@ export class KaminoReserve {
|
|
|
888
893
|
reserveStatus = ReserveStatus.Hidden;
|
|
889
894
|
break;
|
|
890
895
|
}
|
|
896
|
+
const reserveIsUIDeprecated =
|
|
897
|
+
deprecatedAssets.length > 0 ? deprecatedAssets.includes(this.address.toString()) : undefined;
|
|
891
898
|
return {
|
|
892
899
|
// Reserve config
|
|
893
900
|
|
|
@@ -910,6 +917,7 @@ export class KaminoReserve {
|
|
|
910
917
|
depositLimitCrossedTimestamp: parsedData.liquidity.depositLimitCrossedTimestamp.toNumber(),
|
|
911
918
|
borrowLimitCrossedTimestamp: parsedData.liquidity.borrowLimitCrossedTimestamp.toNumber(),
|
|
912
919
|
borrowFactor: parsedData.config.borrowFactorPct.toNumber(),
|
|
920
|
+
isUIDeprecated: reserveIsUIDeprecated,
|
|
913
921
|
};
|
|
914
922
|
}
|
|
915
923
|
|
package/src/classes/shared.ts
CHANGED
package/src/classes/vault.ts
CHANGED
|
@@ -49,9 +49,6 @@ import {
|
|
|
49
49
|
buy,
|
|
50
50
|
BuyAccounts,
|
|
51
51
|
BuyArgs,
|
|
52
|
-
deposit,
|
|
53
|
-
DepositAccounts,
|
|
54
|
-
DepositArgs,
|
|
55
52
|
giveUpPendingFees,
|
|
56
53
|
GiveUpPendingFeesAccounts,
|
|
57
54
|
GiveUpPendingFeesArgs,
|
|
@@ -73,9 +70,6 @@ import {
|
|
|
73
70
|
updateVaultConfig,
|
|
74
71
|
UpdateVaultConfigAccounts,
|
|
75
72
|
UpdateVaultConfigArgs,
|
|
76
|
-
withdraw,
|
|
77
|
-
WithdrawAccounts,
|
|
78
|
-
WithdrawArgs,
|
|
79
73
|
withdrawFromAvailable,
|
|
80
74
|
WithdrawFromAvailableAccounts,
|
|
81
75
|
WithdrawFromAvailableArgs,
|
|
@@ -145,6 +139,7 @@ import { Farms } from '@kamino-finance/farms-sdk';
|
|
|
145
139
|
import { getFarmIncentives } from '@kamino-finance/farms-sdk/dist/utils/apy';
|
|
146
140
|
import { computeReservesAllocation } from '../utils/vaultAllocation';
|
|
147
141
|
import { getReserveFarmRewardsAPY } from '../utils/farmUtils';
|
|
142
|
+
import { fetchKaminoCdnData } from '../utils/readCdnData';
|
|
148
143
|
|
|
149
144
|
export const kaminoVaultId = address('KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd');
|
|
150
145
|
export const kaminoVaultStagingId = address('stKvQfwRsQiKnLtMNVLHKS3exFJmZFsgfzBPWHECUYK');
|
|
@@ -1230,92 +1225,6 @@ export class KaminoVaultClient {
|
|
|
1230
1225
|
]);
|
|
1231
1226
|
createAtasIxs.push(createSharesAtaIxs);
|
|
1232
1227
|
|
|
1233
|
-
const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
|
|
1234
|
-
const depositAccounts: DepositAccounts = {
|
|
1235
|
-
user: user,
|
|
1236
|
-
vaultState: vault.address,
|
|
1237
|
-
tokenVault: vaultState.tokenVault,
|
|
1238
|
-
tokenMint: vaultState.tokenMint,
|
|
1239
|
-
baseVaultAuthority: vaultState.baseVaultAuthority,
|
|
1240
|
-
sharesMint: vaultState.sharesMint,
|
|
1241
|
-
userTokenAta: userTokenAta,
|
|
1242
|
-
userSharesAta: userSharesAta,
|
|
1243
|
-
tokenProgram: tokenProgramID,
|
|
1244
|
-
klendProgram: this._kaminoLendProgramId,
|
|
1245
|
-
sharesTokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
1246
|
-
eventAuthority: eventAuthority,
|
|
1247
|
-
program: this._kaminoVaultProgramId,
|
|
1248
|
-
};
|
|
1249
|
-
|
|
1250
|
-
const depositArgs: DepositArgs = {
|
|
1251
|
-
maxAmount: new BN(
|
|
1252
|
-
numberToLamportsDecimal(tokenAmount, vaultState.tokenMintDecimals.toNumber()).floor().toString()
|
|
1253
|
-
),
|
|
1254
|
-
};
|
|
1255
|
-
|
|
1256
|
-
let depositIx = deposit(depositArgs, depositAccounts, undefined, this._kaminoVaultProgramId);
|
|
1257
|
-
|
|
1258
|
-
const vaultReserves = this.getVaultReserves(vaultState);
|
|
1259
|
-
|
|
1260
|
-
const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
|
|
1261
|
-
depositIx = this.appendRemainingAccountsForVaultReserves(depositIx, vaultReserves, vaultReservesState);
|
|
1262
|
-
|
|
1263
|
-
const depositIxs: DepositIxs = {
|
|
1264
|
-
depositIxs: [...createAtasIxs, depositIx, ...closeAtasIxs],
|
|
1265
|
-
stakeInFarmIfNeededIxs: [],
|
|
1266
|
-
};
|
|
1267
|
-
|
|
1268
|
-
// if there is no farm, we can return the deposit instructions, otherwise include the stake ix in the response
|
|
1269
|
-
if (!(await vault.hasFarm())) {
|
|
1270
|
-
return depositIxs;
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
// if there is a farm, stake the shares
|
|
1274
|
-
const stakeSharesIxs = await this.stakeSharesIxs(user, vault, undefined, farmState);
|
|
1275
|
-
depositIxs.stakeInFarmIfNeededIxs = stakeSharesIxs;
|
|
1276
|
-
return depositIxs;
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
// todo (silviu): after all tx indexing works for buy/sell ixs remove this function and use the buyIx in the deposit function above
|
|
1280
|
-
async buySharesIxs(
|
|
1281
|
-
user: TransactionSigner,
|
|
1282
|
-
vault: KaminoVault,
|
|
1283
|
-
tokenAmount: Decimal,
|
|
1284
|
-
vaultReservesMap?: Map<Address, KaminoReserve>,
|
|
1285
|
-
farmState?: FarmState
|
|
1286
|
-
): Promise<DepositIxs> {
|
|
1287
|
-
const vaultState = await vault.getState();
|
|
1288
|
-
|
|
1289
|
-
const tokenProgramID = vaultState.tokenProgram;
|
|
1290
|
-
const userTokenAta = await getAssociatedTokenAddress(vaultState.tokenMint, user.address, tokenProgramID);
|
|
1291
|
-
const createAtasIxs: Instruction[] = [];
|
|
1292
|
-
const closeAtasIxs: Instruction[] = [];
|
|
1293
|
-
if (vaultState.tokenMint === WRAPPED_SOL_MINT) {
|
|
1294
|
-
const [{ ata: wsolAta, createAtaIx: createWsolAtaIxn }] = await createAtasIdempotent(user, [
|
|
1295
|
-
{
|
|
1296
|
-
mint: WRAPPED_SOL_MINT,
|
|
1297
|
-
tokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
1298
|
-
},
|
|
1299
|
-
]);
|
|
1300
|
-
createAtasIxs.push(createWsolAtaIxn);
|
|
1301
|
-
const transferWsolIxs = getTransferWsolIxs(
|
|
1302
|
-
user,
|
|
1303
|
-
wsolAta,
|
|
1304
|
-
lamports(
|
|
1305
|
-
BigInt(numberToLamportsDecimal(tokenAmount, vaultState.tokenMintDecimals.toNumber()).ceil().toString())
|
|
1306
|
-
)
|
|
1307
|
-
);
|
|
1308
|
-
createAtasIxs.push(...transferWsolIxs);
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
const [{ ata: userSharesAta, createAtaIx: createSharesAtaIxs }] = await createAtasIdempotent(user, [
|
|
1312
|
-
{
|
|
1313
|
-
mint: vaultState.sharesMint,
|
|
1314
|
-
tokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
1315
|
-
},
|
|
1316
|
-
]);
|
|
1317
|
-
createAtasIxs.push(createSharesAtaIxs);
|
|
1318
|
-
|
|
1319
1228
|
const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
|
|
1320
1229
|
const buyAccounts: BuyAccounts = {
|
|
1321
1230
|
user: user,
|
|
@@ -1531,135 +1440,6 @@ export class KaminoVaultClient {
|
|
|
1531
1440
|
return withdrawIxs;
|
|
1532
1441
|
}
|
|
1533
1442
|
|
|
1534
|
-
async sellSharesIxs(
|
|
1535
|
-
user: TransactionSigner,
|
|
1536
|
-
vault: KaminoVault,
|
|
1537
|
-
shareAmountToWithdraw: Decimal,
|
|
1538
|
-
slot: Slot,
|
|
1539
|
-
vaultReservesMap?: Map<Address, KaminoReserve>,
|
|
1540
|
-
farmState?: FarmState
|
|
1541
|
-
): Promise<WithdrawIxs> {
|
|
1542
|
-
const vaultState = await vault.getState();
|
|
1543
|
-
const hasFarm = await vault.hasFarm();
|
|
1544
|
-
|
|
1545
|
-
const withdrawIxs: WithdrawIxs = {
|
|
1546
|
-
unstakeFromFarmIfNeededIxs: [],
|
|
1547
|
-
withdrawIxs: [],
|
|
1548
|
-
postWithdrawIxs: [],
|
|
1549
|
-
};
|
|
1550
|
-
|
|
1551
|
-
// compute the total shares the user has (in ATA + in farm) and check if they want to withdraw everything or just a part
|
|
1552
|
-
let userSharesAtaBalance = new Decimal(0);
|
|
1553
|
-
const userSharesAta = await getAssociatedTokenAddress(vaultState.sharesMint, user.address);
|
|
1554
|
-
const userSharesAtaState = await fetchMaybeToken(this.getConnection(), userSharesAta);
|
|
1555
|
-
if (userSharesAtaState.exists) {
|
|
1556
|
-
const userSharesAtaBalanceInLamports = getTokenBalanceFromAccountInfoLamports(userSharesAtaState);
|
|
1557
|
-
userSharesAtaBalance = userSharesAtaBalanceInLamports.div(
|
|
1558
|
-
new Decimal(10).pow(vaultState.sharesMintDecimals.toString())
|
|
1559
|
-
);
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
|
-
let userSharesInFarm = new Decimal(0);
|
|
1563
|
-
if (hasFarm) {
|
|
1564
|
-
userSharesInFarm = await getUserSharesInTokensStakedInFarm(
|
|
1565
|
-
this.getConnection(),
|
|
1566
|
-
user.address,
|
|
1567
|
-
vaultState.vaultFarm,
|
|
1568
|
-
vaultState.sharesMintDecimals.toNumber()
|
|
1569
|
-
);
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
let sharesToWithdraw = shareAmountToWithdraw;
|
|
1573
|
-
const totalUserShares = userSharesAtaBalance.add(userSharesInFarm);
|
|
1574
|
-
let withdrawAllShares = false;
|
|
1575
|
-
if (sharesToWithdraw.gt(totalUserShares)) {
|
|
1576
|
-
sharesToWithdraw = new Decimal(U64_MAX.toString()).div(
|
|
1577
|
-
new Decimal(10).pow(vaultState.sharesMintDecimals.toString())
|
|
1578
|
-
);
|
|
1579
|
-
withdrawAllShares = true;
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
// if not enough shares in ATA unstake from farm
|
|
1583
|
-
const sharesInAtaAreEnoughForWithdraw = sharesToWithdraw.lte(userSharesAtaBalance);
|
|
1584
|
-
if (hasFarm && !sharesInAtaAreEnoughForWithdraw && userSharesInFarm.gt(0)) {
|
|
1585
|
-
// if we need to unstake we need to make sure share ata is created
|
|
1586
|
-
const [{ createAtaIx }] = await createAtasIdempotent(user, [
|
|
1587
|
-
{
|
|
1588
|
-
mint: vaultState.sharesMint,
|
|
1589
|
-
tokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
1590
|
-
},
|
|
1591
|
-
]);
|
|
1592
|
-
withdrawIxs.unstakeFromFarmIfNeededIxs.push(createAtaIx);
|
|
1593
|
-
let shareLamportsToWithdraw = new Decimal(U64_MAX.toString());
|
|
1594
|
-
if (!withdrawAllShares) {
|
|
1595
|
-
const sharesToWithdrawFromFarm = sharesToWithdraw.sub(userSharesAtaBalance);
|
|
1596
|
-
shareLamportsToWithdraw = collToLamportsDecimal(
|
|
1597
|
-
sharesToWithdrawFromFarm,
|
|
1598
|
-
vaultState.sharesMintDecimals.toNumber()
|
|
1599
|
-
);
|
|
1600
|
-
}
|
|
1601
|
-
const unstakeAndWithdrawFromFarmIxs = await getFarmUnstakeAndWithdrawIxs(
|
|
1602
|
-
this.getConnection(),
|
|
1603
|
-
user,
|
|
1604
|
-
shareLamportsToWithdraw,
|
|
1605
|
-
vaultState.vaultFarm,
|
|
1606
|
-
farmState
|
|
1607
|
-
);
|
|
1608
|
-
withdrawIxs.unstakeFromFarmIfNeededIxs.push(unstakeAndWithdrawFromFarmIxs.unstakeIx);
|
|
1609
|
-
withdrawIxs.unstakeFromFarmIfNeededIxs.push(unstakeAndWithdrawFromFarmIxs.withdrawIx);
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
// if the vault has allocations withdraw otherwise wtihdraw from available ix
|
|
1613
|
-
const vaultAllocation = vaultState.vaultAllocationStrategy.find(
|
|
1614
|
-
(allocation) => allocation.reserve !== DEFAULT_PUBLIC_KEY
|
|
1615
|
-
);
|
|
1616
|
-
|
|
1617
|
-
if (vaultAllocation) {
|
|
1618
|
-
const withdrawFromVaultIxs = await this.sellSharesWithReserveIxs(
|
|
1619
|
-
user,
|
|
1620
|
-
vault,
|
|
1621
|
-
sharesToWithdraw,
|
|
1622
|
-
totalUserShares,
|
|
1623
|
-
slot,
|
|
1624
|
-
vaultReservesMap
|
|
1625
|
-
);
|
|
1626
|
-
withdrawIxs.withdrawIxs = withdrawFromVaultIxs;
|
|
1627
|
-
} else {
|
|
1628
|
-
const withdrawFromVaultIxs = await this.withdrawFromAvailableIxs(user, vault, sharesToWithdraw);
|
|
1629
|
-
withdrawIxs.withdrawIxs = withdrawFromVaultIxs;
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
|
-
// if the vault is for SOL return the ix to unwrap the SOL
|
|
1633
|
-
if (vaultState.tokenMint === WRAPPED_SOL_MINT) {
|
|
1634
|
-
const userWsolAta = await getAssociatedTokenAddress(WRAPPED_SOL_MINT, user.address);
|
|
1635
|
-
const unwrapIx = getCloseAccountInstruction(
|
|
1636
|
-
{
|
|
1637
|
-
account: userWsolAta,
|
|
1638
|
-
owner: user,
|
|
1639
|
-
destination: user.address,
|
|
1640
|
-
},
|
|
1641
|
-
{ programAddress: TOKEN_PROGRAM_ADDRESS }
|
|
1642
|
-
);
|
|
1643
|
-
withdrawIxs.postWithdrawIxs.push(unwrapIx);
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
// if we burn all of user's shares close its shares ATA
|
|
1647
|
-
const burnAllUserShares = sharesToWithdraw.gt(totalUserShares);
|
|
1648
|
-
if (burnAllUserShares) {
|
|
1649
|
-
const closeAtaIx = getCloseAccountInstruction(
|
|
1650
|
-
{
|
|
1651
|
-
account: userSharesAta,
|
|
1652
|
-
owner: user,
|
|
1653
|
-
destination: user.address,
|
|
1654
|
-
},
|
|
1655
|
-
{ programAddress: TOKEN_PROGRAM_ADDRESS }
|
|
1656
|
-
);
|
|
1657
|
-
withdrawIxs.postWithdrawIxs.push(closeAtaIx);
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
return withdrawIxs;
|
|
1661
|
-
}
|
|
1662
|
-
|
|
1663
1443
|
private async withdrawFromAvailableIxs(
|
|
1664
1444
|
user: TransactionSigner,
|
|
1665
1445
|
vault: KaminoVault,
|
|
@@ -1688,115 +1468,6 @@ export class KaminoVaultClient {
|
|
|
1688
1468
|
return [createAtaIx, withdrawFromAvailableIxn];
|
|
1689
1469
|
}
|
|
1690
1470
|
|
|
1691
|
-
private async sellSharesWithReserveIxs(
|
|
1692
|
-
user: TransactionSigner,
|
|
1693
|
-
vault: KaminoVault,
|
|
1694
|
-
shareAmount: Decimal,
|
|
1695
|
-
allUserShares: Decimal,
|
|
1696
|
-
slot: Slot,
|
|
1697
|
-
vaultReservesMap?: Map<Address, KaminoReserve>
|
|
1698
|
-
): Promise<Instruction[]> {
|
|
1699
|
-
const vaultState = await vault.getState();
|
|
1700
|
-
|
|
1701
|
-
const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
|
|
1702
|
-
const userSharesAta = await getAssociatedTokenAddress(vaultState.sharesMint, user.address);
|
|
1703
|
-
const [{ ata: userTokenAta, createAtaIx }] = await createAtasIdempotent(user, [
|
|
1704
|
-
{
|
|
1705
|
-
mint: vaultState.tokenMint,
|
|
1706
|
-
tokenProgram: vaultState.tokenProgram,
|
|
1707
|
-
},
|
|
1708
|
-
]);
|
|
1709
|
-
|
|
1710
|
-
const withdrawAllShares = shareAmount.gte(allUserShares);
|
|
1711
|
-
const actualSharesToWithdraw = shareAmount.lte(allUserShares) ? shareAmount : allUserShares;
|
|
1712
|
-
const shareLamportsToWithdraw = collToLamportsDecimal(
|
|
1713
|
-
actualSharesToWithdraw,
|
|
1714
|
-
vaultState.sharesMintDecimals.toNumber()
|
|
1715
|
-
);
|
|
1716
|
-
const tokensPerShare = await this.getTokensPerShareSingleVault(vault, slot);
|
|
1717
|
-
const sharesPerToken = new Decimal(1).div(tokensPerShare);
|
|
1718
|
-
const tokensToWithdraw = shareLamportsToWithdraw.mul(tokensPerShare);
|
|
1719
|
-
let tokenLeftToWithdraw = tokensToWithdraw;
|
|
1720
|
-
const availableTokens = new Decimal(vaultState.tokenAvailable.toString());
|
|
1721
|
-
tokenLeftToWithdraw = tokenLeftToWithdraw.sub(availableTokens);
|
|
1722
|
-
|
|
1723
|
-
type ReserveWithTokensToWithdraw = { reserve: Address; shares: Decimal };
|
|
1724
|
-
|
|
1725
|
-
const reserveWithSharesAmountToWithdraw: ReserveWithTokensToWithdraw[] = [];
|
|
1726
|
-
let isFirstWithdraw = true;
|
|
1727
|
-
|
|
1728
|
-
if (tokenLeftToWithdraw.lte(0)) {
|
|
1729
|
-
// Availabe enough to withdraw all - using the first existent reserve
|
|
1730
|
-
const firstReserve = vaultState.vaultAllocationStrategy.find((reserve) => reserve.reserve !== DEFAULT_PUBLIC_KEY);
|
|
1731
|
-
if (withdrawAllShares) {
|
|
1732
|
-
reserveWithSharesAmountToWithdraw.push({
|
|
1733
|
-
reserve: firstReserve!.reserve,
|
|
1734
|
-
shares: new Decimal(U64_MAX.toString()),
|
|
1735
|
-
});
|
|
1736
|
-
} else {
|
|
1737
|
-
reserveWithSharesAmountToWithdraw.push({
|
|
1738
|
-
reserve: firstReserve!.reserve,
|
|
1739
|
-
shares: shareLamportsToWithdraw,
|
|
1740
|
-
});
|
|
1741
|
-
}
|
|
1742
|
-
} else {
|
|
1743
|
-
// Get decreasing order sorted available liquidity to withdraw from each reserve allocated to
|
|
1744
|
-
const reserveAllocationAvailableLiquidityToWithdraw = await this.getReserveAllocationAvailableLiquidityToWithdraw(
|
|
1745
|
-
vault,
|
|
1746
|
-
slot,
|
|
1747
|
-
vaultReservesState
|
|
1748
|
-
);
|
|
1749
|
-
// sort
|
|
1750
|
-
const reserveAllocationAvailableLiquidityToWithdrawSorted = [
|
|
1751
|
-
...reserveAllocationAvailableLiquidityToWithdraw.entries(),
|
|
1752
|
-
].sort((a, b) => b[1].sub(a[1]).toNumber());
|
|
1753
|
-
|
|
1754
|
-
reserveAllocationAvailableLiquidityToWithdrawSorted.forEach(([key, availableLiquidityToWithdraw], _) => {
|
|
1755
|
-
if (tokenLeftToWithdraw.gt(0)) {
|
|
1756
|
-
let tokensToWithdrawFromReserve = Decimal.min(tokenLeftToWithdraw, availableLiquidityToWithdraw);
|
|
1757
|
-
if (isFirstWithdraw) {
|
|
1758
|
-
tokensToWithdrawFromReserve = tokensToWithdrawFromReserve.add(availableTokens);
|
|
1759
|
-
isFirstWithdraw = false;
|
|
1760
|
-
}
|
|
1761
|
-
if (withdrawAllShares) {
|
|
1762
|
-
reserveWithSharesAmountToWithdraw.push({ reserve: key, shares: new Decimal(U64_MAX.toString()) });
|
|
1763
|
-
} else {
|
|
1764
|
-
// round up to the nearest integer the shares to withdraw
|
|
1765
|
-
const sharesToWithdrawFromReserve = tokensToWithdrawFromReserve.mul(sharesPerToken).floor();
|
|
1766
|
-
reserveWithSharesAmountToWithdraw.push({ reserve: key, shares: sharesToWithdrawFromReserve });
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
tokenLeftToWithdraw = tokenLeftToWithdraw.sub(tokensToWithdrawFromReserve);
|
|
1770
|
-
}
|
|
1771
|
-
});
|
|
1772
|
-
}
|
|
1773
|
-
|
|
1774
|
-
const withdrawIxs: Instruction[] = [];
|
|
1775
|
-
withdrawIxs.push(createAtaIx);
|
|
1776
|
-
for (let reserveIndex = 0; reserveIndex < reserveWithSharesAmountToWithdraw.length; reserveIndex++) {
|
|
1777
|
-
const reserveWithTokens = reserveWithSharesAmountToWithdraw[reserveIndex];
|
|
1778
|
-
const reserveState = vaultReservesState.get(reserveWithTokens.reserve);
|
|
1779
|
-
if (reserveState === undefined) {
|
|
1780
|
-
throw new Error(`Reserve ${reserveWithTokens.reserve} not found in vault reserves map`);
|
|
1781
|
-
}
|
|
1782
|
-
const marketAddress = reserveState.state.lendingMarket;
|
|
1783
|
-
|
|
1784
|
-
const withdrawFromReserveIx = await this.sellIx(
|
|
1785
|
-
user,
|
|
1786
|
-
vault,
|
|
1787
|
-
vaultState,
|
|
1788
|
-
marketAddress,
|
|
1789
|
-
{ address: reserveWithTokens.reserve, state: reserveState.state },
|
|
1790
|
-
userSharesAta,
|
|
1791
|
-
userTokenAta,
|
|
1792
|
-
reserveWithTokens.shares,
|
|
1793
|
-
vaultReservesState
|
|
1794
|
-
);
|
|
1795
|
-
withdrawIxs.push(withdrawFromReserveIx);
|
|
1796
|
-
}
|
|
1797
|
-
|
|
1798
|
-
return withdrawIxs;
|
|
1799
|
-
}
|
|
1800
1471
|
private async withdrawWithReserveIxs(
|
|
1801
1472
|
user: TransactionSigner,
|
|
1802
1473
|
vault: KaminoVault,
|
|
@@ -1890,7 +1561,7 @@ export class KaminoVaultClient {
|
|
|
1890
1561
|
}
|
|
1891
1562
|
const marketAddress = reserveState.state.lendingMarket;
|
|
1892
1563
|
|
|
1893
|
-
const
|
|
1564
|
+
const sellIx = await this.sellIx(
|
|
1894
1565
|
user,
|
|
1895
1566
|
vault,
|
|
1896
1567
|
vaultState,
|
|
@@ -1901,7 +1572,7 @@ export class KaminoVaultClient {
|
|
|
1901
1572
|
reserveWithTokens.shares,
|
|
1902
1573
|
vaultReservesState
|
|
1903
1574
|
);
|
|
1904
|
-
withdrawIxs.push(
|
|
1575
|
+
withdrawIxs.push(sellIx);
|
|
1905
1576
|
}
|
|
1906
1577
|
|
|
1907
1578
|
return withdrawIxs;
|
|
@@ -2177,65 +1848,6 @@ export class KaminoVaultClient {
|
|
|
2177
1848
|
return sellIxn;
|
|
2178
1849
|
}
|
|
2179
1850
|
|
|
2180
|
-
private async withdrawIx(
|
|
2181
|
-
user: TransactionSigner,
|
|
2182
|
-
vault: KaminoVault,
|
|
2183
|
-
vaultState: VaultState,
|
|
2184
|
-
marketAddress: Address,
|
|
2185
|
-
reserve: ReserveWithAddress,
|
|
2186
|
-
userSharesAta: Address,
|
|
2187
|
-
userTokenAta: Address,
|
|
2188
|
-
shareAmountLamports: Decimal,
|
|
2189
|
-
vaultReservesState: Map<Address, KaminoReserve>
|
|
2190
|
-
): Promise<Instruction> {
|
|
2191
|
-
const [lendingMarketAuth] = await lendingMarketAuthPda(marketAddress, this._kaminoLendProgramId);
|
|
2192
|
-
|
|
2193
|
-
const globalConfig = await getKvaultGlobalConfigPda(this._kaminoVaultProgramId);
|
|
2194
|
-
const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
|
|
2195
|
-
const withdrawAccounts: WithdrawAccounts = {
|
|
2196
|
-
withdrawFromAvailable: {
|
|
2197
|
-
user,
|
|
2198
|
-
vaultState: vault.address,
|
|
2199
|
-
globalConfig: globalConfig,
|
|
2200
|
-
tokenVault: vaultState.tokenVault,
|
|
2201
|
-
baseVaultAuthority: vaultState.baseVaultAuthority,
|
|
2202
|
-
userTokenAta: userTokenAta,
|
|
2203
|
-
tokenMint: vaultState.tokenMint,
|
|
2204
|
-
userSharesAta: userSharesAta,
|
|
2205
|
-
sharesMint: vaultState.sharesMint,
|
|
2206
|
-
tokenProgram: vaultState.tokenProgram,
|
|
2207
|
-
sharesTokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
2208
|
-
klendProgram: this._kaminoLendProgramId,
|
|
2209
|
-
eventAuthority: eventAuthority,
|
|
2210
|
-
program: this._kaminoVaultProgramId,
|
|
2211
|
-
},
|
|
2212
|
-
withdrawFromReserveAccounts: {
|
|
2213
|
-
vaultState: vault.address,
|
|
2214
|
-
reserve: reserve.address,
|
|
2215
|
-
ctokenVault: await getCTokenVaultPda(vault.address, reserve.address, this._kaminoVaultProgramId),
|
|
2216
|
-
lendingMarket: marketAddress,
|
|
2217
|
-
lendingMarketAuthority: lendingMarketAuth,
|
|
2218
|
-
reserveLiquiditySupply: reserve.state.liquidity.supplyVault,
|
|
2219
|
-
reserveCollateralMint: reserve.state.collateral.mintPubkey,
|
|
2220
|
-
reserveCollateralTokenProgram: TOKEN_PROGRAM_ADDRESS,
|
|
2221
|
-
instructionSysvarAccount: SYSVAR_INSTRUCTIONS_ADDRESS,
|
|
2222
|
-
},
|
|
2223
|
-
eventAuthority: eventAuthority,
|
|
2224
|
-
program: this._kaminoVaultProgramId,
|
|
2225
|
-
};
|
|
2226
|
-
|
|
2227
|
-
const withdrawArgs: WithdrawArgs = {
|
|
2228
|
-
sharesAmount: new BN(shareAmountLamports.floor().toString()),
|
|
2229
|
-
};
|
|
2230
|
-
|
|
2231
|
-
let withdrawIxn = withdraw(withdrawArgs, withdrawAccounts, undefined, this._kaminoVaultProgramId);
|
|
2232
|
-
|
|
2233
|
-
const vaultReserves = this.getVaultReserves(vaultState);
|
|
2234
|
-
withdrawIxn = this.appendRemainingAccountsForVaultReserves(withdrawIxn, vaultReserves, vaultReservesState);
|
|
2235
|
-
|
|
2236
|
-
return withdrawIxn;
|
|
2237
|
-
}
|
|
2238
|
-
|
|
2239
1851
|
private async withdrawFromAvailableIx(
|
|
2240
1852
|
user: TransactionSigner,
|
|
2241
1853
|
vault: KaminoVault,
|
|
@@ -2970,7 +2582,10 @@ export class KaminoVaultClient {
|
|
|
2970
2582
|
const deserializedReserves = await batchFetch(vaultReservesAddresses, (chunk) =>
|
|
2971
2583
|
this.loadReserializedReserves(chunk)
|
|
2972
2584
|
);
|
|
2973
|
-
const reservesAndOracles = await
|
|
2585
|
+
const [reservesAndOracles, cdnResourcesData] = await Promise.all([
|
|
2586
|
+
getTokenOracleData(this.getConnection(), deserializedReserves, oracleAccounts),
|
|
2587
|
+
fetchKaminoCdnData(),
|
|
2588
|
+
]);
|
|
2974
2589
|
const kaminoReserves = new Map<Address, KaminoReserve>();
|
|
2975
2590
|
reservesAndOracles.forEach(([reserve, oracle], index) => {
|
|
2976
2591
|
if (!oracle) {
|
|
@@ -2985,7 +2600,8 @@ export class KaminoVaultClient {
|
|
|
2985
2600
|
reserve,
|
|
2986
2601
|
oracle,
|
|
2987
2602
|
this.getConnection(),
|
|
2988
|
-
this.recentSlotDurationMs
|
|
2603
|
+
this.recentSlotDurationMs,
|
|
2604
|
+
cdnResourcesData
|
|
2989
2605
|
);
|
|
2990
2606
|
kaminoReserves.set(kaminoReserve.address, kaminoReserve);
|
|
2991
2607
|
});
|