@haven-fi/solauto-sdk 1.0.626 → 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 +3 -4
- package/dist/constants/marginfiAccounts.d.ts.map +1 -1
- package/dist/constants/marginfiAccounts.js +9 -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 +7 -8
- package/dist/services/solauto/solautoClient.d.ts.map +1 -1
- package/dist/services/solauto/solautoClient.js +5 -6
- package/dist/services/solauto/solautoMarginfiClient.d.ts.map +1 -1
- package/dist/services/solauto/solautoMarginfiClient.js +4 -9
- 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 +15 -2
- package/dist/solautoPosition/solautoPositionEx.d.ts +2 -0
- package/dist/solautoPosition/solautoPositionEx.d.ts.map +1 -1
- 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 +7 -2
- package/dist/utils/marginfiUtils.d.ts.map +1 -1
- package/dist/utils/marginfiUtils.js +44 -11
- 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 +9 -15
- package/package.json +1 -1
- package/src/constants/marginfiAccounts.ts +13 -39
- package/src/constants/pythConstants.ts +8 -0
- package/src/services/flashLoans/marginfiFlProvider.ts +9 -9
- package/src/services/solauto/solautoClient.ts +6 -6
- package/src/services/solauto/solautoMarginfiClient.ts +5 -11
- package/src/services/transactions/transactionUtils.ts +1 -1
- package/src/solautoPosition/marginfiSolautoPositionEx.ts +22 -3
- package/src/solautoPosition/solautoPositionEx.ts +2 -0
- 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 +75 -17
- 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
@@ -1,5 +1,13 @@
|
|
1
1
|
import { NATIVE_MINT } from "@solana/spl-token";
|
2
2
|
import * as tokens from "./tokenConstants";
|
3
|
+
import { PublicKey } from "@solana/web3.js";
|
4
|
+
|
5
|
+
export const PYTH_PUSH_PROGRAM = new PublicKey(
|
6
|
+
"pythWSnswVUd12oZpeFP8e9CVaEqJg25g1Vtc2biRsT"
|
7
|
+
);
|
8
|
+
|
9
|
+
export const PYTH_SPONSORED_SHARD_ID = 0;
|
10
|
+
export const MARGINFI_SPONSORED_SHARD_ID = 3301;
|
3
11
|
|
4
12
|
// https://pyth.network/developers/price-feed-ids#solana-stable
|
5
13
|
export const PYTH_PRICE_FEED_IDS = {
|
@@ -29,6 +29,7 @@ import {
|
|
29
29
|
fromBaseUnit,
|
30
30
|
getBankLiquidityAvailableBaseUnit,
|
31
31
|
getEmptyMarginfiAccountsByAuthority,
|
32
|
+
getMarginfiPriceOracle,
|
32
33
|
getTokenAccount,
|
33
34
|
rpcAccountCreated,
|
34
35
|
safeGetPrice,
|
@@ -72,15 +73,15 @@ export class MarginfiFlProvider extends FlProviderBase {
|
|
72
73
|
const bankAccounts = getMarginfiAccounts(this.programEnv).bankAccounts;
|
73
74
|
|
74
75
|
const availableBanks: string[] = [];
|
75
|
-
const checkIfUsable = (group: string, mint:
|
76
|
-
if (Object.keys(bankAccounts[group]).includes(mint
|
77
|
-
availableBanks.push(bankAccounts[group][mint
|
76
|
+
const checkIfUsable = (group: string, mint: string) => {
|
77
|
+
if (Object.keys(bankAccounts[group]).includes(mint)) {
|
78
|
+
availableBanks.push(bankAccounts[group][mint].bank);
|
78
79
|
}
|
79
80
|
};
|
80
81
|
|
81
82
|
for (const group of Object.keys(bankAccounts)) {
|
82
|
-
checkIfUsable(group, this.supplyMint);
|
83
|
-
checkIfUsable(group, this.debtMint);
|
83
|
+
checkIfUsable(group, this.supplyMint.toString());
|
84
|
+
checkIfUsable(group, this.debtMint.toString());
|
84
85
|
}
|
85
86
|
|
86
87
|
const banks = await safeFetchAllBank(
|
@@ -301,11 +302,10 @@ export class MarginfiFlProvider extends FlProviderBase {
|
|
301
302
|
flBankHadPrevBalance = true;
|
302
303
|
}
|
303
304
|
|
304
|
-
// TODO: Don't dynamically pull from bank until Marginfi sorts out their price oracle issues.
|
305
|
-
// const bankData = await safeFetchBank(this.umi, publicKey(accounts.data.bank));
|
306
|
-
// const priceOracle = bankData!.config.oracleKeys[0];
|
307
305
|
const priceOracle = publicKey(
|
308
|
-
|
306
|
+
await getMarginfiPriceOracle(this.umi, {
|
307
|
+
pk: toWeb3JsPublicKey(x.bankPk),
|
308
|
+
})
|
309
309
|
);
|
310
310
|
|
311
311
|
remainingAccounts.push(
|
@@ -205,12 +205,8 @@ export abstract class SolautoClient extends ReferralStateManager {
|
|
205
205
|
return [
|
206
206
|
this.authority,
|
207
207
|
...(this.authorityLutAddress ? [this.authorityLutAddress] : []),
|
208
|
-
|
209
|
-
|
210
|
-
: []),
|
211
|
-
...(toWeb3JsPublicKey(this.signer.publicKey).equals(this.authority)
|
212
|
-
? [this.signerDebtTa]
|
213
|
-
: []),
|
208
|
+
this.signerSupplyTa,
|
209
|
+
this.signerDebtTa,
|
214
210
|
this.pos.publicKey,
|
215
211
|
this.positionSupplyTa,
|
216
212
|
this.positionDebtTa,
|
@@ -239,6 +235,10 @@ export abstract class SolautoClient extends ReferralStateManager {
|
|
239
235
|
}
|
240
236
|
| undefined
|
241
237
|
> {
|
238
|
+
if (!toWeb3JsPublicKey(this.signer.publicKey).equals(this.authority)) {
|
239
|
+
return undefined;
|
240
|
+
}
|
241
|
+
|
242
242
|
const existingLutAccounts = await this.fetchExistingAuthorityLutAccounts();
|
243
243
|
if (
|
244
244
|
this.lutAccountsToAdd().every((element) =>
|
@@ -32,6 +32,7 @@ import {
|
|
32
32
|
hasFirstRebalance,
|
33
33
|
} from "../../utils";
|
34
34
|
import {
|
35
|
+
Bank,
|
35
36
|
lendingAccountBorrow,
|
36
37
|
lendingAccountDeposit,
|
37
38
|
lendingAccountRepay,
|
@@ -113,19 +114,12 @@ export class SolautoMarginfiClient extends SolautoClient {
|
|
113
114
|
this.pos.debtMint().toString()
|
114
115
|
]!;
|
115
116
|
|
116
|
-
|
117
|
-
|
118
|
-
// publicKey(this.marginfiSupplyAccounts.bank),
|
119
|
-
// publicKey(this.marginfiDebtAccounts.bank),
|
120
|
-
// ]);
|
121
|
-
// this.supplyPriceOracle = toWeb3JsPublicKey(supplyBank.config.oracleKeys[0]);
|
122
|
-
// this.debtPriceOracle = toWeb3JsPublicKey(debtBank.config.oracleKeys[0]);
|
123
|
-
this.supplyPriceOracle = new PublicKey(
|
124
|
-
this.marginfiSupplyAccounts.priceOracle
|
125
|
-
);
|
126
|
-
this.debtPriceOracle = new PublicKey(this.marginfiDebtAccounts.priceOracle);
|
117
|
+
[this.supplyPriceOracle, this.debtPriceOracle] =
|
118
|
+
await this.pos.priceOracles();
|
127
119
|
|
128
120
|
this.log("Marginfi account:", this.marginfiAccountPk.toString());
|
121
|
+
this.log("Supply price oracle:", this.supplyPriceOracle.toString());
|
122
|
+
this.log("Debt price oracle:", this.debtPriceOracle.toString());
|
129
123
|
}
|
130
124
|
|
131
125
|
defaultLookupTables(): string[] {
|
@@ -39,7 +39,6 @@ import {
|
|
39
39
|
getTokenAccount,
|
40
40
|
getTokenAccountData,
|
41
41
|
isMarginfiClient,
|
42
|
-
isMarginfiProgram,
|
43
42
|
} from "../../utils";
|
44
43
|
import {
|
45
44
|
createMarginfiProgram,
|
@@ -56,6 +55,7 @@ import {
|
|
56
55
|
JUPITER_PROGRAM_ID,
|
57
56
|
} from "../../jupiter-sdk";
|
58
57
|
import { TransactionItemInputs, BundleSimulationError } from "../../types";
|
58
|
+
import { isMarginfiProgram } from "../../constants";
|
59
59
|
|
60
60
|
interface wSolTokenUsage {
|
61
61
|
wSolTokenAccount: PublicKey;
|
@@ -13,11 +13,15 @@ import {
|
|
13
13
|
fromBaseUnit,
|
14
14
|
getBankLiquidityAvailableBaseUnit,
|
15
15
|
getMarginfiAccountPositionState,
|
16
|
+
getMarginfiPriceOracle,
|
16
17
|
} from "../utils";
|
17
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;
|
@@ -47,7 +51,7 @@ export class MarginfiSolautoPositionEx extends SolautoPositionEx {
|
|
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
57
|
const bankAccounts = getMarginfiAccounts(this.lpEnv).bankAccounts;
|
@@ -60,10 +64,25 @@ export class MarginfiSolautoPositionEx extends SolautoPositionEx {
|
|
60
64
|
]);
|
61
65
|
}
|
62
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
|
+
|
63
82
|
const [supplyPrice] = await fetchTokenPrices([this.supplyMint()]);
|
64
83
|
const [maxLtvBps, liqThresholdBps] = calcMarginfiMaxLtvAndLiqThresholdBps(
|
65
|
-
|
66
|
-
|
84
|
+
supplyBank,
|
85
|
+
debtBank,
|
67
86
|
supplyPrice
|
68
87
|
);
|
69
88
|
|
@@ -67,6 +67,7 @@ interface PositionExArgs {
|
|
67
67
|
}
|
68
68
|
|
69
69
|
export abstract class SolautoPositionEx {
|
70
|
+
public lendingPlatform!: LendingPlatform;
|
70
71
|
public umi!: Umi;
|
71
72
|
public publicKey!: PublicKey;
|
72
73
|
protected _data!: SolautoPositionExData;
|
@@ -226,6 +227,7 @@ export abstract class SolautoPositionEx {
|
|
226
227
|
}
|
227
228
|
|
228
229
|
abstract maxLtvAndLiqThresholdBps(): Promise<[number, number]>;
|
230
|
+
abstract priceOracles(): Promise<PublicKey[]>;
|
229
231
|
abstract supplyLiquidityAvailable(): number;
|
230
232
|
|
231
233
|
sufficientLiquidityToBoost() {
|
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
@@ -9,20 +9,22 @@ import { PositionState, PositionTokenState } from "../generated";
|
|
9
9
|
import {
|
10
10
|
ALL_SUPPORTED_TOKENS,
|
11
11
|
getMarginfiAccounts,
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
MARGINFI_SPONSORED_SHARD_ID,
|
13
|
+
MarginfiBankAccountsMap,
|
14
|
+
PYTH_SPONSORED_SHARD_ID,
|
15
15
|
TOKEN_INFO,
|
16
16
|
USD_DECIMALS,
|
17
17
|
} from "../constants";
|
18
18
|
import {
|
19
19
|
Bank,
|
20
20
|
deserializeMarginfiAccount,
|
21
|
+
fetchBank,
|
21
22
|
getMarginfiAccountSize,
|
22
23
|
getMarginfiErrorFromCode,
|
23
24
|
getMarginfiErrorFromName,
|
24
25
|
MarginfiAccount,
|
25
26
|
OracleSetup,
|
27
|
+
safeFetchAllBank,
|
26
28
|
safeFetchBank,
|
27
29
|
safeFetchMarginfiAccount,
|
28
30
|
} from "../marginfi-sdk";
|
@@ -38,22 +40,15 @@ import {
|
|
38
40
|
toBps,
|
39
41
|
} from "./numberUtils";
|
40
42
|
import { getTokenAccountData } from "./accountUtils";
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
}
|
45
|
-
|
46
|
-
export function isMarginfiProgram(programId: PublicKey) {
|
47
|
-
return (
|
48
|
-
programId.equals(MARGINFI_PROD_PROGRAM) ||
|
49
|
-
programId.equals(MARGINFI_STAGING_PROGRAM)
|
50
|
-
);
|
51
|
-
}
|
43
|
+
import {
|
44
|
+
getMostUpToDatePythOracle,
|
45
|
+
getPythPushOracleAddress,
|
46
|
+
} from "./pythUtils";
|
52
47
|
|
53
48
|
export function createDynamicMarginfiProgram(env?: ProgramEnv): Program {
|
54
49
|
return {
|
55
50
|
name: "marginfi",
|
56
|
-
publicKey: publicKey(
|
51
|
+
publicKey: publicKey(getMarginfiAccounts(env ?? "Prod").program),
|
57
52
|
getErrorFromCode(code: number, cause?: Error) {
|
58
53
|
return getMarginfiErrorFromCode(code, this, cause);
|
59
54
|
},
|
@@ -77,18 +72,81 @@ export function umiWithMarginfiProgram(umi: Umi, marginfiEnv?: ProgramEnv) {
|
|
77
72
|
});
|
78
73
|
}
|
79
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
|
+
|
80
115
|
export async function fetchBankAddresses(umi: Umi, bankPk: PublicKey) {
|
81
|
-
const bank = await
|
116
|
+
const bank = await fetchBank(umi, fromWeb3JsPublicKey(bankPk));
|
82
117
|
const liquidityVault = toWeb3JsPublicKey(bank!.liquidityVault);
|
83
118
|
const vaultAuthority = (await getTokenAccountData(umi, liquidityVault))
|
84
119
|
?.owner;
|
120
|
+
const priceOracle = await getMarginfiPriceOracle(umi, { data: bank });
|
121
|
+
|
85
122
|
return {
|
86
123
|
bank: bankPk,
|
87
124
|
liquidityVault,
|
88
125
|
vaultAuthority,
|
126
|
+
priceOracle,
|
89
127
|
};
|
90
128
|
}
|
91
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
|
+
|
92
150
|
interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts {
|
93
151
|
mint: PublicKey;
|
94
152
|
}
|
@@ -96,7 +154,7 @@ interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts {
|
|
96
154
|
export function findMarginfiAccounts(
|
97
155
|
bank: PublicKey
|
98
156
|
): AllMarginfiAssetAccounts {
|
99
|
-
const search = (bankAccounts:
|
157
|
+
const search = (bankAccounts: MarginfiBankAccountsMap) => {
|
100
158
|
for (const group in bankAccounts) {
|
101
159
|
for (const key in bankAccounts[group]) {
|
102
160
|
const account = bankAccounts[group][key];
|
@@ -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
|
});
|