@haven-fi/solauto-sdk 1.0.625 → 1.0.627
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/README.md +115 -0
- package/dist/constants/marginfiAccounts.d.ts +10 -6
- package/dist/constants/marginfiAccounts.d.ts.map +1 -1
- package/dist/constants/marginfiAccounts.js +61 -37
- package/dist/constants/pythConstants.d.ts +4 -0
- package/dist/constants/pythConstants.d.ts.map +1 -1
- package/dist/constants/pythConstants.js +5 -1
- package/dist/services/flashLoans/marginfiFlProvider.d.ts.map +1 -1
- package/dist/services/flashLoans/marginfiFlProvider.js +9 -9
- package/dist/services/solauto/solautoClient.d.ts.map +1 -1
- package/dist/services/solauto/solautoClient.js +6 -6
- package/dist/services/solauto/solautoMarginfiClient.d.ts +2 -1
- package/dist/services/solauto/solautoMarginfiClient.d.ts.map +1 -1
- package/dist/services/solauto/solautoMarginfiClient.js +15 -17
- package/dist/services/transactions/transactionUtils.d.ts.map +1 -1
- package/dist/services/transactions/transactionUtils.js +3 -2
- package/dist/solautoPosition/marginfiSolautoPositionEx.d.ts +5 -0
- package/dist/solautoPosition/marginfiSolautoPositionEx.d.ts.map +1 -1
- package/dist/solautoPosition/marginfiSolautoPositionEx.js +20 -6
- package/dist/solautoPosition/solautoPositionEx.d.ts +5 -1
- package/dist/solautoPosition/solautoPositionEx.d.ts.map +1 -1
- package/dist/solautoPosition/solautoPositionEx.js +11 -4
- package/dist/solautoPosition/utils.js +1 -1
- package/dist/types/accounts.d.ts +0 -1
- package/dist/types/accounts.d.ts.map +1 -1
- package/dist/utils/generalUtils.d.ts +1 -0
- package/dist/utils/generalUtils.d.ts.map +1 -1
- package/dist/utils/generalUtils.js +10 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/marginfiUtils.d.ts +13 -3
- package/dist/utils/marginfiUtils.d.ts.map +1 -1
- package/dist/utils/marginfiUtils.js +78 -20
- package/dist/utils/pythUtils.d.ts +21 -0
- package/dist/utils/pythUtils.d.ts.map +1 -0
- package/dist/utils/pythUtils.js +67 -0
- package/dist/utils/solautoUtils.js +1 -1
- package/local/txSandbox.ts +3 -3
- package/local/updateMarginfiLUT.ts +17 -26
- package/package.json +1 -1
- package/src/constants/marginfiAccounts.ts +80 -37
- package/src/constants/pythConstants.ts +8 -0
- package/src/services/flashLoans/marginfiFlProvider.ts +13 -11
- package/src/services/solauto/solautoClient.ts +7 -6
- package/src/services/solauto/solautoMarginfiClient.ts +18 -26
- package/src/services/transactions/transactionUtils.ts +1 -1
- package/src/solautoPosition/marginfiSolautoPositionEx.ts +28 -9
- package/src/solautoPosition/solautoPositionEx.ts +18 -5
- package/src/solautoPosition/utils.ts +1 -1
- package/src/types/accounts.ts +0 -1
- package/src/utils/generalUtils.ts +12 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/marginfiUtils.ts +123 -30
- package/src/utils/pythUtils.ts +84 -0
- package/src/utils/solautoUtils.ts +1 -1
- package/tests/unit/accounts.ts +7 -13
- package/tests/unit/lookupTables.ts +27 -48
@@ -13,11 +13,15 @@ import {
|
|
13
13
|
fromBaseUnit,
|
14
14
|
getBankLiquidityAvailableBaseUnit,
|
15
15
|
getMarginfiAccountPositionState,
|
16
|
+
getMarginfiPriceOracle,
|
16
17
|
} from "../utils";
|
17
|
-
import {
|
18
|
+
import { getMarginfiAccounts } from "../constants";
|
18
19
|
import { SolautoPositionEx } from "./solautoPositionEx";
|
20
|
+
import { LendingPlatform } from "../generated";
|
19
21
|
|
20
22
|
export class MarginfiSolautoPositionEx extends SolautoPositionEx {
|
23
|
+
lendingPlatform = LendingPlatform.Marginfi;
|
24
|
+
|
21
25
|
private marginfiAccountData: MarginfiAccount | null = null;
|
22
26
|
private supplyBank: Bank | null = null;
|
23
27
|
private debtBank: Bank | null = null;
|
@@ -41,19 +45,18 @@ export class MarginfiSolautoPositionEx extends SolautoPositionEx {
|
|
41
45
|
}
|
42
46
|
|
43
47
|
if (!this.lp) {
|
44
|
-
this.lp =
|
48
|
+
this.lp = getMarginfiAccounts(this.lpEnv).defaultGroup;
|
45
49
|
}
|
46
50
|
|
47
51
|
return this.lp;
|
48
52
|
}
|
49
53
|
|
50
|
-
async
|
54
|
+
async getBanks(): Promise<Bank[]> {
|
51
55
|
if (!this.supplyBank || !this.debtBank) {
|
52
56
|
const group = (await this.lendingPool()).toString();
|
53
|
-
const
|
54
|
-
|
55
|
-
const debtBank =
|
56
|
-
MARGINFI_ACCOUNTS[group][this.debtMint().toString()].bank;
|
57
|
+
const bankAccounts = getMarginfiAccounts(this.lpEnv).bankAccounts;
|
58
|
+
const supplyBank = bankAccounts[group][this.supplyMint().toString()].bank;
|
59
|
+
const debtBank = bankAccounts[group][this.debtMint().toString()].bank;
|
57
60
|
|
58
61
|
[this.supplyBank, this.debtBank] = await safeFetchAllBank(this.umi, [
|
59
62
|
publicKey(supplyBank),
|
@@ -61,10 +64,25 @@ export class MarginfiSolautoPositionEx extends SolautoPositionEx {
|
|
61
64
|
]);
|
62
65
|
}
|
63
66
|
|
67
|
+
return [this.supplyBank!, this.debtBank!];
|
68
|
+
}
|
69
|
+
|
70
|
+
async priceOracles(): Promise<PublicKey[]> {
|
71
|
+
const [supplyBank, debtBank] = await this.getBanks();
|
72
|
+
|
73
|
+
return await Promise.all([
|
74
|
+
getMarginfiPriceOracle(this.umi, { data: supplyBank }),
|
75
|
+
getMarginfiPriceOracle(this.umi, { data: debtBank }),
|
76
|
+
]);
|
77
|
+
}
|
78
|
+
|
79
|
+
async maxLtvAndLiqThresholdBps(): Promise<[number, number]> {
|
80
|
+
const [supplyBank, debtBank] = await this.getBanks();
|
81
|
+
|
64
82
|
const [supplyPrice] = await fetchTokenPrices([this.supplyMint()]);
|
65
83
|
const [maxLtvBps, liqThresholdBps] = calcMarginfiMaxLtvAndLiqThresholdBps(
|
66
|
-
|
67
|
-
|
84
|
+
supplyBank,
|
85
|
+
debtBank,
|
68
86
|
supplyPrice
|
69
87
|
);
|
70
88
|
|
@@ -94,6 +112,7 @@ export class MarginfiSolautoPositionEx extends SolautoPositionEx {
|
|
94
112
|
useDesignatedMint
|
95
113
|
? { mint: toWeb3JsPublicKey(this.state().debt.mint) }
|
96
114
|
: undefined,
|
115
|
+
this.lpEnv,
|
97
116
|
this.contextUpdates
|
98
117
|
);
|
99
118
|
|
@@ -37,7 +37,7 @@ import {
|
|
37
37
|
tokenInfo,
|
38
38
|
toRoundedUsdValue,
|
39
39
|
} from "../utils";
|
40
|
-
import { RebalanceAction } from "../types";
|
40
|
+
import { ProgramEnv, RebalanceAction } from "../types";
|
41
41
|
import {
|
42
42
|
getDebtAdjustment,
|
43
43
|
getRebalanceValues,
|
@@ -51,6 +51,7 @@ export interface PositionCustomArgs {
|
|
51
51
|
debtMint?: PublicKey;
|
52
52
|
lendingPool?: PublicKey;
|
53
53
|
lpUserAccount?: PublicKey;
|
54
|
+
lpEnv?: ProgramEnv;
|
54
55
|
}
|
55
56
|
|
56
57
|
interface SolautoPositionExData extends Partial<SolautoPosition> {
|
@@ -66,10 +67,12 @@ interface PositionExArgs {
|
|
66
67
|
}
|
67
68
|
|
68
69
|
export abstract class SolautoPositionEx {
|
70
|
+
public lendingPlatform!: LendingPlatform;
|
69
71
|
public umi!: Umi;
|
70
72
|
public publicKey!: PublicKey;
|
71
73
|
protected _data!: SolautoPositionExData;
|
72
74
|
protected lp?: PublicKey = undefined;
|
75
|
+
protected lpEnv!: ProgramEnv;
|
73
76
|
public lpUserAccount?: PublicKey = undefined;
|
74
77
|
protected contextUpdates?: ContextUpdates;
|
75
78
|
|
@@ -86,6 +89,7 @@ export abstract class SolautoPositionEx {
|
|
86
89
|
(args.data.position
|
87
90
|
? toWeb3JsPublicKey(args.data.position!.protocolUserAccount)
|
88
91
|
: undefined);
|
92
|
+
this.lpEnv = args.customArgs?.lpEnv ?? "Prod";
|
89
93
|
|
90
94
|
this._data = args.data;
|
91
95
|
this.firstState = { ...args.data.state };
|
@@ -183,7 +187,10 @@ export abstract class SolautoPositionEx {
|
|
183
187
|
}
|
184
188
|
|
185
189
|
supplyUsd() {
|
186
|
-
|
190
|
+
const supplyPrice = safeGetPrice(this.supplyMint());
|
191
|
+
return supplyPrice
|
192
|
+
? calcTotalSupply(this.state()) * supplyPrice
|
193
|
+
: calcSupplyUsd(this.state());
|
187
194
|
}
|
188
195
|
|
189
196
|
totalDebt() {
|
@@ -191,7 +198,10 @@ export abstract class SolautoPositionEx {
|
|
191
198
|
}
|
192
199
|
|
193
200
|
debtUsd() {
|
194
|
-
|
201
|
+
const debtPrice = safeGetPrice(this.debtMint());
|
202
|
+
return debtPrice
|
203
|
+
? calcTotalDebt(this.state()) * debtPrice
|
204
|
+
: calcDebtUsd(this.state());
|
195
205
|
}
|
196
206
|
|
197
207
|
supplyLiquidityDepositable() {
|
@@ -203,7 +213,9 @@ export abstract class SolautoPositionEx {
|
|
203
213
|
}
|
204
214
|
|
205
215
|
supplyLiquidityUsdAvailable() {
|
206
|
-
return
|
216
|
+
return (
|
217
|
+
this.supplyLiquidityAvailable() * (safeGetPrice(this.supplyMint()) ?? 0)
|
218
|
+
);
|
207
219
|
}
|
208
220
|
|
209
221
|
debtLiquidityAvailable() {
|
@@ -215,6 +227,7 @@ export abstract class SolautoPositionEx {
|
|
215
227
|
}
|
216
228
|
|
217
229
|
abstract maxLtvAndLiqThresholdBps(): Promise<[number, number]>;
|
230
|
+
abstract priceOracles(): Promise<PublicKey[]>;
|
218
231
|
abstract supplyLiquidityAvailable(): number;
|
219
232
|
|
220
233
|
sufficientLiquidityToBoost() {
|
@@ -244,7 +257,7 @@ export abstract class SolautoPositionEx {
|
|
244
257
|
}
|
245
258
|
|
246
259
|
eligibleForRebalance(bpsDistanceThreshold = 0): RebalanceAction | undefined {
|
247
|
-
if (!this.settings() || !
|
260
|
+
if (!this.settings() || !this.supplyUsd()) {
|
248
261
|
return undefined;
|
249
262
|
}
|
250
263
|
|
package/src/types/accounts.ts
CHANGED
@@ -184,3 +184,15 @@ export async function customRpcCall(umi: Umi, method: string, params?: any) {
|
|
184
184
|
return data;
|
185
185
|
}
|
186
186
|
}
|
187
|
+
|
188
|
+
export function u16ToArrayBufferLE(value: number): Uint8Array {
|
189
|
+
// Create a buffer of 2 bytes
|
190
|
+
const buffer = new ArrayBuffer(2);
|
191
|
+
const dataView = new DataView(buffer);
|
192
|
+
|
193
|
+
// Set the Uint16 value in little-endian order
|
194
|
+
dataView.setUint16(0, value, true);
|
195
|
+
|
196
|
+
// Return the buffer
|
197
|
+
return new Uint8Array(buffer);
|
198
|
+
}
|
package/src/utils/index.ts
CHANGED
@@ -1,25 +1,30 @@
|
|
1
1
|
import { PublicKey } from "@solana/web3.js";
|
2
2
|
import { Program, publicKey, Umi } from "@metaplex-foundation/umi";
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
fromWeb3JsPublicKey,
|
5
|
+
toWeb3JsPublicKey,
|
6
|
+
} from "@metaplex-foundation/umi-web3js-adapters";
|
4
7
|
import { ProgramEnv, MarginfiAssetAccounts } from "../types";
|
5
8
|
import { PositionState, PositionTokenState } from "../generated";
|
6
9
|
import {
|
7
|
-
DEFAULT_MARGINFI_GROUP,
|
8
|
-
MARGINFI_ACCOUNTS,
|
9
10
|
ALL_SUPPORTED_TOKENS,
|
10
|
-
|
11
|
-
|
11
|
+
getMarginfiAccounts,
|
12
|
+
MARGINFI_SPONSORED_SHARD_ID,
|
13
|
+
MarginfiBankAccountsMap,
|
14
|
+
PYTH_SPONSORED_SHARD_ID,
|
12
15
|
TOKEN_INFO,
|
13
16
|
USD_DECIMALS,
|
14
17
|
} from "../constants";
|
15
18
|
import {
|
16
19
|
Bank,
|
17
20
|
deserializeMarginfiAccount,
|
21
|
+
fetchBank,
|
18
22
|
getMarginfiAccountSize,
|
19
23
|
getMarginfiErrorFromCode,
|
20
24
|
getMarginfiErrorFromName,
|
21
25
|
MarginfiAccount,
|
22
26
|
OracleSetup,
|
27
|
+
safeFetchAllBank,
|
23
28
|
safeFetchBank,
|
24
29
|
safeFetchMarginfiAccount,
|
25
30
|
} from "../marginfi-sdk";
|
@@ -34,22 +39,16 @@ import {
|
|
34
39
|
toBaseUnit,
|
35
40
|
toBps,
|
36
41
|
} from "./numberUtils";
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
export function isMarginfiProgram(programId: PublicKey) {
|
43
|
-
return (
|
44
|
-
programId.equals(MARGINFI_PROD_PROGRAM) ||
|
45
|
-
programId.equals(MARGINFI_STAGING_PROGRAM)
|
46
|
-
);
|
47
|
-
}
|
42
|
+
import { getTokenAccountData } from "./accountUtils";
|
43
|
+
import {
|
44
|
+
getMostUpToDatePythOracle,
|
45
|
+
getPythPushOracleAddress,
|
46
|
+
} from "./pythUtils";
|
48
47
|
|
49
48
|
export function createDynamicMarginfiProgram(env?: ProgramEnv): Program {
|
50
49
|
return {
|
51
50
|
name: "marginfi",
|
52
|
-
publicKey: publicKey(
|
51
|
+
publicKey: publicKey(getMarginfiAccounts(env ?? "Prod").program),
|
53
52
|
getErrorFromCode(code: number, cause?: Error) {
|
54
53
|
return getMarginfiErrorFromCode(code, this, cause);
|
55
54
|
},
|
@@ -73,6 +72,81 @@ export function umiWithMarginfiProgram(umi: Umi, marginfiEnv?: ProgramEnv) {
|
|
73
72
|
});
|
74
73
|
}
|
75
74
|
|
75
|
+
export async function getAllBankRelatedAccounts(
|
76
|
+
umi: Umi,
|
77
|
+
bankAccountsMap: MarginfiBankAccountsMap
|
78
|
+
): Promise<PublicKey[]> {
|
79
|
+
const banks = Object.values(bankAccountsMap).flatMap((group) =>
|
80
|
+
Object.values(group).map((accounts) => accounts.bank)
|
81
|
+
);
|
82
|
+
const banksData = await safeFetchAllBank(
|
83
|
+
umi,
|
84
|
+
banks.map((x) => publicKey(x))
|
85
|
+
);
|
86
|
+
|
87
|
+
const oracles = banksData
|
88
|
+
.map((bank) => {
|
89
|
+
const oracleKey = toWeb3JsPublicKey(bank.config.oracleKeys[0]);
|
90
|
+
return bank.config.oracleSetup === OracleSetup.PythPushOracle
|
91
|
+
? [
|
92
|
+
getPythPushOracleAddress(oracleKey, PYTH_SPONSORED_SHARD_ID),
|
93
|
+
getPythPushOracleAddress(oracleKey, MARGINFI_SPONSORED_SHARD_ID),
|
94
|
+
]
|
95
|
+
: [oracleKey];
|
96
|
+
|
97
|
+
})
|
98
|
+
.flat()
|
99
|
+
.map((x) => x.toString());
|
100
|
+
|
101
|
+
const otherAccounts = Object.entries(bankAccountsMap).flatMap(
|
102
|
+
([groupName, tokenMap]) =>
|
103
|
+
Object.values(tokenMap).flatMap((accounts) => [
|
104
|
+
groupName,
|
105
|
+
accounts.liquidityVault,
|
106
|
+
accounts.vaultAuthority,
|
107
|
+
])
|
108
|
+
);
|
109
|
+
|
110
|
+
return Array.from(new Set([...banks, ...oracles, ...otherAccounts]))
|
111
|
+
.filter((x) => x !== PublicKey.default.toString())
|
112
|
+
.map((x) => new PublicKey(x));
|
113
|
+
}
|
114
|
+
|
115
|
+
export async function fetchBankAddresses(umi: Umi, bankPk: PublicKey) {
|
116
|
+
const bank = await fetchBank(umi, fromWeb3JsPublicKey(bankPk));
|
117
|
+
const liquidityVault = toWeb3JsPublicKey(bank!.liquidityVault);
|
118
|
+
const vaultAuthority = (await getTokenAccountData(umi, liquidityVault))
|
119
|
+
?.owner;
|
120
|
+
const priceOracle = await getMarginfiPriceOracle(umi, { data: bank });
|
121
|
+
|
122
|
+
return {
|
123
|
+
bank: bankPk,
|
124
|
+
liquidityVault,
|
125
|
+
vaultAuthority,
|
126
|
+
priceOracle,
|
127
|
+
};
|
128
|
+
}
|
129
|
+
|
130
|
+
export async function getMarginfiPriceOracle(
|
131
|
+
umi: Umi,
|
132
|
+
bank: { pk?: PublicKey; data?: Bank }
|
133
|
+
) {
|
134
|
+
if (!bank.data) {
|
135
|
+
bank.data = await fetchBank(umi, fromWeb3JsPublicKey(bank.pk!));
|
136
|
+
}
|
137
|
+
|
138
|
+
const oracleKey = toWeb3JsPublicKey(bank.data.config.oracleKeys[0]);
|
139
|
+
const priceOracle =
|
140
|
+
bank.data.config.oracleSetup === OracleSetup.PythPushOracle
|
141
|
+
? await getMostUpToDatePythOracle(umi, [
|
142
|
+
getPythPushOracleAddress(oracleKey, PYTH_SPONSORED_SHARD_ID),
|
143
|
+
getPythPushOracleAddress(oracleKey, MARGINFI_SPONSORED_SHARD_ID),
|
144
|
+
])
|
145
|
+
: oracleKey;
|
146
|
+
|
147
|
+
return priceOracle;
|
148
|
+
}
|
149
|
+
|
76
150
|
interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts {
|
77
151
|
mint: PublicKey;
|
78
152
|
}
|
@@ -80,16 +154,29 @@ interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts {
|
|
80
154
|
export function findMarginfiAccounts(
|
81
155
|
bank: PublicKey
|
82
156
|
): AllMarginfiAssetAccounts {
|
83
|
-
|
84
|
-
for (const
|
85
|
-
const
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
157
|
+
const search = (bankAccounts: MarginfiBankAccountsMap) => {
|
158
|
+
for (const group in bankAccounts) {
|
159
|
+
for (const key in bankAccounts[group]) {
|
160
|
+
const account = bankAccounts[group][key];
|
161
|
+
if (
|
162
|
+
account.bank.toString().toLowerCase() ===
|
163
|
+
bank.toString().toLowerCase()
|
164
|
+
) {
|
165
|
+
return { ...account, mint: new PublicKey(key) };
|
166
|
+
}
|
90
167
|
}
|
91
168
|
}
|
169
|
+
};
|
170
|
+
|
171
|
+
let res = search(getMarginfiAccounts("Prod").bankAccounts);
|
172
|
+
if (res) {
|
173
|
+
return res;
|
174
|
+
}
|
175
|
+
res = search(getMarginfiAccounts("Staging").bankAccounts);
|
176
|
+
if (res) {
|
177
|
+
return res;
|
92
178
|
}
|
179
|
+
|
93
180
|
throw new Error(`Marginfi accounts not found by the bank: ${bank}`);
|
94
181
|
}
|
95
182
|
|
@@ -145,11 +232,16 @@ export async function getMarginfiMaxLtvAndLiqThresholdBps(
|
|
145
232
|
return [0, 0];
|
146
233
|
}
|
147
234
|
|
235
|
+
const bankAccounts = getMarginfiAccounts(
|
236
|
+
undefined,
|
237
|
+
marginfiGroup
|
238
|
+
).bankAccounts;
|
239
|
+
|
148
240
|
if (!supply.bank || supply.bank === null) {
|
149
241
|
supply.bank = await safeFetchBank(
|
150
242
|
umi,
|
151
243
|
publicKey(
|
152
|
-
|
244
|
+
bankAccounts[marginfiGroup.toString()][supply.mint.toString()].bank
|
153
245
|
),
|
154
246
|
{ commitment: "confirmed" }
|
155
247
|
);
|
@@ -162,7 +254,7 @@ export async function getMarginfiMaxLtvAndLiqThresholdBps(
|
|
162
254
|
debt.bank = await safeFetchBank(
|
163
255
|
umi,
|
164
256
|
publicKey(
|
165
|
-
|
257
|
+
bankAccounts[marginfiGroup.toString()][debt.mint.toString()].bank
|
166
258
|
),
|
167
259
|
{ commitment: "confirmed" }
|
168
260
|
);
|
@@ -381,9 +473,9 @@ async function getBank(
|
|
381
473
|
? await safeFetchBank(
|
382
474
|
umi,
|
383
475
|
publicKey(
|
384
|
-
|
385
|
-
|
386
|
-
].bank
|
476
|
+
getMarginfiAccounts(undefined, marginfiGroup).bankAccounts[
|
477
|
+
marginfiGroup?.toString() ?? ""
|
478
|
+
][data?.mint.toString()].bank
|
387
479
|
),
|
388
480
|
{ commitment: "confirmed" }
|
389
481
|
)
|
@@ -396,6 +488,7 @@ export async function getMarginfiAccountPositionState(
|
|
396
488
|
marginfiGroup?: PublicKey,
|
397
489
|
supply?: BankSelection,
|
398
490
|
debt?: BankSelection,
|
491
|
+
programEnv?: ProgramEnv,
|
399
492
|
contextUpdates?: ContextUpdates
|
400
493
|
): Promise<
|
401
494
|
| { supplyBank: Bank | null; debtBank: Bank | null; state: PositionState }
|
@@ -523,7 +616,7 @@ export async function getMarginfiAccountPositionState(
|
|
523
616
|
const supplyPrice = safeGetPrice(supply.mint!)!;
|
524
617
|
let [maxLtvBps, liqThresholdBps] = await getMarginfiMaxLtvAndLiqThresholdBps(
|
525
618
|
umi,
|
526
|
-
marginfiGroup ??
|
619
|
+
marginfiGroup ?? getMarginfiAccounts(programEnv).defaultGroup,
|
527
620
|
{
|
528
621
|
mint: toWeb3JsPublicKey(supplyBank.mint),
|
529
622
|
bank: supplyBank,
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
2
|
+
import { PYTH_PUSH_PROGRAM } from "../constants";
|
3
|
+
import { u16ToArrayBufferLE, zip } from "./generalUtils";
|
4
|
+
import * as borsh from "borsh";
|
5
|
+
import { Umi } from "@metaplex-foundation/umi";
|
6
|
+
import { fromWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
|
7
|
+
|
8
|
+
type PriceUpdateV2 = {
|
9
|
+
writeAuthority: Buffer;
|
10
|
+
verificationLevel: number;
|
11
|
+
priceMessage: {
|
12
|
+
feedId: Buffer;
|
13
|
+
price: bigint;
|
14
|
+
conf: bigint;
|
15
|
+
exponent: number;
|
16
|
+
publishTime: bigint;
|
17
|
+
prevPublishTime: bigint;
|
18
|
+
emaPrice: bigint;
|
19
|
+
emaConf: bigint;
|
20
|
+
};
|
21
|
+
};
|
22
|
+
|
23
|
+
const priceUpdateV2Schema = {
|
24
|
+
struct: {
|
25
|
+
writeAuthority: {
|
26
|
+
array: { type: "u8", len: 32 },
|
27
|
+
},
|
28
|
+
verificationLevel: "u8",
|
29
|
+
priceMessage: {
|
30
|
+
struct: {
|
31
|
+
feedId: { array: { type: "u8", len: 32 } },
|
32
|
+
price: "i64",
|
33
|
+
conf: "u64",
|
34
|
+
exponent: "i32",
|
35
|
+
publishTime: "i64",
|
36
|
+
prevPublishTime: "i64",
|
37
|
+
emaPrice: "i64",
|
38
|
+
emaConf: "u64",
|
39
|
+
},
|
40
|
+
},
|
41
|
+
postedSlot: "u64",
|
42
|
+
},
|
43
|
+
};
|
44
|
+
|
45
|
+
export function parsePriceInfo(data: Uint8Array): PriceUpdateV2 {
|
46
|
+
let decoded: PriceUpdateV2 = borsh.deserialize(
|
47
|
+
priceUpdateV2Schema,
|
48
|
+
data
|
49
|
+
) as any;
|
50
|
+
return decoded;
|
51
|
+
}
|
52
|
+
|
53
|
+
export async function getMostUpToDatePythOracle(
|
54
|
+
umi: Umi,
|
55
|
+
oracleKeys: PublicKey[]
|
56
|
+
) {
|
57
|
+
const oracles = zip(
|
58
|
+
oracleKeys,
|
59
|
+
(
|
60
|
+
await umi.rpc.getAccounts(
|
61
|
+
oracleKeys.map((x) => fromWeb3JsPublicKey(x)),
|
62
|
+
{ commitment: "confirmed" }
|
63
|
+
)
|
64
|
+
).map((x) => (x.exists ? parsePriceInfo(x!.data.slice(8)) : undefined))
|
65
|
+
).sort(
|
66
|
+
(a, b) =>
|
67
|
+
Number(b[1]?.priceMessage.publishTime ?? 0) -
|
68
|
+
Number(a[1]?.priceMessage.publishTime ?? 0)
|
69
|
+
);
|
70
|
+
|
71
|
+
return oracles[0][0];
|
72
|
+
}
|
73
|
+
|
74
|
+
export function getPythPushOracleAddress(
|
75
|
+
feedId: PublicKey,
|
76
|
+
shardId: number,
|
77
|
+
programId: PublicKey = PYTH_PUSH_PROGRAM
|
78
|
+
): PublicKey {
|
79
|
+
const shardBytes = u16ToArrayBufferLE(shardId);
|
80
|
+
return PublicKey.findProgramAddressSync(
|
81
|
+
[shardBytes, feedId.toBuffer()],
|
82
|
+
programId
|
83
|
+
)[0];
|
84
|
+
}
|
@@ -453,7 +453,7 @@ export function getClient(
|
|
453
453
|
export function isMarginfiClient(
|
454
454
|
client: SolautoClient
|
455
455
|
): client is SolautoMarginfiClient {
|
456
|
-
return client.lendingPlatform
|
456
|
+
return client.lendingPlatform === LendingPlatform.Marginfi;
|
457
457
|
}
|
458
458
|
// TODO: PF
|
459
459
|
|
package/tests/unit/accounts.ts
CHANGED
@@ -6,21 +6,17 @@ import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
|
|
6
6
|
import {
|
7
7
|
ALL_SUPPORTED_TOKENS,
|
8
8
|
TOKEN_INFO,
|
9
|
-
MARGINFI_ACCOUNTS,
|
10
9
|
SOLAUTO_FEES_WALLET,
|
11
10
|
SOLAUTO_MANAGER,
|
12
11
|
LOCAL_IRONFORGE_API_URL,
|
13
|
-
|
14
|
-
import {
|
12
|
+
getMarginfiAccounts,
|
15
13
|
getSolanaRpcConnection,
|
16
14
|
getEmptyMarginfiAccountsByAuthority,
|
17
15
|
getTokenAccount,
|
18
|
-
} from "../../src
|
16
|
+
} from "../../src";
|
19
17
|
|
20
18
|
async function hasTokenAccounts(wallet: PublicKey) {
|
21
|
-
let [_, umi] = getSolanaRpcConnection(
|
22
|
-
LOCAL_IRONFORGE_API_URL
|
23
|
-
);
|
19
|
+
let [_, umi] = getSolanaRpcConnection(LOCAL_IRONFORGE_API_URL);
|
24
20
|
|
25
21
|
const tokenAccounts = await umi.rpc.getAccounts(
|
26
22
|
ALL_SUPPORTED_TOKENS.map((x) =>
|
@@ -45,17 +41,15 @@ describe("Assert Solauto fee token accounts are created", async () => {
|
|
45
41
|
});
|
46
42
|
|
47
43
|
it("ISM accounts for every supported Marginfi group", async () => {
|
48
|
-
let [_, umi] = getSolanaRpcConnection(
|
49
|
-
LOCAL_IRONFORGE_API_URL
|
50
|
-
);
|
44
|
+
let [_, umi] = getSolanaRpcConnection(LOCAL_IRONFORGE_API_URL);
|
51
45
|
|
52
46
|
const ismAccounts = await getEmptyMarginfiAccountsByAuthority(
|
53
47
|
umi,
|
54
48
|
SOLAUTO_MANAGER
|
55
49
|
);
|
56
|
-
const supportedMarginfiGroups = Object.keys(
|
57
|
-
(
|
58
|
-
);
|
50
|
+
const supportedMarginfiGroups = Object.keys(
|
51
|
+
getMarginfiAccounts("Prod").bankAccounts
|
52
|
+
).map((x) => new PublicKey(x));
|
59
53
|
const missingIsmAccounts = supportedMarginfiGroups.filter(
|
60
54
|
(group) =>
|
61
55
|
!ismAccounts.find((x) => group.equals(toWeb3JsPublicKey(x.group)))
|
@@ -1,64 +1,43 @@
|
|
1
1
|
import { describe, it } from "mocha";
|
2
|
-
import { PublicKey } from "@solana/web3.js";
|
3
2
|
import {
|
3
|
+
getMarginfiAccounts,
|
4
4
|
LOCAL_IRONFORGE_API_URL,
|
5
|
-
MARGINFI_ACCOUNTS,
|
6
|
-
MARGINFI_ACCOUNTS_LOOKUP_TABLE,
|
7
5
|
SOLAUTO_MANAGER,
|
8
|
-
|
9
|
-
import {
|
6
|
+
getAllBankRelatedAccounts,
|
10
7
|
getEmptyMarginfiAccountsByAuthority,
|
11
8
|
getSolanaRpcConnection,
|
12
|
-
|
9
|
+
ProgramEnv,
|
10
|
+
} from "../../src";
|
13
11
|
|
14
|
-
const [conn, umi] = getSolanaRpcConnection(
|
15
|
-
LOCAL_IRONFORGE_API_URL
|
16
|
-
);
|
12
|
+
const [conn, umi] = getSolanaRpcConnection(LOCAL_IRONFORGE_API_URL);
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
);
|
23
|
-
|
24
|
-
throw new Error("Lookup table not found");
|
25
|
-
}
|
14
|
+
async function checkLookupTableAccounts(programEnv: ProgramEnv) {
|
15
|
+
const data = getMarginfiAccounts(programEnv);
|
16
|
+
const lookupTable = await conn.getAddressLookupTable(data.lookupTable);
|
17
|
+
if (lookupTable === null) {
|
18
|
+
throw new Error("Lookup table not found");
|
19
|
+
}
|
26
20
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
);
|
21
|
+
const ismAccounts = (
|
22
|
+
await getEmptyMarginfiAccountsByAuthority(umi, SOLAUTO_MANAGER)
|
23
|
+
).map((x) => x.publicKey.toString());
|
31
24
|
|
32
|
-
|
33
|
-
|
25
|
+
const bankAccounts = (
|
26
|
+
await getAllBankRelatedAccounts(umi, data.bankAccounts)
|
27
|
+
).map((x) => x.toString());
|
34
28
|
|
35
|
-
|
36
|
-
for (const key in MARGINFI_ACCOUNTS[group]) {
|
37
|
-
if (key === PublicKey.default.toString()) {
|
38
|
-
continue;
|
39
|
-
}
|
29
|
+
const accountsRequired = [...ismAccounts, ...bankAccounts];
|
40
30
|
|
41
|
-
|
42
|
-
|
43
|
-
.map((x) => x.publicKey.toString());
|
44
|
-
if (groupIsmAccounts.length === 0) {
|
45
|
-
throw new Error(`Missing ISM account for group: ${group}`);
|
46
|
-
}
|
31
|
+
const existingAccounts =
|
32
|
+
lookupTable.value?.state.addresses.map((x) => x.toString()) ?? [];
|
47
33
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
accounts.liquidityVault,
|
53
|
-
accounts.vaultAuthority,
|
54
|
-
accounts.priceOracle,
|
55
|
-
...groupIsmAccounts,
|
56
|
-
];
|
34
|
+
if (accountsRequired.find((x) => !existingAccounts.includes(x.toString()))) {
|
35
|
+
throw new Error("Marginfi accounts lookup table missing an account");
|
36
|
+
}
|
37
|
+
}
|
57
38
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
}
|
62
|
-
}
|
39
|
+
describe("Assert lookup tables up-to-date", async () => {
|
40
|
+
it("marginfi accounts LUT should have everything", async () => {
|
41
|
+
await checkLookupTableAccounts("Prod");
|
63
42
|
});
|
64
43
|
});
|