@hobba-io/core 0.1.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/LICENSE +15 -0
- package/README.md +87 -0
- package/dist/amountUtils.d.ts +15 -0
- package/dist/amountUtils.d.ts.map +1 -0
- package/dist/amountUtils.js +59 -0
- package/dist/amountUtils.js.map +1 -0
- package/dist/constants.d.ts +148 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +231 -0
- package/dist/constants.js.map +1 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +29 -0
- package/dist/errors.js.map +1 -0
- package/dist/executeAction.d.ts +36 -0
- package/dist/executeAction.d.ts.map +1 -0
- package/dist/executeAction.js +606 -0
- package/dist/executeAction.js.map +1 -0
- package/dist/executeDepositBorrow.d.ts +49 -0
- package/dist/executeDepositBorrow.d.ts.map +1 -0
- package/dist/executeDepositBorrow.js +444 -0
- package/dist/executeDepositBorrow.js.map +1 -0
- package/dist/idl/hobba.json +5072 -0
- package/dist/idl.d.ts +783 -0
- package/dist/idl.d.ts.map +1 -0
- package/dist/idl.js +7 -0
- package/dist/idl.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/juplend/accountResolvers.d.ts +106 -0
- package/dist/juplend/accountResolvers.d.ts.map +1 -0
- package/dist/juplend/accountResolvers.js +148 -0
- package/dist/juplend/accountResolvers.js.map +1 -0
- package/dist/juplend/operateRemaining.d.ts +11 -0
- package/dist/juplend/operateRemaining.d.ts.map +1 -0
- package/dist/juplend/operateRemaining.js +46 -0
- package/dist/juplend/operateRemaining.js.map +1 -0
- package/dist/juplend/positionInit.d.ts +34 -0
- package/dist/juplend/positionInit.d.ts.map +1 -0
- package/dist/juplend/positionInit.js +56 -0
- package/dist/juplend/positionInit.js.map +1 -0
- package/dist/juplend/positionParser.d.ts +8 -0
- package/dist/juplend/positionParser.d.ts.map +1 -0
- package/dist/juplend/positionParser.js +30 -0
- package/dist/juplend/positionParser.js.map +1 -0
- package/dist/juplend/sdkApi.d.ts +39 -0
- package/dist/juplend/sdkApi.d.ts.map +1 -0
- package/dist/juplend/sdkApi.js +68 -0
- package/dist/juplend/sdkApi.js.map +1 -0
- package/dist/juplend/tickMath.d.ts +19 -0
- package/dist/juplend/tickMath.d.ts.map +1 -0
- package/dist/juplend/tickMath.js +91 -0
- package/dist/juplend/tickMath.js.map +1 -0
- package/dist/juplend/tickUtils.d.ts +20 -0
- package/dist/juplend/tickUtils.d.ts.map +1 -0
- package/dist/juplend/tickUtils.js +115 -0
- package/dist/juplend/tickUtils.js.map +1 -0
- package/dist/juplend/vaultConfigParser.d.ts +25 -0
- package/dist/juplend/vaultConfigParser.d.ts.map +1 -0
- package/dist/juplend/vaultConfigParser.js +38 -0
- package/dist/juplend/vaultConfigParser.js.map +1 -0
- package/dist/kamino/accountResolvers.d.ts +110 -0
- package/dist/kamino/accountResolvers.d.ts.map +1 -0
- package/dist/kamino/accountResolvers.js +220 -0
- package/dist/kamino/accountResolvers.js.map +1 -0
- package/dist/kamino/obligationParser.d.ts +13 -0
- package/dist/kamino/obligationParser.d.ts.map +1 -0
- package/dist/kamino/obligationParser.js +92 -0
- package/dist/kamino/obligationParser.js.map +1 -0
- package/dist/kamino/refreshInstructions.d.ts +4 -0
- package/dist/kamino/refreshInstructions.d.ts.map +1 -0
- package/dist/kamino/refreshInstructions.js +32 -0
- package/dist/kamino/refreshInstructions.js.map +1 -0
- package/dist/limits.d.ts +87 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +174 -0
- package/dist/limits.js.map +1 -0
- package/dist/marketConfig.d.ts +35 -0
- package/dist/marketConfig.d.ts.map +1 -0
- package/dist/marketConfig.js +85 -0
- package/dist/marketConfig.js.map +1 -0
- package/dist/oracle/scopePrice.d.ts +7 -0
- package/dist/oracle/scopePrice.d.ts.map +1 -0
- package/dist/oracle/scopePrice.js +31 -0
- package/dist/oracle/scopePrice.js.map +1 -0
- package/dist/pdas.d.ts +33 -0
- package/dist/pdas.d.ts.map +1 -0
- package/dist/pdas.js +116 -0
- package/dist/pdas.js.map +1 -0
- package/dist/position.d.ts +45 -0
- package/dist/position.d.ts.map +1 -0
- package/dist/position.js +68 -0
- package/dist/position.js.map +1 -0
- package/dist/positionMetrics.d.ts +108 -0
- package/dist/positionMetrics.d.ts.map +1 -0
- package/dist/positionMetrics.js +238 -0
- package/dist/positionMetrics.js.map +1 -0
- package/dist/program.d.ts +12 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +24 -0
- package/dist/program.js.map +1 -0
- package/dist/quote.d.ts +90 -0
- package/dist/quote.d.ts.map +1 -0
- package/dist/quote.js +198 -0
- package/dist/quote.js.map +1 -0
- package/dist/rates/borrowRates.d.ts +44 -0
- package/dist/rates/borrowRates.d.ts.map +1 -0
- package/dist/rates/borrowRates.js +229 -0
- package/dist/rates/borrowRates.js.map +1 -0
- package/dist/rates/collateralInfo.d.ts +21 -0
- package/dist/rates/collateralInfo.d.ts.map +1 -0
- package/dist/rates/collateralInfo.js +104 -0
- package/dist/rates/collateralInfo.js.map +1 -0
- package/dist/rates/constants.d.ts +22 -0
- package/dist/rates/constants.d.ts.map +1 -0
- package/dist/rates/constants.js +70 -0
- package/dist/rates/constants.js.map +1 -0
- package/dist/rates/index.d.ts +11 -0
- package/dist/rates/index.d.ts.map +1 -0
- package/dist/rates/index.js +11 -0
- package/dist/rates/index.js.map +1 -0
- package/dist/rates/kaminoApi.d.ts +24 -0
- package/dist/rates/kaminoApi.d.ts.map +1 -0
- package/dist/rates/kaminoApi.js +98 -0
- package/dist/rates/kaminoApi.js.map +1 -0
- package/dist/rates/supplyRates.d.ts +23 -0
- package/dist/rates/supplyRates.d.ts.map +1 -0
- package/dist/rates/supplyRates.js +67 -0
- package/dist/rates/supplyRates.js.map +1 -0
- package/dist/transactionBuilder.d.ts +5 -0
- package/dist/transactionBuilder.d.ts.map +1 -0
- package/dist/transactionBuilder.js +287 -0
- package/dist/transactionBuilder.js.map +1 -0
- package/dist/txHelpers.d.ts +16 -0
- package/dist/txHelpers.d.ts.map +1 -0
- package/dist/txHelpers.js +82 -0
- package/dist/txHelpers.js.map +1 -0
- package/dist/userState.d.ts +21 -0
- package/dist/userState.d.ts.map +1 -0
- package/dist/userState.js +60 -0
- package/dist/userState.js.map +1 -0
- package/dist/vault/vaultAccounts.d.ts +82 -0
- package/dist/vault/vaultAccounts.d.ts.map +1 -0
- package/dist/vault/vaultAccounts.js +275 -0
- package/dist/vault/vaultAccounts.js.map +1 -0
- package/dist/wallet.d.ts +12 -0
- package/dist/wallet.d.ts.map +1 -0
- package/dist/wallet.js +2 -0
- package/dist/wallet.js.map +1 -0
- package/package.json +63 -0
- package/src/amountUtils.ts +69 -0
- package/src/constants.ts +263 -0
- package/src/errors.ts +33 -0
- package/src/executeAction.ts +884 -0
- package/src/executeDepositBorrow.ts +677 -0
- package/src/idl/hobba.json +5072 -0
- package/src/idl.ts +7 -0
- package/src/index.ts +21 -0
- package/src/juplend/accountResolvers.ts +207 -0
- package/src/juplend/operateRemaining.ts +81 -0
- package/src/juplend/positionInit.ts +86 -0
- package/src/juplend/positionParser.ts +41 -0
- package/src/juplend/sdkApi.ts +112 -0
- package/src/juplend/tickMath.ts +82 -0
- package/src/juplend/tickUtils.ts +163 -0
- package/src/juplend/vaultConfigParser.ts +45 -0
- package/src/kamino/accountResolvers.ts +299 -0
- package/src/kamino/obligationParser.ts +123 -0
- package/src/kamino/refreshInstructions.ts +45 -0
- package/src/limits.ts +222 -0
- package/src/marketConfig.ts +139 -0
- package/src/oracle/scopePrice.ts +31 -0
- package/src/pdas.ts +226 -0
- package/src/position.ts +131 -0
- package/src/positionMetrics.ts +387 -0
- package/src/program.ts +30 -0
- package/src/quote.ts +310 -0
- package/src/rates/borrowRates.ts +270 -0
- package/src/rates/collateralInfo.ts +136 -0
- package/src/rates/constants.ts +87 -0
- package/src/rates/index.ts +38 -0
- package/src/rates/kaminoApi.ts +134 -0
- package/src/rates/supplyRates.ts +102 -0
- package/src/transactionBuilder.ts +324 -0
- package/src/txHelpers.ts +107 -0
- package/src/userState.ts +91 -0
- package/src/vault/vaultAccounts.ts +365 -0
- package/src/wallet.ts +12 -0
package/src/limits.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { Connection, PublicKey } from "@solana/web3.js";
|
|
2
|
+
import { getAssociatedTokenAddress } from "@solana/spl-token";
|
|
3
|
+
import { SOL_MINT, USDC_MINT } from "./constants";
|
|
4
|
+
import { collateralDecimals, collateralLabel } from "./amountUtils";
|
|
5
|
+
import { getPosition, type Lender } from "./position";
|
|
6
|
+
|
|
7
|
+
/** Protocol input rules, shared by the app, the widget and /api/widget/config. */
|
|
8
|
+
export const LIMITS_CONFIG = {
|
|
9
|
+
/** Users can borrow up to this % of collateral value. */
|
|
10
|
+
maxUserLtvPct: 50,
|
|
11
|
+
/** Minimum deposits in UI units, first-deposit vs top-up. */
|
|
12
|
+
minDepositUi: {
|
|
13
|
+
SOL: { first: 1, topUp: 0.1 },
|
|
14
|
+
cbBTC: { first: 0.001, topUp: 0.0001 },
|
|
15
|
+
} as Record<string, { first: number; topUp: number }>,
|
|
16
|
+
/** SOL held back from "Max" for rents + fees (lamports). */
|
|
17
|
+
solReserveLamports: { first: 50_000_000, topUp: 5_000_000 },
|
|
18
|
+
/** JupLend deposit rounding shave, base units per collateral. */
|
|
19
|
+
juplendDepositBufferBase: { SOL: 3_000_000, cbBTC: 1 } as Record<string, number>,
|
|
20
|
+
/**
|
|
21
|
+
* JupLend max-repay dust shave, micro-USDC. JupLend's operate rejects a
|
|
22
|
+
* repay that leaves 0 < debt < its min-debt floor (error 6025
|
|
23
|
+
* VAULT_USER_DEBT_TOO_LOW): a 0.002 USDC residual was observed failing on
|
|
24
|
+
* staging while ~0.01 passed, so leave 0.02 to clear the floor plus the
|
|
25
|
+
* interest accrued between quote and execution.
|
|
26
|
+
*/
|
|
27
|
+
juplendRepayShaveMicro: 20_000,
|
|
28
|
+
/**
|
|
29
|
+
* klend balance-for-accrued-debt drift divisor applied to max withdraw
|
|
30
|
+
* (observed refresh-time BFAD drift up to ~0.43% on SOL).
|
|
31
|
+
*/
|
|
32
|
+
kaminoWithdrawDriftFactor: 1.006,
|
|
33
|
+
/**
|
|
34
|
+
* JupLend max-withdraw margin. Hobba's 50% user-LTV cap is client-side
|
|
35
|
+
* policy (JupLend's on-chain ceiling is its 75-80% liquidation LTV), so
|
|
36
|
+
* only a hair of headroom is needed for interest accrued between quote
|
|
37
|
+
* and execution — lands ~49.99% instead of stranding 0.6% of the
|
|
38
|
+
* withdrawable amount like the klend factor would.
|
|
39
|
+
*/
|
|
40
|
+
juplendWithdrawDriftFactor: 1.0005,
|
|
41
|
+
/**
|
|
42
|
+
* Collateral reserve for the post-unwind residual on max withdraw when the
|
|
43
|
+
* operator has capital deployed. prepare_withdraw redeems vault shares to
|
|
44
|
+
* repay the operator debt as collateral leaves, but redemption rounding,
|
|
45
|
+
* vault fees and the repay dust leave a small residual — withdrawing ALL
|
|
46
|
+
* collateral against it is infinite LTV and the lender rejects the tx
|
|
47
|
+
* (observed: JupLend 6011 VAULT_POSITION_ABOVE_CF). Reserve enough
|
|
48
|
+
* collateral to hold the residual at ≤ 50% LTV: residual is estimated as
|
|
49
|
+
* max(floorUsd, fracOfOperatorDebt × operator debt).
|
|
50
|
+
*/
|
|
51
|
+
unwindResidualReserve: { floorUsd: 0.1, fracOfOperatorDebt: 0.002 },
|
|
52
|
+
/** Fallback liquidation LTVs (%) when /api/rates is unavailable. */
|
|
53
|
+
fallbackLiquidationLtvPct: {
|
|
54
|
+
kamino: { SOL: 75, cbBTC: 80 },
|
|
55
|
+
juplend: { SOL: 75, cbBTC: 80 },
|
|
56
|
+
} as Record<Lender, Record<string, number>>,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export interface HobbaLimits {
|
|
60
|
+
lender: Lender;
|
|
61
|
+
/** micro-USDC — respects the 50% max user LTV. */
|
|
62
|
+
maxBorrowUsdc: string;
|
|
63
|
+
/** collateral base units — keeps LTV ≤ 50%, drift-adjusted. */
|
|
64
|
+
maxWithdraw: string;
|
|
65
|
+
/** micro-USDC — min(wallet USDC, debt), minus the JupLend dust shave. */
|
|
66
|
+
maxRepayUsdc: string;
|
|
67
|
+
/** collateral base units — first-deposit vs top-up aware. */
|
|
68
|
+
minDeposit: string;
|
|
69
|
+
/** lamports held back from Max when depositing SOL. */
|
|
70
|
+
solReserve: string;
|
|
71
|
+
/** JupLend rounding shave in collateral base units; "0" on Kamino. */
|
|
72
|
+
depositBuffer: string;
|
|
73
|
+
maxUserLtvPct: number;
|
|
74
|
+
/** % — per active lender + collateral. */
|
|
75
|
+
liquidationLtvPct: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function fetchCollateralPriceUsd(base: string, label: string): Promise<number> {
|
|
79
|
+
const route = label === "SOL" ? "sol-price" : "btc-price";
|
|
80
|
+
const res = await fetch(`${base}/api/${route}`);
|
|
81
|
+
if (!res.ok) throw new Error(`${route} fetch failed: ${res.status}`);
|
|
82
|
+
const data = await res.json();
|
|
83
|
+
const price = Number(data.price);
|
|
84
|
+
if (!Number.isFinite(price) || price <= 0) throw new Error(`${route} returned no price`);
|
|
85
|
+
return price;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function fetchLiquidationLtvPct(
|
|
89
|
+
base: string,
|
|
90
|
+
lender: Lender,
|
|
91
|
+
label: string,
|
|
92
|
+
): Promise<number> {
|
|
93
|
+
try {
|
|
94
|
+
const res = await fetch(`${base}/api/rates`);
|
|
95
|
+
if (!res.ok) throw new Error(String(res.status));
|
|
96
|
+
const data = await res.json();
|
|
97
|
+
const table = lender === "juplend" ? data.juplendCollateralLtvs : data.collateralLtvs;
|
|
98
|
+
const pct = Number(table?.[label]?.liquidationThresholdPct);
|
|
99
|
+
if (Number.isFinite(pct) && pct > 0) return pct;
|
|
100
|
+
} catch {
|
|
101
|
+
/* fall through to constants */
|
|
102
|
+
}
|
|
103
|
+
return LIMITS_CONFIG.fallbackLiquidationLtvPct[lender][label] ?? 80;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function fetchTokenBalanceBase(
|
|
107
|
+
connection: Connection,
|
|
108
|
+
owner: PublicKey,
|
|
109
|
+
mint: PublicKey,
|
|
110
|
+
): Promise<number> {
|
|
111
|
+
try {
|
|
112
|
+
const ata = await getAssociatedTokenAddress(mint, owner);
|
|
113
|
+
const bal = await connection.getTokenAccountBalance(ata);
|
|
114
|
+
return Number(bal.value.amount);
|
|
115
|
+
} catch {
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface GetLimitsParams {
|
|
121
|
+
connection: Connection;
|
|
122
|
+
owner: PublicKey;
|
|
123
|
+
collateralMint: PublicKey;
|
|
124
|
+
apiBaseUrl?: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Single quote for input bounds so integrator math can't drift from protocol
|
|
129
|
+
* rules (SOL rent reserves, JupLend rounding shaves, klend accrual drift).
|
|
130
|
+
* Mirrors the app's CalculatorSection / ActionModal / max-withdraw logic.
|
|
131
|
+
*/
|
|
132
|
+
export async function getLimits(params: GetLimitsParams): Promise<HobbaLimits> {
|
|
133
|
+
const { connection, owner, collateralMint } = params;
|
|
134
|
+
const base = (params.apiBaseUrl ?? "").replace(/\/+$/, "");
|
|
135
|
+
const label = collateralLabel(collateralMint);
|
|
136
|
+
const decimals = collateralDecimals(collateralMint);
|
|
137
|
+
const isSol = collateralMint.equals(SOL_MINT);
|
|
138
|
+
|
|
139
|
+
const position = await getPosition({ connection, owner, collateralMint, apiBaseUrl: base });
|
|
140
|
+
const [price, liquidationLtvPct] = await Promise.all([
|
|
141
|
+
fetchCollateralPriceUsd(base, label),
|
|
142
|
+
fetchLiquidationLtvPct(base, position.lender, label),
|
|
143
|
+
]);
|
|
144
|
+
|
|
145
|
+
const collateralBase = Number(position.collateral);
|
|
146
|
+
const userDebtMicro = Number(position.userDebtUsdc);
|
|
147
|
+
const collateralUsd = (collateralBase / 10 ** decimals) * price;
|
|
148
|
+
const maxLtv = LIMITS_CONFIG.maxUserLtvPct / 100;
|
|
149
|
+
|
|
150
|
+
// Max borrow: 50% of collateral value minus current user debt.
|
|
151
|
+
const maxBorrowMicro = Math.max(0, Math.floor(collateralUsd * maxLtv * 1e6 - userDebtMicro));
|
|
152
|
+
|
|
153
|
+
// Max withdraw: keep post-withdraw LTV ≤ 50%. With debt, shave a per-lender
|
|
154
|
+
// drift margin so the quote survives until submission: klend needs real
|
|
155
|
+
// headroom for refresh-time BFAD drift, JupLend only accrual dust. When the
|
|
156
|
+
// operator has capital deployed, additionally reserve collateral for the
|
|
157
|
+
// post-unwind residual (see LIMITS_CONFIG.unwindResidualReserve) so a max
|
|
158
|
+
// withdraw can't strand residual debt against ~zero collateral.
|
|
159
|
+
const operatorDebtUsd = Number(position.depositedToVault) / 1e6;
|
|
160
|
+
const unwindResidualUsd =
|
|
161
|
+
operatorDebtUsd > 0
|
|
162
|
+
? Math.max(
|
|
163
|
+
LIMITS_CONFIG.unwindResidualReserve.floorUsd,
|
|
164
|
+
operatorDebtUsd * LIMITS_CONFIG.unwindResidualReserve.fracOfOperatorDebt,
|
|
165
|
+
)
|
|
166
|
+
: 0;
|
|
167
|
+
const effectiveDebtUsd = userDebtMicro / 1e6 + unwindResidualUsd;
|
|
168
|
+
let maxWithdrawBase: number;
|
|
169
|
+
if (effectiveDebtUsd > 0 && price > 0) {
|
|
170
|
+
const driftFactor =
|
|
171
|
+
position.lender === "juplend"
|
|
172
|
+
? LIMITS_CONFIG.juplendWithdrawDriftFactor
|
|
173
|
+
: LIMITS_CONFIG.kaminoWithdrawDriftFactor;
|
|
174
|
+
const minCollateralUsd = effectiveDebtUsd / maxLtv;
|
|
175
|
+
const maxWithdrawUsd = Math.max(0, collateralUsd - minCollateralUsd);
|
|
176
|
+
maxWithdrawBase = Math.floor(
|
|
177
|
+
((maxWithdrawUsd / price) * 10 ** decimals) / driftFactor,
|
|
178
|
+
);
|
|
179
|
+
maxWithdrawBase = Math.min(maxWithdrawBase, collateralBase);
|
|
180
|
+
} else {
|
|
181
|
+
maxWithdrawBase = collateralBase;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Max repay: wallet USDC capped at debt; JupLend leaves rounding dust.
|
|
185
|
+
const walletUsdcMicro = await fetchTokenBalanceBase(connection, owner, USDC_MINT);
|
|
186
|
+
let maxRepayMicro: number;
|
|
187
|
+
if (position.lender === "juplend") {
|
|
188
|
+
// Cap at debt - shave so the residual clears JupLend's min-debt floor
|
|
189
|
+
// (VAULT_USER_DEBT_TOO_LOW) whether the wallet or the debt is the binding
|
|
190
|
+
// limit; the extra micro off the wallet cap guards balance rounding.
|
|
191
|
+
const targetMicro = Math.max(0, userDebtMicro - LIMITS_CONFIG.juplendRepayShaveMicro);
|
|
192
|
+
maxRepayMicro = Math.max(0, Math.min(walletUsdcMicro - 1, targetMicro));
|
|
193
|
+
} else {
|
|
194
|
+
maxRepayMicro = Math.min(walletUsdcMicro, userDebtMicro);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const minDepositUi = position.exists
|
|
198
|
+
? LIMITS_CONFIG.minDepositUi[label]?.topUp
|
|
199
|
+
: LIMITS_CONFIG.minDepositUi[label]?.first;
|
|
200
|
+
const minDepositBase = Math.round((minDepositUi ?? 0) * 10 ** decimals);
|
|
201
|
+
|
|
202
|
+
const solReserveLamports = isSol
|
|
203
|
+
? position.exists
|
|
204
|
+
? LIMITS_CONFIG.solReserveLamports.topUp
|
|
205
|
+
: LIMITS_CONFIG.solReserveLamports.first
|
|
206
|
+
: 0;
|
|
207
|
+
|
|
208
|
+
const depositBufferBase =
|
|
209
|
+
position.lender === "juplend" ? (LIMITS_CONFIG.juplendDepositBufferBase[label] ?? 0) : 0;
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
lender: position.lender,
|
|
213
|
+
maxBorrowUsdc: String(maxBorrowMicro),
|
|
214
|
+
maxWithdraw: String(maxWithdrawBase),
|
|
215
|
+
maxRepayUsdc: String(maxRepayMicro),
|
|
216
|
+
minDeposit: String(minDepositBase),
|
|
217
|
+
solReserve: String(solReserveLamports),
|
|
218
|
+
depositBuffer: String(depositBufferBase),
|
|
219
|
+
maxUserLtvPct: LIMITS_CONFIG.maxUserLtvPct,
|
|
220
|
+
liquidationLtvPct,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import {
|
|
3
|
+
MAIN_MARKET,
|
|
4
|
+
XSTOCKS_MARKET,
|
|
5
|
+
CBBTC_MINT,
|
|
6
|
+
SOL_MINT,
|
|
7
|
+
CBBTC_RESERVE,
|
|
8
|
+
CBBTC_LIQUIDITY_SUPPLY,
|
|
9
|
+
CBBTC_COLLATERAL_MINT,
|
|
10
|
+
CBBTC_COLLATERAL_SUPPLY,
|
|
11
|
+
CBBTC_RESERVE_FARM_COLLATERAL,
|
|
12
|
+
SOL_RESERVE,
|
|
13
|
+
SOL_LIQUIDITY_SUPPLY,
|
|
14
|
+
SOL_COLLATERAL_MINT,
|
|
15
|
+
SOL_COLLATERAL_SUPPLY,
|
|
16
|
+
SOL_RESERVE_FARM_COLLATERAL,
|
|
17
|
+
USDC_RESERVE,
|
|
18
|
+
USDC_LIQUIDITY_SUPPLY,
|
|
19
|
+
USDC_FEE_VAULT,
|
|
20
|
+
SCOPE_PRICES,
|
|
21
|
+
XSTOCKS_CBBTC_RESERVE,
|
|
22
|
+
XSTOCKS_USDC_RESERVE,
|
|
23
|
+
XSTOCKS_CBBTC_LIQUIDITY_SUPPLY,
|
|
24
|
+
XSTOCKS_CBBTC_COLLATERAL_MINT,
|
|
25
|
+
XSTOCKS_CBBTC_COLLATERAL_SUPPLY,
|
|
26
|
+
XSTOCKS_USDC_LIQUIDITY_SUPPLY,
|
|
27
|
+
XSTOCKS_USDC_FEE_VAULT,
|
|
28
|
+
XSTOCKS_USDC_RESERVE_FARM_DEBT,
|
|
29
|
+
} from "./constants";
|
|
30
|
+
import { deriveMarketAuthority } from "./pdas";
|
|
31
|
+
|
|
32
|
+
export interface KaminoMarketConfig {
|
|
33
|
+
collateralMint: PublicKey;
|
|
34
|
+
market: PublicKey;
|
|
35
|
+
marketAuthority: PublicKey;
|
|
36
|
+
collateralReserve: PublicKey;
|
|
37
|
+
collateralLiquiditySupply: PublicKey;
|
|
38
|
+
collateralCTokenMint: PublicKey;
|
|
39
|
+
collateralCTokenSupply: PublicKey;
|
|
40
|
+
collateralFarmState: PublicKey | null;
|
|
41
|
+
usdcReserve: PublicKey;
|
|
42
|
+
usdcLiquiditySupply: PublicKey;
|
|
43
|
+
usdcFeeVault: PublicKey;
|
|
44
|
+
usdcReserveFarmDebt: PublicKey | null;
|
|
45
|
+
scopePrices: PublicKey;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const [mainMarketAuthority] = deriveMarketAuthority(MAIN_MARKET);
|
|
49
|
+
|
|
50
|
+
export const MAIN_MARKET_CONFIG: KaminoMarketConfig = {
|
|
51
|
+
collateralMint: CBBTC_MINT,
|
|
52
|
+
market: MAIN_MARKET,
|
|
53
|
+
marketAuthority: mainMarketAuthority,
|
|
54
|
+
collateralReserve: CBBTC_RESERVE,
|
|
55
|
+
collateralLiquiditySupply: CBBTC_LIQUIDITY_SUPPLY,
|
|
56
|
+
collateralCTokenMint: CBBTC_COLLATERAL_MINT,
|
|
57
|
+
collateralCTokenSupply: CBBTC_COLLATERAL_SUPPLY,
|
|
58
|
+
collateralFarmState: CBBTC_RESERVE_FARM_COLLATERAL,
|
|
59
|
+
usdcReserve: USDC_RESERVE,
|
|
60
|
+
usdcLiquiditySupply: USDC_LIQUIDITY_SUPPLY,
|
|
61
|
+
usdcFeeVault: USDC_FEE_VAULT,
|
|
62
|
+
usdcReserveFarmDebt: null,
|
|
63
|
+
scopePrices: SCOPE_PRICES,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const SOL_MAIN_MARKET_CONFIG: KaminoMarketConfig = {
|
|
67
|
+
collateralMint: SOL_MINT,
|
|
68
|
+
market: MAIN_MARKET,
|
|
69
|
+
marketAuthority: mainMarketAuthority,
|
|
70
|
+
collateralReserve: SOL_RESERVE,
|
|
71
|
+
collateralLiquiditySupply: SOL_LIQUIDITY_SUPPLY,
|
|
72
|
+
collateralCTokenMint: SOL_COLLATERAL_MINT,
|
|
73
|
+
collateralCTokenSupply: SOL_COLLATERAL_SUPPLY,
|
|
74
|
+
collateralFarmState: SOL_RESERVE_FARM_COLLATERAL,
|
|
75
|
+
usdcReserve: USDC_RESERVE,
|
|
76
|
+
usdcLiquiditySupply: USDC_LIQUIDITY_SUPPLY,
|
|
77
|
+
usdcFeeVault: USDC_FEE_VAULT,
|
|
78
|
+
usdcReserveFarmDebt: null,
|
|
79
|
+
scopePrices: SCOPE_PRICES,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const [xstocksMarketAuthority] = deriveMarketAuthority(XSTOCKS_MARKET);
|
|
83
|
+
|
|
84
|
+
export const XSTOCKS_MARKET_CONFIG: KaminoMarketConfig = {
|
|
85
|
+
collateralMint: CBBTC_MINT,
|
|
86
|
+
market: XSTOCKS_MARKET,
|
|
87
|
+
marketAuthority: xstocksMarketAuthority,
|
|
88
|
+
collateralReserve: XSTOCKS_CBBTC_RESERVE,
|
|
89
|
+
collateralLiquiditySupply: XSTOCKS_CBBTC_LIQUIDITY_SUPPLY,
|
|
90
|
+
collateralCTokenMint: XSTOCKS_CBBTC_COLLATERAL_MINT,
|
|
91
|
+
collateralCTokenSupply: XSTOCKS_CBBTC_COLLATERAL_SUPPLY,
|
|
92
|
+
collateralFarmState: null,
|
|
93
|
+
usdcReserve: XSTOCKS_USDC_RESERVE,
|
|
94
|
+
usdcLiquiditySupply: XSTOCKS_USDC_LIQUIDITY_SUPPLY,
|
|
95
|
+
usdcFeeVault: XSTOCKS_USDC_FEE_VAULT,
|
|
96
|
+
usdcReserveFarmDebt: XSTOCKS_USDC_RESERVE_FARM_DEBT,
|
|
97
|
+
scopePrices: SCOPE_PRICES,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export async function resolveXStocksConfig(): Promise<KaminoMarketConfig> {
|
|
101
|
+
return XSTOCKS_MARKET_CONFIG;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Resolve market config for a given market address.
|
|
106
|
+
* Main Market uses hardcoded addresses; XStocks fetches from mainnet.
|
|
107
|
+
*/
|
|
108
|
+
export async function resolveMarketConfig(
|
|
109
|
+
market: PublicKey
|
|
110
|
+
): Promise<KaminoMarketConfig | null> {
|
|
111
|
+
if (market.equals(MAIN_MARKET)) return MAIN_MARKET_CONFIG;
|
|
112
|
+
if (market.equals(XSTOCKS_MARKET)) return resolveXStocksConfig();
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Resolve market config for a given collateral mint on Main Market.
|
|
118
|
+
*/
|
|
119
|
+
export function resolveMainMarketConfigByMint(
|
|
120
|
+
collateralMint: PublicKey
|
|
121
|
+
): KaminoMarketConfig {
|
|
122
|
+
if (collateralMint.equals(SOL_MINT)) return SOL_MAIN_MARKET_CONFIG;
|
|
123
|
+
return MAIN_MARKET_CONFIG;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Resolve the best Kamino market config based on the rates API response.
|
|
128
|
+
* Returns the config for whichever Kamino market has the lowest borrow APY.
|
|
129
|
+
*/
|
|
130
|
+
export async function resolveBestKaminoConfig(
|
|
131
|
+
bestMarketName: string | null
|
|
132
|
+
): Promise<KaminoMarketConfig> {
|
|
133
|
+
if (bestMarketName === "Kamino xStocks") {
|
|
134
|
+
const xstocks = await resolveXStocksConfig();
|
|
135
|
+
if (xstocks) return xstocks;
|
|
136
|
+
}
|
|
137
|
+
// Default to Main Market
|
|
138
|
+
return MAIN_MARKET_CONFIG;
|
|
139
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kamino Scope oracle price decoding, shared by /api/btc-price and
|
|
3
|
+
* /api/sol-price (and any script needing a Scope-chain USD price).
|
|
4
|
+
* Server-leaning (decodeReserve lazy-requires klend-sdk) — import via the
|
|
5
|
+
* subpath `@hobba/core/oracle/scopePrice`, not core's index.
|
|
6
|
+
*/
|
|
7
|
+
import { decodeReserve } from "../rates/borrowRates";
|
|
8
|
+
|
|
9
|
+
// Scope oracle layout: 8 disc + 32 oracleMappings + 512×56 prices
|
|
10
|
+
export const SCOPE_PRICES_OFFSET = 40; // 8 + 32
|
|
11
|
+
export const SCOPE_ENTRY_SIZE = 56; // DatedPrice: 8 value + 8 exp + ...
|
|
12
|
+
|
|
13
|
+
/** Scope price-chain token indices configured on a klend reserve. */
|
|
14
|
+
export function getReservePriceChain(reserveData: Buffer): number[] {
|
|
15
|
+
const reserve = decodeReserve(reserveData);
|
|
16
|
+
return reserve.config.tokenInfo.scopeConfiguration.priceChain.map((x: any) => Number(x));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Walk the price chain through the Scope prices account, multiplying hops. */
|
|
20
|
+
export function readScopePrice(scopeData: Buffer, priceChain: number[]): number {
|
|
21
|
+
let price = 1.0;
|
|
22
|
+
for (const tokenIndex of priceChain) {
|
|
23
|
+
if (tokenIndex === 65535) break;
|
|
24
|
+
const entryOffset = SCOPE_PRICES_OFFSET + tokenIndex * SCOPE_ENTRY_SIZE;
|
|
25
|
+
if (entryOffset + 16 > scopeData.length) break;
|
|
26
|
+
const value = Number(scopeData.readBigUInt64LE(entryOffset));
|
|
27
|
+
const exp = Number(scopeData.readBigUInt64LE(entryOffset + 8));
|
|
28
|
+
price *= value * Math.pow(10, -exp);
|
|
29
|
+
}
|
|
30
|
+
return price;
|
|
31
|
+
}
|
package/src/pdas.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import { getAssociatedTokenAddress } from "@solana/spl-token";
|
|
3
|
+
import BN from "bn.js";
|
|
4
|
+
import {
|
|
5
|
+
PROGRAM_ID,
|
|
6
|
+
KAMINO_LENDING_PROGRAM_ID,
|
|
7
|
+
KVAULT_PROGRAM_ID,
|
|
8
|
+
FARMS_PROGRAM,
|
|
9
|
+
JUPLEND_PROGRAM,
|
|
10
|
+
METAPLEX_PROGRAM,
|
|
11
|
+
USDC_MINT,
|
|
12
|
+
ALLEZ_USDC_SHARES_MINT,
|
|
13
|
+
USDC_PRIME_SHARES_MINT,
|
|
14
|
+
ROCKAWAY_RWA_SHARES_MINT,
|
|
15
|
+
STAR_USDC_MINT,
|
|
16
|
+
JUPLEND_MAX_TICK,
|
|
17
|
+
} from "./constants";
|
|
18
|
+
|
|
19
|
+
export function deriveUserStatePda(owner: PublicKey, collateralMint: PublicKey): [PublicKey, number] {
|
|
20
|
+
return PublicKey.findProgramAddressSync(
|
|
21
|
+
[Buffer.from("user_state"), owner.toBuffer(), collateralMint.toBuffer()],
|
|
22
|
+
PROGRAM_ID
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function deriveAllowlistEntryPda(owner: PublicKey): [PublicKey, number] {
|
|
27
|
+
return PublicKey.findProgramAddressSync(
|
|
28
|
+
[Buffer.from("allowlist"), owner.toBuffer()],
|
|
29
|
+
PROGRAM_ID
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function deriveProgramConfigPda(): [PublicKey, number] {
|
|
34
|
+
return PublicKey.findProgramAddressSync(
|
|
35
|
+
[Buffer.from("program_config")],
|
|
36
|
+
PROGRAM_ID
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function deriveDepositConfigPda(collateralMint: PublicKey): [PublicKey, number] {
|
|
41
|
+
return PublicKey.findProgramAddressSync(
|
|
42
|
+
[Buffer.from("deposit_config"), collateralMint.toBuffer()],
|
|
43
|
+
PROGRAM_ID
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function deriveOriginConfigPda(origin: number): [PublicKey, number] {
|
|
48
|
+
return PublicKey.findProgramAddressSync(
|
|
49
|
+
[Buffer.from("origin_config"), Buffer.from([origin])],
|
|
50
|
+
PROGRAM_ID
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function deriveUserOriginPda(owner: PublicKey): [PublicKey, number] {
|
|
55
|
+
return PublicKey.findProgramAddressSync(
|
|
56
|
+
[Buffer.from("user_origin"), owner.toBuffer()],
|
|
57
|
+
PROGRAM_ID
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function deriveObligationPda(
|
|
62
|
+
userStatePda: PublicKey,
|
|
63
|
+
market: PublicKey
|
|
64
|
+
): [PublicKey, number] {
|
|
65
|
+
return PublicKey.findProgramAddressSync(
|
|
66
|
+
[
|
|
67
|
+
Buffer.from([0]),
|
|
68
|
+
Buffer.from([0]),
|
|
69
|
+
userStatePda.toBuffer(),
|
|
70
|
+
market.toBuffer(),
|
|
71
|
+
PublicKey.default.toBuffer(),
|
|
72
|
+
PublicKey.default.toBuffer(),
|
|
73
|
+
],
|
|
74
|
+
KAMINO_LENDING_PROGRAM_ID
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function deriveUserMetadataPda(userStatePda: PublicKey): [PublicKey, number] {
|
|
79
|
+
return PublicKey.findProgramAddressSync(
|
|
80
|
+
[Buffer.from("user_meta"), userStatePda.toBuffer()],
|
|
81
|
+
KAMINO_LENDING_PROGRAM_ID
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function deriveMarketAuthority(market: PublicKey): [PublicKey, number] {
|
|
86
|
+
return PublicKey.findProgramAddressSync(
|
|
87
|
+
[Buffer.from("lma"), market.toBuffer()],
|
|
88
|
+
KAMINO_LENDING_PROGRAM_ID
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function deriveFarmUserStatePda(
|
|
93
|
+
farmState: PublicKey,
|
|
94
|
+
obligation: PublicKey
|
|
95
|
+
): [PublicKey, number] {
|
|
96
|
+
return PublicKey.findProgramAddressSync(
|
|
97
|
+
[Buffer.from("user"), farmState.toBuffer(), obligation.toBuffer()],
|
|
98
|
+
FARMS_PROGRAM
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function deriveKvaultEventAuthority(): [PublicKey, number] {
|
|
103
|
+
return PublicKey.findProgramAddressSync(
|
|
104
|
+
[Buffer.from("__event_authority")],
|
|
105
|
+
KVAULT_PROGRAM_ID
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function deriveKvaultGlobalConfig(): [PublicKey, number] {
|
|
110
|
+
return PublicKey.findProgramAddressSync(
|
|
111
|
+
[Buffer.from("global_config")],
|
|
112
|
+
KVAULT_PROGRAM_ID
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// JupLend PDAs
|
|
117
|
+
export function deriveJuplendPositionPda(
|
|
118
|
+
vaultId: number,
|
|
119
|
+
positionId: number
|
|
120
|
+
): [PublicKey, number] {
|
|
121
|
+
const vaultIdBytes = Buffer.alloc(2);
|
|
122
|
+
vaultIdBytes.writeUInt16LE(vaultId);
|
|
123
|
+
const positionIdBytes = Buffer.alloc(4);
|
|
124
|
+
positionIdBytes.writeUInt32LE(positionId);
|
|
125
|
+
return PublicKey.findProgramAddressSync(
|
|
126
|
+
[Buffer.from("position"), vaultIdBytes, positionIdBytes],
|
|
127
|
+
JUPLEND_PROGRAM
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function deriveJuplendPositionMintPda(
|
|
132
|
+
vaultId: number,
|
|
133
|
+
positionId: number
|
|
134
|
+
): [PublicKey, number] {
|
|
135
|
+
const vaultIdBytes = Buffer.alloc(2);
|
|
136
|
+
vaultIdBytes.writeUInt16LE(vaultId);
|
|
137
|
+
const positionIdBytes = Buffer.alloc(4);
|
|
138
|
+
positionIdBytes.writeUInt32LE(positionId);
|
|
139
|
+
return PublicKey.findProgramAddressSync(
|
|
140
|
+
[Buffer.from("position_mint"), vaultIdBytes, positionIdBytes],
|
|
141
|
+
JUPLEND_PROGRAM
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function deriveJuplendTickPda(vaultId: number, tick: number): PublicKey {
|
|
146
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
147
|
+
[
|
|
148
|
+
Buffer.from("tick"),
|
|
149
|
+
new BN(vaultId).toArrayLike(Buffer, "le", 2),
|
|
150
|
+
new BN(tick + JUPLEND_MAX_TICK).toArrayLike(Buffer, "le", 4),
|
|
151
|
+
],
|
|
152
|
+
JUPLEND_PROGRAM
|
|
153
|
+
);
|
|
154
|
+
return pda;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function deriveJuplendTickIdLiquidationPda(
|
|
158
|
+
vaultId: number,
|
|
159
|
+
tick: number,
|
|
160
|
+
totalIds: number = 0
|
|
161
|
+
): PublicKey {
|
|
162
|
+
const [pda] = PublicKey.findProgramAddressSync(
|
|
163
|
+
[
|
|
164
|
+
Buffer.from("tick_id_liquidation"),
|
|
165
|
+
new BN(vaultId).toArrayLike(Buffer, "le", 2),
|
|
166
|
+
new BN(tick + JUPLEND_MAX_TICK).toArrayLike(Buffer, "le", 4),
|
|
167
|
+
new BN(Math.floor((totalIds + 2) / 3)).toArrayLike(Buffer, "le", 4),
|
|
168
|
+
],
|
|
169
|
+
JUPLEND_PROGRAM
|
|
170
|
+
);
|
|
171
|
+
return pda;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function derivePositionMetadataPda(positionMint: PublicKey): [PublicKey, number] {
|
|
175
|
+
return PublicKey.findProgramAddressSync(
|
|
176
|
+
[Buffer.from("metadata"), METAPLEX_PROGRAM.toBuffer(), positionMint.toBuffer()],
|
|
177
|
+
METAPLEX_PROGRAM
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Derive all PDA token accounts for a user
|
|
182
|
+
export async function deriveUserTokenAccounts(
|
|
183
|
+
owner: PublicKey,
|
|
184
|
+
userStatePda: PublicKey,
|
|
185
|
+
collateralMint: PublicKey
|
|
186
|
+
) {
|
|
187
|
+
const [userCollateralAta, pdaCollateralAta, userUsdcAta, pdaUsdcAta, pdaSharesAta, pdaPrimeSharesAta, pdaRockawaySharesAta, pdaPerenaSharesAta] =
|
|
188
|
+
await Promise.all([
|
|
189
|
+
getAssociatedTokenAddress(collateralMint, owner),
|
|
190
|
+
getAssociatedTokenAddress(collateralMint, userStatePda, true),
|
|
191
|
+
getAssociatedTokenAddress(USDC_MINT, owner),
|
|
192
|
+
getAssociatedTokenAddress(USDC_MINT, userStatePda, true),
|
|
193
|
+
getAssociatedTokenAddress(ALLEZ_USDC_SHARES_MINT, userStatePda, true),
|
|
194
|
+
getAssociatedTokenAddress(USDC_PRIME_SHARES_MINT, userStatePda, true),
|
|
195
|
+
getAssociatedTokenAddress(ROCKAWAY_RWA_SHARES_MINT, userStatePda, true),
|
|
196
|
+
getAssociatedTokenAddress(STAR_USDC_MINT, userStatePda, true),
|
|
197
|
+
]);
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
userCollateralAta,
|
|
201
|
+
pdaCollateralAta,
|
|
202
|
+
userUsdcAta,
|
|
203
|
+
pdaUsdcAta,
|
|
204
|
+
pdaSharesAta,
|
|
205
|
+
pdaPrimeSharesAta,
|
|
206
|
+
pdaRockawaySharesAta,
|
|
207
|
+
pdaPerenaSharesAta,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Derive farm PDAs for any market, given its farm addresses
|
|
212
|
+
export function deriveFarmPdas(
|
|
213
|
+
obligationPda: PublicKey,
|
|
214
|
+
collateralFarmState: PublicKey | null,
|
|
215
|
+
usdcReserveFarmDebt: PublicKey | null
|
|
216
|
+
) {
|
|
217
|
+
const obligationFarmUserStatePda = collateralFarmState
|
|
218
|
+
? deriveFarmUserStatePda(collateralFarmState, obligationPda)[0]
|
|
219
|
+
: KAMINO_LENDING_PROGRAM_ID;
|
|
220
|
+
|
|
221
|
+
const obligationFarmDebtUserStatePda = usdcReserveFarmDebt
|
|
222
|
+
? deriveFarmUserStatePda(usdcReserveFarmDebt, obligationPda)[0]
|
|
223
|
+
: KAMINO_LENDING_PROGRAM_ID;
|
|
224
|
+
|
|
225
|
+
return { obligationFarmUserStatePda, obligationFarmDebtUserStatePda };
|
|
226
|
+
}
|