@haven-fi/solauto-sdk 1.0.667 → 1.0.669
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/marginfi-sdk/errors/marginfi.d.ts +209 -23
- package/dist/marginfi-sdk/errors/marginfi.d.ts.map +1 -1
- package/dist/marginfi-sdk/errors/marginfi.js +465 -123
- package/dist/marginfi-sdk/instructions/lendingAccountDeposit.d.ts +3 -1
- package/dist/marginfi-sdk/instructions/lendingAccountDeposit.d.ts.map +1 -1
- package/dist/marginfi-sdk/instructions/lendingAccountDeposit.js +1 -0
- package/dist/services/rebalance/rebalanceTxBuilder.js +1 -1
- package/dist/services/solauto/solautoMarginfiClient.d.ts.map +1 -1
- package/dist/services/solauto/solautoMarginfiClient.js +2 -1
- package/dist/services/transactions/transactionUtils.d.ts.map +1 -1
- package/dist/services/transactions/transactionUtils.js +1 -0
- package/dist/solautoPosition/positionUtils.d.ts.map +1 -1
- package/dist/solautoPosition/solautoPositionEx.d.ts +3 -3
- package/dist/solautoPosition/solautoPositionEx.d.ts.map +1 -1
- package/dist/solautoPosition/solautoPositionEx.js +14 -8
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/marginfi/data.d.ts +41 -0
- package/dist/utils/marginfi/data.d.ts.map +1 -0
- package/dist/utils/{marginfiUtils.js → marginfi/data.js} +8 -145
- package/dist/utils/marginfi/general.d.ts +27 -0
- package/dist/utils/marginfi/general.d.ts.map +1 -0
- package/dist/utils/marginfi/general.js +146 -0
- package/dist/utils/marginfi/index.d.ts +3 -0
- package/dist/utils/marginfi/index.d.ts.map +1 -0
- package/dist/utils/marginfi/index.js +18 -0
- package/dist/utils/solanaUtils.d.ts.map +1 -1
- package/dist/utils/solanaUtils.js +14 -13
- package/dist/utils/solautoUtils.d.ts.map +1 -1
- package/dist/utils/solautoUtils.js +4 -4
- package/local/txSandbox.ts +3 -3
- package/package.json +1 -1
- package/src/marginfi-sdk/errors/marginfi.ts +583 -122
- package/src/marginfi-sdk/instructions/lendingAccountDeposit.ts +7 -0
- package/src/services/rebalance/rebalanceTxBuilder.ts +1 -1
- package/src/services/solauto/solautoMarginfiClient.ts +2 -1
- package/src/services/transactions/transactionUtils.ts +1 -0
- package/src/solautoPosition/positionUtils.ts +0 -1
- package/src/solautoPosition/solautoPositionEx.ts +24 -9
- package/src/utils/index.ts +1 -1
- package/src/utils/{marginfiUtils.ts → marginfi/data.ts} +10 -220
- package/src/utils/marginfi/general.ts +226 -0
- package/src/utils/marginfi/index.ts +2 -0
- package/src/utils/solanaUtils.ts +12 -17
- package/src/utils/solautoUtils.ts +1 -4
- package/tests/transactions/shared.ts +3 -1
- package/tests/transactions/solautoMarginfi.ts +4 -2
- package/dist/utils/marginfiUtils.d.ts +0 -62
- package/dist/utils/marginfiUtils.d.ts.map +0 -1
@@ -8,6 +8,8 @@
|
|
8
8
|
|
9
9
|
import {
|
10
10
|
Context,
|
11
|
+
Option,
|
12
|
+
OptionOrNullable,
|
11
13
|
Pda,
|
12
14
|
PublicKey,
|
13
15
|
Signer,
|
@@ -17,7 +19,9 @@ import {
|
|
17
19
|
import {
|
18
20
|
Serializer,
|
19
21
|
array,
|
22
|
+
bool,
|
20
23
|
mapSerializer,
|
24
|
+
option,
|
21
25
|
struct,
|
22
26
|
u64,
|
23
27
|
u8,
|
@@ -43,10 +47,12 @@ export type LendingAccountDepositInstructionAccounts = {
|
|
43
47
|
export type LendingAccountDepositInstructionData = {
|
44
48
|
discriminator: Array<number>;
|
45
49
|
amount: bigint;
|
50
|
+
depositUpToLimit: Option<boolean>;
|
46
51
|
};
|
47
52
|
|
48
53
|
export type LendingAccountDepositInstructionDataArgs = {
|
49
54
|
amount: number | bigint;
|
55
|
+
depositUpToLimit: OptionOrNullable<boolean>;
|
50
56
|
};
|
51
57
|
|
52
58
|
export function getLendingAccountDepositInstructionDataSerializer(): Serializer<
|
@@ -62,6 +68,7 @@ export function getLendingAccountDepositInstructionDataSerializer(): Serializer<
|
|
62
68
|
[
|
63
69
|
['discriminator', array(u8(), { size: 8 })],
|
64
70
|
['amount', u64()],
|
71
|
+
['depositUpToLimit', option(bool())],
|
65
72
|
],
|
66
73
|
{ description: 'LendingAccountDepositInstructionData' }
|
67
74
|
),
|
@@ -259,7 +259,6 @@ export class SolautoMarginfiClient extends SolautoClient {
|
|
259
259
|
switch (args.__kind) {
|
260
260
|
case "Deposit": {
|
261
261
|
return lendingAccountDeposit(this.umi, {
|
262
|
-
amount: args.fields[0],
|
263
262
|
signer: this.signer,
|
264
263
|
signerTokenAccount: publicKey(this.signerSupplyTa),
|
265
264
|
marginfiAccount: publicKey(this.marginfiAccountPk),
|
@@ -268,6 +267,8 @@ export class SolautoMarginfiClient extends SolautoClient {
|
|
268
267
|
bankLiquidityVault: publicKey(
|
269
268
|
this.marginfiSupplyAccounts.liquidityVault
|
270
269
|
),
|
270
|
+
amount: args.fields[0],
|
271
|
+
depositUpToLimit: true,
|
271
272
|
});
|
272
273
|
}
|
273
274
|
case "Borrow": {
|
@@ -29,7 +29,6 @@ import {
|
|
29
29
|
SolautoPositionEx,
|
30
30
|
} from "./solautoPositionEx";
|
31
31
|
import { MarginfiSolautoPositionEx } from "./marginfiSolautoPositionEx";
|
32
|
-
import { assert } from "console";
|
33
32
|
|
34
33
|
export function createSolautoSettings(
|
35
34
|
settings: SolautoSettingsParametersInpArgs
|
@@ -91,7 +91,7 @@ export abstract class SolautoPositionEx {
|
|
91
91
|
private _supplyPrice?: number;
|
92
92
|
private _debtPrice?: number;
|
93
93
|
|
94
|
-
public
|
94
|
+
public rebalance!: PositionRebalanceHelper;
|
95
95
|
|
96
96
|
constructor(args: PositionExArgs) {
|
97
97
|
this.umi = args.umi;
|
@@ -120,7 +120,7 @@ export abstract class SolautoPositionEx {
|
|
120
120
|
this._data = args.data;
|
121
121
|
this.firstState = { ...args.data.state };
|
122
122
|
|
123
|
-
this.
|
123
|
+
this.rebalance = new PositionRebalanceHelper(this);
|
124
124
|
}
|
125
125
|
|
126
126
|
abstract lendingPool(): Promise<PublicKey>;
|
@@ -288,9 +288,13 @@ export abstract class SolautoPositionEx {
|
|
288
288
|
}
|
289
289
|
|
290
290
|
eligibleForRebalance(
|
291
|
-
bpsDistanceThreshold: number = 0
|
291
|
+
bpsDistanceThreshold: number = 0,
|
292
|
+
skipExtraChecks?: boolean
|
292
293
|
): RebalanceAction | undefined {
|
293
|
-
return this.
|
294
|
+
return this.rebalance.eligibleForRebalance(
|
295
|
+
bpsDistanceThreshold,
|
296
|
+
skipExtraChecks
|
297
|
+
);
|
294
298
|
}
|
295
299
|
|
296
300
|
eligibleForRefresh(): boolean {
|
@@ -457,11 +461,21 @@ class PositionRebalanceHelper {
|
|
457
461
|
}
|
458
462
|
|
459
463
|
private validBoostFromHere() {
|
464
|
+
const realtimeSupplyUsd = this.pos.supplyUsd(PriceType.Realtime);
|
465
|
+
const realtimeDebtUsd = this.pos.debtUsd(PriceType.Realtime);
|
466
|
+
|
467
|
+
if (
|
468
|
+
realtimeSupplyUsd === this.pos.supplyUsd(PriceType.Ema) &&
|
469
|
+
realtimeDebtUsd === this.pos.debtUsd(PriceType.Ema)
|
470
|
+
) {
|
471
|
+
return true;
|
472
|
+
}
|
473
|
+
|
460
474
|
const { debtAdjustmentUsd } = getDebtAdjustment(
|
461
475
|
this.pos.state.liqThresholdBps,
|
462
476
|
{
|
463
|
-
supplyUsd:
|
464
|
-
debtUsd:
|
477
|
+
supplyUsd: realtimeSupplyUsd,
|
478
|
+
debtUsd: realtimeDebtUsd,
|
465
479
|
},
|
466
480
|
this.pos.boostToBps,
|
467
481
|
{ solauto: 25, lpBorrow: 0, flashLoan: 0 } // Undershoot fees
|
@@ -471,7 +485,8 @@ class PositionRebalanceHelper {
|
|
471
485
|
}
|
472
486
|
|
473
487
|
eligibleForRebalance(
|
474
|
-
bpsDistanceThreshold: number
|
488
|
+
bpsDistanceThreshold: number,
|
489
|
+
skipExtraChecks?: boolean
|
475
490
|
): RebalanceAction | undefined {
|
476
491
|
if (!this.pos.settings || !this.pos.supplyUsd()) {
|
477
492
|
return undefined;
|
@@ -488,8 +503,8 @@ class PositionRebalanceHelper {
|
|
488
503
|
return "repay";
|
489
504
|
} else if (
|
490
505
|
realtimeLiqUtilRateBps - this.pos.boostFromBps <= bpsDistanceThreshold &&
|
491
|
-
|
492
|
-
|
506
|
+
(!skipExtraChecks ||
|
507
|
+
(this.validBoostFromHere() && this.sufficientLiquidityToBoost()))
|
493
508
|
) {
|
494
509
|
return "boost";
|
495
510
|
}
|
package/src/utils/index.ts
CHANGED
@@ -3,7 +3,7 @@ export * from "./generalUtils";
|
|
3
3
|
export * from "./instructionUtils";
|
4
4
|
export * from "./jitoUtils";
|
5
5
|
export * from "./jupiterUtils";
|
6
|
-
export * from "./
|
6
|
+
export * from "./marginfi";
|
7
7
|
export * from "./numberUtils";
|
8
8
|
export * from "./solautoUtils";
|
9
9
|
export * from "./solanaUtils";
|
@@ -1,37 +1,28 @@
|
|
1
1
|
import { PublicKey } from "@solana/web3.js";
|
2
|
-
import {
|
2
|
+
import { publicKey, Umi } from "@metaplex-foundation/umi";
|
3
3
|
import {
|
4
|
-
fromWeb3JsPublicKey,
|
5
4
|
toWeb3JsPublicKey,
|
6
5
|
} from "@metaplex-foundation/umi-web3js-adapters";
|
7
|
-
import { ProgramEnv
|
8
|
-
import { PositionState, PositionTokenState, PriceType } from "
|
6
|
+
import { ProgramEnv } from "../../types";
|
7
|
+
import { PositionState, PositionTokenState, PriceType } from "../../generated";
|
9
8
|
import {
|
10
9
|
ALL_SUPPORTED_TOKENS,
|
11
10
|
getMarginfiAccounts,
|
12
|
-
MARGINFI_SPONSORED_SHARD_ID,
|
13
|
-
MarginfiBankAccountsMap,
|
14
|
-
PYTH_SPONSORED_SHARD_ID,
|
15
11
|
TOKEN_INFO,
|
16
12
|
USD_DECIMALS,
|
17
|
-
} from "
|
13
|
+
} from "../../constants";
|
18
14
|
import {
|
19
|
-
Balance,
|
20
15
|
Bank,
|
21
16
|
deserializeMarginfiAccount,
|
22
|
-
fetchBank,
|
23
17
|
getMarginfiAccountSize,
|
24
|
-
getMarginfiErrorFromCode,
|
25
|
-
getMarginfiErrorFromName,
|
26
18
|
MarginfiAccount,
|
27
19
|
OracleSetup,
|
28
|
-
safeFetchAllBank,
|
29
20
|
safeFetchBank,
|
30
21
|
safeFetchMarginfiAccount,
|
31
|
-
} from "
|
32
|
-
import { ContextUpdates } from "
|
33
|
-
import { fetchTokenPrices, safeGetPrice } from "
|
34
|
-
import { currentUnixSeconds, validPubkey } from "
|
22
|
+
} from "../../marginfi-sdk";
|
23
|
+
import { ContextUpdates } from "../solautoUtils";
|
24
|
+
import { fetchTokenPrices, safeGetPrice } from "../priceUtils";
|
25
|
+
import { currentUnixSeconds, validPubkey } from "../generalUtils";
|
35
26
|
import {
|
36
27
|
bytesToI80F48,
|
37
28
|
calcNetWorthUsd,
|
@@ -39,198 +30,8 @@ import {
|
|
39
30
|
getLiqUtilzationRateBps,
|
40
31
|
toBaseUnit,
|
41
32
|
toBps,
|
42
|
-
} from "
|
43
|
-
import {
|
44
|
-
import {
|
45
|
-
getMostUpToDatePythOracle,
|
46
|
-
getPythPushOracleAddress,
|
47
|
-
} from "./pythUtils";
|
48
|
-
import { getAccountMeta } from "./solanaUtils";
|
49
|
-
|
50
|
-
export function createDynamicMarginfiProgram(env?: ProgramEnv): Program {
|
51
|
-
return {
|
52
|
-
name: "marginfi",
|
53
|
-
publicKey: publicKey(getMarginfiAccounts(env ?? "Prod").program),
|
54
|
-
getErrorFromCode(code: number, cause?: Error) {
|
55
|
-
return getMarginfiErrorFromCode(code, this, cause);
|
56
|
-
},
|
57
|
-
getErrorFromName(name: string, cause?: Error) {
|
58
|
-
return getMarginfiErrorFromName(name, this, cause);
|
59
|
-
},
|
60
|
-
isOnCluster() {
|
61
|
-
return true;
|
62
|
-
},
|
63
|
-
};
|
64
|
-
}
|
65
|
-
|
66
|
-
export function umiWithMarginfiProgram(umi: Umi, marginfiEnv?: ProgramEnv) {
|
67
|
-
return umi.use({
|
68
|
-
install(umi) {
|
69
|
-
umi.programs.add(
|
70
|
-
createDynamicMarginfiProgram(marginfiEnv ?? "Prod"),
|
71
|
-
false
|
72
|
-
);
|
73
|
-
},
|
74
|
-
});
|
75
|
-
}
|
76
|
-
|
77
|
-
export async function getAllBankRelatedAccounts(
|
78
|
-
umi: Umi,
|
79
|
-
bankAccountsMap: MarginfiBankAccountsMap
|
80
|
-
): Promise<PublicKey[]> {
|
81
|
-
const banks = Object.values(bankAccountsMap).flatMap((group) =>
|
82
|
-
Object.values(group).map((accounts) => accounts.bank)
|
83
|
-
);
|
84
|
-
const banksData = await safeFetchAllBank(
|
85
|
-
umi,
|
86
|
-
banks.map((x) => publicKey(x))
|
87
|
-
);
|
88
|
-
|
89
|
-
const oracles = banksData
|
90
|
-
.map((bank) => {
|
91
|
-
const oracleKey = toWeb3JsPublicKey(bank.config.oracleKeys[0]);
|
92
|
-
return bank.config.oracleSetup === OracleSetup.PythPushOracle
|
93
|
-
? [
|
94
|
-
getPythPushOracleAddress(oracleKey, PYTH_SPONSORED_SHARD_ID),
|
95
|
-
getPythPushOracleAddress(oracleKey, MARGINFI_SPONSORED_SHARD_ID),
|
96
|
-
]
|
97
|
-
: [oracleKey];
|
98
|
-
})
|
99
|
-
.flat()
|
100
|
-
.map((x) => x.toString());
|
101
|
-
|
102
|
-
const otherAccounts = Object.entries(bankAccountsMap).flatMap(
|
103
|
-
([groupName, tokenMap]) =>
|
104
|
-
Object.values(tokenMap).flatMap((accounts) => [
|
105
|
-
groupName,
|
106
|
-
accounts.liquidityVault,
|
107
|
-
accounts.vaultAuthority,
|
108
|
-
])
|
109
|
-
);
|
110
|
-
|
111
|
-
return Array.from(new Set([...banks, ...oracles, ...otherAccounts]))
|
112
|
-
.filter((x) => x !== PublicKey.default.toString())
|
113
|
-
.map((x) => new PublicKey(x));
|
114
|
-
}
|
115
|
-
|
116
|
-
export async function fetchBankAddresses(umi: Umi, bankPk: PublicKey) {
|
117
|
-
const bank = await fetchBank(umi, fromWeb3JsPublicKey(bankPk));
|
118
|
-
const liquidityVault = toWeb3JsPublicKey(bank!.liquidityVault);
|
119
|
-
const vaultAuthority = (await getTokenAccountData(umi, liquidityVault))
|
120
|
-
?.owner;
|
121
|
-
const priceOracle = await getMarginfiPriceOracle(umi, { data: bank });
|
122
|
-
|
123
|
-
return {
|
124
|
-
bank: bankPk,
|
125
|
-
liquidityVault,
|
126
|
-
vaultAuthority,
|
127
|
-
priceOracle,
|
128
|
-
};
|
129
|
-
}
|
130
|
-
|
131
|
-
export async function getMarginfiPriceOracle(
|
132
|
-
umi: Umi,
|
133
|
-
bank: { pk?: PublicKey; data?: Bank }
|
134
|
-
) {
|
135
|
-
if (!bank.data) {
|
136
|
-
bank.data = await fetchBank(umi, fromWeb3JsPublicKey(bank.pk!));
|
137
|
-
}
|
138
|
-
|
139
|
-
const oracleKey = toWeb3JsPublicKey(bank.data.config.oracleKeys[0]);
|
140
|
-
const priceOracle =
|
141
|
-
bank.data.config.oracleSetup === OracleSetup.PythPushOracle
|
142
|
-
? await getMostUpToDatePythOracle(umi, [
|
143
|
-
getPythPushOracleAddress(oracleKey, PYTH_SPONSORED_SHARD_ID),
|
144
|
-
getPythPushOracleAddress(oracleKey, MARGINFI_SPONSORED_SHARD_ID),
|
145
|
-
])
|
146
|
-
: oracleKey;
|
147
|
-
|
148
|
-
return priceOracle;
|
149
|
-
}
|
150
|
-
|
151
|
-
interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts {
|
152
|
-
mint: PublicKey;
|
153
|
-
}
|
154
|
-
|
155
|
-
export function findMarginfiAccounts(
|
156
|
-
bank: PublicKey
|
157
|
-
): AllMarginfiAssetAccounts {
|
158
|
-
const search = (bankAccounts: MarginfiBankAccountsMap) => {
|
159
|
-
for (const group in bankAccounts) {
|
160
|
-
for (const key in bankAccounts[group]) {
|
161
|
-
const account = bankAccounts[group][key];
|
162
|
-
if (
|
163
|
-
account.bank.toString().toLowerCase() ===
|
164
|
-
bank.toString().toLowerCase()
|
165
|
-
) {
|
166
|
-
return { ...account, mint: new PublicKey(key) };
|
167
|
-
}
|
168
|
-
}
|
169
|
-
}
|
170
|
-
};
|
171
|
-
|
172
|
-
let res = search(getMarginfiAccounts("Prod").bankAccounts);
|
173
|
-
if (res) {
|
174
|
-
return res;
|
175
|
-
}
|
176
|
-
res = search(getMarginfiAccounts("Staging").bankAccounts);
|
177
|
-
if (res) {
|
178
|
-
return res;
|
179
|
-
}
|
180
|
-
|
181
|
-
throw new Error(`Marginfi accounts not found by the bank: ${bank}`);
|
182
|
-
}
|
183
|
-
|
184
|
-
export async function getRemainingAccountsForMarginfiHealthCheck(
|
185
|
-
umi: Umi,
|
186
|
-
balance: Balance
|
187
|
-
): Promise<AccountMeta[]> {
|
188
|
-
if (!balance.active) {
|
189
|
-
return [];
|
190
|
-
}
|
191
|
-
const priceOracle = await getMarginfiPriceOracle(umi, {
|
192
|
-
pk: toWeb3JsPublicKey(balance.bankPk),
|
193
|
-
});
|
194
|
-
return [
|
195
|
-
getAccountMeta(toWeb3JsPublicKey(balance.bankPk)),
|
196
|
-
getAccountMeta(priceOracle),
|
197
|
-
];
|
198
|
-
}
|
199
|
-
|
200
|
-
export function calcMarginfiMaxLtvAndLiqThresholdBps(
|
201
|
-
supplyBank: Bank,
|
202
|
-
debtBank: Bank,
|
203
|
-
supplyPrice: number
|
204
|
-
): [number, number] {
|
205
|
-
let maxLtv =
|
206
|
-
bytesToI80F48(supplyBank.config.assetWeightInit.value) /
|
207
|
-
bytesToI80F48(debtBank.config.liabilityWeightInit.value);
|
208
|
-
const liqThreshold =
|
209
|
-
bytesToI80F48(supplyBank.config.assetWeightMaint.value) /
|
210
|
-
bytesToI80F48(debtBank.config.liabilityWeightMaint.value);
|
211
|
-
|
212
|
-
const totalDepositedUsdValue =
|
213
|
-
fromBaseUnit(
|
214
|
-
BigInt(
|
215
|
-
Math.round(
|
216
|
-
bytesToI80F48(supplyBank.totalAssetShares.value) *
|
217
|
-
bytesToI80F48(supplyBank.assetShareValue.value)
|
218
|
-
)
|
219
|
-
),
|
220
|
-
supplyBank.mintDecimals
|
221
|
-
) * supplyPrice!;
|
222
|
-
if (
|
223
|
-
supplyBank.config.totalAssetValueInitLimit !== BigInt(0) &&
|
224
|
-
totalDepositedUsdValue > supplyBank.config.totalAssetValueInitLimit
|
225
|
-
) {
|
226
|
-
const discount =
|
227
|
-
Number(supplyBank.config.totalAssetValueInitLimit) /
|
228
|
-
totalDepositedUsdValue;
|
229
|
-
maxLtv = maxLtv * Number(discount);
|
230
|
-
}
|
231
|
-
|
232
|
-
return [toBps(maxLtv, "Floor"), toBps(liqThreshold, "Floor")];
|
233
|
-
}
|
33
|
+
} from "../numberUtils";
|
34
|
+
import { calcMarginfiMaxLtvAndLiqThresholdBps, marginfiAccountEmpty } from "./general";
|
234
35
|
|
235
36
|
export async function getMarginfiMaxLtvAndLiqThresholdBps(
|
236
37
|
umi: Umi,
|
@@ -777,14 +578,3 @@ export function getUpToDateShareValues(bank: Bank): [number, number] {
|
|
777
578
|
),
|
778
579
|
];
|
779
580
|
}
|
780
|
-
|
781
|
-
export function marginfiAccountEmpty(marginfiAccount: MarginfiAccount) {
|
782
|
-
return (
|
783
|
-
marginfiAccount.lendingAccount.balances.find(
|
784
|
-
(x) =>
|
785
|
-
x.bankPk.toString() !== PublicKey.default.toString() &&
|
786
|
-
(bytesToI80F48(x.assetShares.value) > 0.000001 ||
|
787
|
-
bytesToI80F48(x.liabilityShares.value) > 0.000001)
|
788
|
-
) === undefined
|
789
|
-
);
|
790
|
-
}
|
@@ -0,0 +1,226 @@
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
2
|
+
import { AccountMeta, Program, publicKey, Umi } from "@metaplex-foundation/umi";
|
3
|
+
import {
|
4
|
+
fromWeb3JsPublicKey,
|
5
|
+
toWeb3JsPublicKey,
|
6
|
+
} from "@metaplex-foundation/umi-web3js-adapters";
|
7
|
+
import { ProgramEnv, MarginfiAssetAccounts } from "../../types";
|
8
|
+
import {
|
9
|
+
getMarginfiAccounts,
|
10
|
+
MARGINFI_SPONSORED_SHARD_ID,
|
11
|
+
MarginfiBankAccountsMap,
|
12
|
+
PYTH_SPONSORED_SHARD_ID,
|
13
|
+
} from "../../constants";
|
14
|
+
import {
|
15
|
+
Balance,
|
16
|
+
Bank,
|
17
|
+
fetchBank,
|
18
|
+
getMarginfiErrorFromCode,
|
19
|
+
getMarginfiErrorFromName,
|
20
|
+
MarginfiAccount,
|
21
|
+
OracleSetup,
|
22
|
+
safeFetchAllBank,
|
23
|
+
} from "../../marginfi-sdk";
|
24
|
+
import { bytesToI80F48, fromBaseUnit, toBps } from "../numberUtils";
|
25
|
+
import { getTokenAccountData } from "../accountUtils";
|
26
|
+
import {
|
27
|
+
getMostUpToDatePythOracle,
|
28
|
+
getPythPushOracleAddress,
|
29
|
+
} from "../pythUtils";
|
30
|
+
import { getAccountMeta } from "../solanaUtils";
|
31
|
+
|
32
|
+
export function createDynamicMarginfiProgram(env?: ProgramEnv): Program {
|
33
|
+
return {
|
34
|
+
name: "marginfi",
|
35
|
+
publicKey: publicKey(getMarginfiAccounts(env ?? "Prod").program),
|
36
|
+
getErrorFromCode(code: number, cause?: Error) {
|
37
|
+
return getMarginfiErrorFromCode(code, this, cause);
|
38
|
+
},
|
39
|
+
getErrorFromName(name: string, cause?: Error) {
|
40
|
+
return getMarginfiErrorFromName(name, this, cause);
|
41
|
+
},
|
42
|
+
isOnCluster() {
|
43
|
+
return true;
|
44
|
+
},
|
45
|
+
};
|
46
|
+
}
|
47
|
+
|
48
|
+
export function umiWithMarginfiProgram(umi: Umi, marginfiEnv?: ProgramEnv) {
|
49
|
+
return umi.use({
|
50
|
+
install(umi) {
|
51
|
+
umi.programs.add(
|
52
|
+
createDynamicMarginfiProgram(marginfiEnv ?? "Prod"),
|
53
|
+
false
|
54
|
+
);
|
55
|
+
},
|
56
|
+
});
|
57
|
+
}
|
58
|
+
|
59
|
+
export async function getAllBankRelatedAccounts(
|
60
|
+
umi: Umi,
|
61
|
+
bankAccountsMap: MarginfiBankAccountsMap
|
62
|
+
): Promise<PublicKey[]> {
|
63
|
+
const banks = Object.values(bankAccountsMap).flatMap((group) =>
|
64
|
+
Object.values(group).map((accounts) => accounts.bank)
|
65
|
+
);
|
66
|
+
const banksData = await safeFetchAllBank(
|
67
|
+
umi,
|
68
|
+
banks.map((x) => publicKey(x))
|
69
|
+
);
|
70
|
+
|
71
|
+
const oracles = banksData
|
72
|
+
.map((bank) => {
|
73
|
+
const oracleKey = toWeb3JsPublicKey(bank.config.oracleKeys[0]);
|
74
|
+
return bank.config.oracleSetup === OracleSetup.PythPushOracle
|
75
|
+
? [
|
76
|
+
getPythPushOracleAddress(oracleKey, PYTH_SPONSORED_SHARD_ID),
|
77
|
+
getPythPushOracleAddress(oracleKey, MARGINFI_SPONSORED_SHARD_ID),
|
78
|
+
]
|
79
|
+
: [oracleKey];
|
80
|
+
})
|
81
|
+
.flat()
|
82
|
+
.map((x) => x.toString());
|
83
|
+
|
84
|
+
const otherAccounts = Object.entries(bankAccountsMap).flatMap(
|
85
|
+
([groupName, tokenMap]) =>
|
86
|
+
Object.values(tokenMap).flatMap((accounts) => [
|
87
|
+
groupName,
|
88
|
+
accounts.liquidityVault,
|
89
|
+
accounts.vaultAuthority,
|
90
|
+
])
|
91
|
+
);
|
92
|
+
|
93
|
+
return Array.from(new Set([...banks, ...oracles, ...otherAccounts]))
|
94
|
+
.filter((x) => x !== PublicKey.default.toString())
|
95
|
+
.map((x) => new PublicKey(x));
|
96
|
+
}
|
97
|
+
|
98
|
+
export async function fetchBankAddresses(umi: Umi, bankPk: PublicKey) {
|
99
|
+
const bank = await fetchBank(umi, fromWeb3JsPublicKey(bankPk));
|
100
|
+
const liquidityVault = toWeb3JsPublicKey(bank!.liquidityVault);
|
101
|
+
const vaultAuthority = (await getTokenAccountData(umi, liquidityVault))
|
102
|
+
?.owner;
|
103
|
+
const priceOracle = await getMarginfiPriceOracle(umi, { data: bank });
|
104
|
+
|
105
|
+
return {
|
106
|
+
bank: bankPk,
|
107
|
+
liquidityVault,
|
108
|
+
vaultAuthority,
|
109
|
+
priceOracle,
|
110
|
+
};
|
111
|
+
}
|
112
|
+
|
113
|
+
export async function getMarginfiPriceOracle(
|
114
|
+
umi: Umi,
|
115
|
+
bank: { pk?: PublicKey; data?: Bank }
|
116
|
+
) {
|
117
|
+
if (!bank.data) {
|
118
|
+
bank.data = await fetchBank(umi, fromWeb3JsPublicKey(bank.pk!));
|
119
|
+
}
|
120
|
+
|
121
|
+
const oracleKey = toWeb3JsPublicKey(bank.data.config.oracleKeys[0]);
|
122
|
+
const priceOracle =
|
123
|
+
bank.data.config.oracleSetup === OracleSetup.PythPushOracle
|
124
|
+
? await getMostUpToDatePythOracle(umi, [
|
125
|
+
getPythPushOracleAddress(oracleKey, PYTH_SPONSORED_SHARD_ID),
|
126
|
+
getPythPushOracleAddress(oracleKey, MARGINFI_SPONSORED_SHARD_ID),
|
127
|
+
])
|
128
|
+
: oracleKey;
|
129
|
+
|
130
|
+
return priceOracle;
|
131
|
+
}
|
132
|
+
|
133
|
+
interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts {
|
134
|
+
mint: PublicKey;
|
135
|
+
}
|
136
|
+
|
137
|
+
export function findMarginfiAccounts(
|
138
|
+
bank: PublicKey
|
139
|
+
): AllMarginfiAssetAccounts {
|
140
|
+
const search = (bankAccounts: MarginfiBankAccountsMap) => {
|
141
|
+
for (const group in bankAccounts) {
|
142
|
+
for (const key in bankAccounts[group]) {
|
143
|
+
const account = bankAccounts[group][key];
|
144
|
+
if (
|
145
|
+
account.bank.toString().toLowerCase() ===
|
146
|
+
bank.toString().toLowerCase()
|
147
|
+
) {
|
148
|
+
return { ...account, mint: new PublicKey(key) };
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
};
|
153
|
+
|
154
|
+
let res = search(getMarginfiAccounts("Prod").bankAccounts);
|
155
|
+
if (res) {
|
156
|
+
return res;
|
157
|
+
}
|
158
|
+
res = search(getMarginfiAccounts("Staging").bankAccounts);
|
159
|
+
if (res) {
|
160
|
+
return res;
|
161
|
+
}
|
162
|
+
|
163
|
+
throw new Error(`Marginfi accounts not found by the bank: ${bank}`);
|
164
|
+
}
|
165
|
+
|
166
|
+
export async function getRemainingAccountsForMarginfiHealthCheck(
|
167
|
+
umi: Umi,
|
168
|
+
balance: Balance
|
169
|
+
): Promise<AccountMeta[]> {
|
170
|
+
if (!balance.active) {
|
171
|
+
return [];
|
172
|
+
}
|
173
|
+
const priceOracle = await getMarginfiPriceOracle(umi, {
|
174
|
+
pk: toWeb3JsPublicKey(balance.bankPk),
|
175
|
+
});
|
176
|
+
return [
|
177
|
+
getAccountMeta(toWeb3JsPublicKey(balance.bankPk)),
|
178
|
+
getAccountMeta(priceOracle),
|
179
|
+
];
|
180
|
+
}
|
181
|
+
|
182
|
+
export function calcMarginfiMaxLtvAndLiqThresholdBps(
|
183
|
+
supplyBank: Bank,
|
184
|
+
debtBank: Bank,
|
185
|
+
supplyPrice: number
|
186
|
+
): [number, number] {
|
187
|
+
let maxLtv =
|
188
|
+
bytesToI80F48(supplyBank.config.assetWeightInit.value) /
|
189
|
+
bytesToI80F48(debtBank.config.liabilityWeightInit.value);
|
190
|
+
const liqThreshold =
|
191
|
+
bytesToI80F48(supplyBank.config.assetWeightMaint.value) /
|
192
|
+
bytesToI80F48(debtBank.config.liabilityWeightMaint.value);
|
193
|
+
|
194
|
+
const totalDepositedUsdValue =
|
195
|
+
fromBaseUnit(
|
196
|
+
BigInt(
|
197
|
+
Math.round(
|
198
|
+
bytesToI80F48(supplyBank.totalAssetShares.value) *
|
199
|
+
bytesToI80F48(supplyBank.assetShareValue.value)
|
200
|
+
)
|
201
|
+
),
|
202
|
+
supplyBank.mintDecimals
|
203
|
+
) * supplyPrice!;
|
204
|
+
if (
|
205
|
+
supplyBank.config.totalAssetValueInitLimit !== BigInt(0) &&
|
206
|
+
totalDepositedUsdValue > supplyBank.config.totalAssetValueInitLimit
|
207
|
+
) {
|
208
|
+
const discount =
|
209
|
+
Number(supplyBank.config.totalAssetValueInitLimit) /
|
210
|
+
totalDepositedUsdValue;
|
211
|
+
maxLtv = maxLtv * Number(discount);
|
212
|
+
}
|
213
|
+
|
214
|
+
return [toBps(maxLtv, "Floor"), toBps(liqThreshold, "Floor")];
|
215
|
+
}
|
216
|
+
|
217
|
+
export function marginfiAccountEmpty(marginfiAccount: MarginfiAccount) {
|
218
|
+
return (
|
219
|
+
marginfiAccount.lendingAccount.balances.find(
|
220
|
+
(x) =>
|
221
|
+
x.bankPk.toString() !== PublicKey.default.toString() &&
|
222
|
+
(bytesToI80F48(x.assetShares.value) > 0.000001 ||
|
223
|
+
bytesToI80F48(x.liabilityShares.value) > 0.000001)
|
224
|
+
) === undefined
|
225
|
+
);
|
226
|
+
}
|