@haven-fi/solauto-sdk 1.0.302 → 1.0.304
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/clients/solautoMarginfiClient.d.ts +1 -2
- package/dist/clients/solautoMarginfiClient.d.ts.map +1 -1
- package/dist/clients/solautoMarginfiClient.js +20 -24
- package/dist/transactions/transactionUtils.d.ts.map +1 -1
- package/dist/transactions/transactionUtils.js +4 -3
- package/dist/transactions/transactionsManager.d.ts.map +1 -1
- package/dist/transactions/transactionsManager.js +15 -0
- package/dist/utils/generalUtils.d.ts +2 -6
- package/dist/utils/generalUtils.d.ts.map +1 -1
- package/dist/utils/generalUtils.js +0 -85
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -0
- package/dist/utils/jupiterUtils.d.ts.map +1 -1
- package/dist/utils/jupiterUtils.js +10 -2
- package/dist/utils/marginfiUtils.d.ts +2 -2
- package/dist/utils/marginfiUtils.d.ts.map +1 -1
- package/dist/utils/marginfiUtils.js +13 -16
- package/dist/utils/priceUtils.d.ts +12 -0
- package/dist/utils/priceUtils.d.ts.map +1 -0
- package/dist/utils/priceUtils.js +97 -0
- package/dist/utils/solanaUtils.d.ts.map +1 -1
- package/dist/utils/solanaUtils.js +1 -1
- package/dist/utils/solauto/generalUtils.d.ts.map +1 -1
- package/dist/utils/solauto/generalUtils.js +2 -1
- package/dist/utils/solauto/rebalanceUtils.d.ts.map +1 -1
- package/dist/utils/solauto/rebalanceUtils.js +5 -4
- package/dist/utils/switchboardUtils.d.ts +7 -0
- package/dist/utils/switchboardUtils.d.ts.map +1 -0
- package/dist/utils/switchboardUtils.js +41 -0
- package/local/createISMAccounts.ts +59 -0
- package/local/updateMarginfiLUT.ts +0 -1
- package/local/updateSolautoLUT.ts +38 -7
- package/package.json +2 -1
- package/src/clients/solautoMarginfiClient.ts +18 -27
- package/src/constants/README.md +0 -2
- package/src/transactions/transactionUtils.ts +8 -3
- package/src/transactions/transactionsManager.ts +33 -2
- package/src/utils/generalUtils.ts +2 -147
- package/src/utils/index.ts +3 -1
- package/src/utils/jupiterUtils.ts +13 -3
- package/src/utils/marginfiUtils.ts +5 -25
- package/src/utils/priceUtils.ts +154 -0
- package/src/utils/solanaUtils.ts +1 -2
- package/src/utils/solauto/generalUtils.ts +3 -2
- package/src/utils/solauto/rebalanceUtils.ts +2 -1
- package/src/utils/switchboardUtils.ts +66 -0
- package/tests/transactions/solautoMarginfi.ts +29 -24
- package/tests/unit/rebalanceCalculations.ts +6 -5
- package/local/createSolautoManagerAccount.ts +0 -48
@@ -1,27 +1,5 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
Keypair,
|
4
|
-
PublicKey,
|
5
|
-
Transaction,
|
6
|
-
VersionedTransaction,
|
7
|
-
} from "@solana/web3.js";
|
8
|
-
import {
|
9
|
-
MaybeRpcAccount,
|
10
|
-
publicKey,
|
11
|
-
Umi,
|
12
|
-
PublicKey as UmiPublicKey,
|
13
|
-
} from "@metaplex-foundation/umi";
|
14
|
-
import { PYTH_PRICE_FEED_IDS } from "../constants/pythConstants";
|
15
|
-
import { fromBaseUnit, toBaseUnit } from "./numberUtils";
|
16
|
-
import { PRICES } from "../constants/solautoConstants";
|
17
|
-
import {
|
18
|
-
getProgramId,
|
19
|
-
PullFeed,
|
20
|
-
SwitchboardPermission,
|
21
|
-
} from "@switchboard-xyz/on-demand";
|
22
|
-
import { AnchorProvider, Idl, Program, Wallet } from "@coral-xyz/anchor";
|
23
|
-
import switchboardIdl from "../idls/switchboard.json";
|
24
|
-
import { SWITCHBOARD_PRICE_FEED_IDS } from "../constants/switchboardConstants";
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
2
|
+
import { MaybeRpcAccount, publicKey, Umi } from "@metaplex-foundation/umi";
|
25
3
|
|
26
4
|
export function consoleLog(...args: any[]): void {
|
27
5
|
if ((globalThis as any).LOCAL_TEST) {
|
@@ -82,129 +60,6 @@ export function zip<T, U>(list1: T[], list2: U[]): [T, U][] {
|
|
82
60
|
return result;
|
83
61
|
}
|
84
62
|
|
85
|
-
export async function fetchTokenPrices(
|
86
|
-
conn: Connection,
|
87
|
-
mints: PublicKey[]
|
88
|
-
): Promise<number[]> {
|
89
|
-
const currentTime = currentUnixSeconds();
|
90
|
-
if (
|
91
|
-
!mints.some(
|
92
|
-
(mint) =>
|
93
|
-
!(mint.toString() in PRICES) ||
|
94
|
-
currentTime - PRICES[mint.toString()].time > 3
|
95
|
-
)
|
96
|
-
) {
|
97
|
-
return mints.map((mint) => PRICES[mint.toString()].price);
|
98
|
-
}
|
99
|
-
|
100
|
-
const pythMints = mints.filter((x) => x.toString() in PYTH_PRICE_FEED_IDS);
|
101
|
-
const switchboardMints = mints.filter(
|
102
|
-
(x) => x.toString() in SWITCHBOARD_PRICE_FEED_IDS
|
103
|
-
);
|
104
|
-
|
105
|
-
const [pythData, switchboardData] = await Promise.all([
|
106
|
-
zip(pythMints, await getPythPrices(pythMints)),
|
107
|
-
zip(switchboardMints, await getSwitchboardPrices(conn, switchboardMints)),
|
108
|
-
]);
|
109
|
-
|
110
|
-
const prices = mints.map((mint) => {
|
111
|
-
const item = [...pythData, ...switchboardData].find((data) =>
|
112
|
-
data[0].equals(mint)
|
113
|
-
);
|
114
|
-
return item ? item[1] : 0;
|
115
|
-
});
|
116
|
-
|
117
|
-
for (var i = 0; i < mints.length; i++) {
|
118
|
-
PRICES[mints[i].toString()] = {
|
119
|
-
price: prices[i],
|
120
|
-
time: currentUnixSeconds(),
|
121
|
-
};
|
122
|
-
}
|
123
|
-
|
124
|
-
return prices;
|
125
|
-
}
|
126
|
-
|
127
|
-
export async function getPythPrices(mints: PublicKey[]) {
|
128
|
-
const priceFeedIds = mints.map(
|
129
|
-
(mint) => PYTH_PRICE_FEED_IDS[mint.toString()]
|
130
|
-
);
|
131
|
-
|
132
|
-
const getReq = async () =>
|
133
|
-
await fetch(
|
134
|
-
`https://hermes.pyth.network/v2/updates/price/latest?${priceFeedIds.map((x) => `ids%5B%5D=${x}`).join("&")}`
|
135
|
-
);
|
136
|
-
|
137
|
-
const prices: number[] = await retryWithExponentialBackoff(
|
138
|
-
async () => {
|
139
|
-
let resp = await getReq();
|
140
|
-
let status = resp.status;
|
141
|
-
if (status !== 200) {
|
142
|
-
throw new Error(JSON.stringify(resp));
|
143
|
-
}
|
144
|
-
|
145
|
-
const json = await resp.json();
|
146
|
-
const prices = json.parsed.map((x: any) => {
|
147
|
-
if (x.price.expo > 0) {
|
148
|
-
return Number(toBaseUnit(Number(x.price.price), x.price.expo));
|
149
|
-
} else if (x.price.expo < 0) {
|
150
|
-
return fromBaseUnit(BigInt(x.price.price), Math.abs(x.price.expo));
|
151
|
-
} else {
|
152
|
-
return Number(x.price.price);
|
153
|
-
}
|
154
|
-
});
|
155
|
-
|
156
|
-
return prices;
|
157
|
-
},
|
158
|
-
5,
|
159
|
-
200
|
160
|
-
);
|
161
|
-
|
162
|
-
return prices;
|
163
|
-
}
|
164
|
-
|
165
|
-
export async function getSwitchboardPrices(
|
166
|
-
conn: Connection,
|
167
|
-
mints: PublicKey[]
|
168
|
-
) {
|
169
|
-
const dummyWallet = {
|
170
|
-
publicKey: new PublicKey("11111111111111111111111111111111"),
|
171
|
-
signTransaction: async <T extends Transaction | VersionedTransaction>(
|
172
|
-
tx: T
|
173
|
-
): Promise<T> => tx,
|
174
|
-
signAllTransactions: async <T extends Transaction | VersionedTransaction>(
|
175
|
-
txs: T[]
|
176
|
-
): Promise<T[]> => txs,
|
177
|
-
};
|
178
|
-
const provider = new AnchorProvider(
|
179
|
-
conn,
|
180
|
-
dummyWallet,
|
181
|
-
AnchorProvider.defaultOptions()
|
182
|
-
);
|
183
|
-
const program = new Program(switchboardIdl as Idl, provider);
|
184
|
-
|
185
|
-
const results = await Promise.all(
|
186
|
-
mints.map(async (mint) => {
|
187
|
-
const feed = new PullFeed(
|
188
|
-
program,
|
189
|
-
new PublicKey(SWITCHBOARD_PRICE_FEED_IDS[mint.toString()])
|
190
|
-
);
|
191
|
-
const result = await feed.loadData();
|
192
|
-
return Number(result.result.value) / Math.pow(10, 18);
|
193
|
-
})
|
194
|
-
);
|
195
|
-
|
196
|
-
return results;
|
197
|
-
}
|
198
|
-
|
199
|
-
export function safeGetPrice(
|
200
|
-
mint: PublicKey | UmiPublicKey | undefined
|
201
|
-
): number | undefined {
|
202
|
-
if (mint && mint?.toString() in PRICES) {
|
203
|
-
return PRICES[mint!.toString()].price;
|
204
|
-
}
|
205
|
-
return undefined;
|
206
|
-
}
|
207
|
-
|
208
63
|
export type ErrorsToThrow = Array<new (...args: any[]) => Error>;
|
209
64
|
|
210
65
|
export function retryWithExponentialBackoff<T>(
|
package/src/utils/index.ts
CHANGED
@@ -13,6 +13,7 @@ import {
|
|
13
13
|
} from "@jup-ag/api";
|
14
14
|
import { getTokenAccount } from "./accountUtils";
|
15
15
|
import { consoleLog, retryWithExponentialBackoff } from "./generalUtils";
|
16
|
+
import { TOKEN_INFO } from "../constants";
|
16
17
|
|
17
18
|
const jupApi = createJupiterApiClient();
|
18
19
|
|
@@ -54,7 +55,10 @@ export async function getJupSwapTransaction(
|
|
54
55
|
swapDetails: JupSwapDetails,
|
55
56
|
attemptNum?: number
|
56
57
|
): Promise<JupSwapTransaction> {
|
57
|
-
|
58
|
+
const memecoinSwap =
|
59
|
+
TOKEN_INFO[swapDetails.inputMint.toString()].isMeme ||
|
60
|
+
TOKEN_INFO[swapDetails.outputMint.toString()].isMeme;
|
61
|
+
|
58
62
|
const quoteResponse = await retryWithExponentialBackoff(
|
59
63
|
async () =>
|
60
64
|
await jupApi.quoteGet({
|
@@ -66,7 +70,7 @@ export async function getJupSwapTransaction(
|
|
66
70
|
: swapDetails.exactIn
|
67
71
|
? "ExactIn"
|
68
72
|
: undefined,
|
69
|
-
slippageBps: 50,
|
73
|
+
slippageBps: memecoinSwap ? 150 : 50,
|
70
74
|
maxAccounts: !swapDetails.exactOut ? 60 : undefined,
|
71
75
|
}),
|
72
76
|
4,
|
@@ -90,6 +94,12 @@ export async function getJupSwapTransaction(
|
|
90
94
|
)
|
91
95
|
).toString();
|
92
96
|
}
|
97
|
+
// else {
|
98
|
+
// quoteResponse.inAmount = (
|
99
|
+
// parseInt(quoteResponse.inAmount) +
|
100
|
+
// Math.ceil(parseInt(quoteResponse.inAmount) * fromBps(priceImpactBps))
|
101
|
+
// ).toString();
|
102
|
+
// }
|
93
103
|
|
94
104
|
consoleLog("Getting jup instructions...");
|
95
105
|
const instructions = await retryWithExponentialBackoff(
|
@@ -143,4 +153,4 @@ export async function getJupSwapTransaction(
|
|
143
153
|
)
|
144
154
|
),
|
145
155
|
};
|
146
|
-
}
|
156
|
+
}
|
@@ -9,11 +9,7 @@ import {
|
|
9
9
|
safeFetchBank,
|
10
10
|
safeFetchMarginfiAccount,
|
11
11
|
} from "../marginfi-sdk";
|
12
|
-
import {
|
13
|
-
currentUnixSeconds,
|
14
|
-
fetchTokenPrices,
|
15
|
-
safeGetPrice,
|
16
|
-
} from "./generalUtils";
|
12
|
+
import { currentUnixSeconds } from "./generalUtils";
|
17
13
|
import {
|
18
14
|
bytesToI80F48,
|
19
15
|
fromBaseUnit,
|
@@ -30,6 +26,7 @@ import { PositionState, PositionTokenUsage } from "../generated";
|
|
30
26
|
import { USD_DECIMALS } from "../constants/generalAccounts";
|
31
27
|
import { LivePositionUpdates } from "./solauto/generalUtils";
|
32
28
|
import { TOKEN_INFO } from "../constants";
|
29
|
+
import { fetchTokenPrices, safeGetPrice } from "./priceUtils";
|
33
30
|
|
34
31
|
interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts {
|
35
32
|
mint: PublicKey;
|
@@ -87,7 +84,6 @@ export function calcMaxLtvAndLiqThreshold(
|
|
87
84
|
}
|
88
85
|
|
89
86
|
export async function getMaxLtvAndLiqThreshold(
|
90
|
-
conn: Connection,
|
91
87
|
umi: Umi,
|
92
88
|
marginfiGroup: PublicKey,
|
93
89
|
supply: {
|
@@ -128,7 +124,7 @@ export async function getMaxLtvAndLiqThreshold(
|
|
128
124
|
}
|
129
125
|
|
130
126
|
if (!supplyPrice) {
|
131
|
-
const [price] = await fetchTokenPrices(
|
127
|
+
const [price] = await fetchTokenPrices([
|
132
128
|
toWeb3JsPublicKey(supply.bank!.mint),
|
133
129
|
]);
|
134
130
|
supplyPrice = price;
|
@@ -175,7 +171,7 @@ export async function getAllMarginfiAccountsByAuthority(
|
|
175
171
|
const positionStates = await Promise.all(
|
176
172
|
marginfiAccounts.map(async (x) => ({
|
177
173
|
publicKey: x.publicKey,
|
178
|
-
state: await getMarginfiAccountPositionState(
|
174
|
+
state: await getMarginfiAccountPositionState(umi, {
|
179
175
|
pk: toWeb3JsPublicKey(x.publicKey),
|
180
176
|
}),
|
181
177
|
}))
|
@@ -206,7 +202,6 @@ export async function getAllMarginfiAccountsByAuthority(
|
|
206
202
|
}
|
207
203
|
|
208
204
|
async function getTokenUsage(
|
209
|
-
conn: Connection,
|
210
205
|
bank: Bank | null,
|
211
206
|
isAsset: boolean,
|
212
207
|
shares: number,
|
@@ -217,7 +212,7 @@ async function getTokenUsage(
|
|
217
212
|
let marketPrice = 0;
|
218
213
|
|
219
214
|
if (bank !== null) {
|
220
|
-
[marketPrice] = await fetchTokenPrices(
|
215
|
+
[marketPrice] = await fetchTokenPrices([
|
221
216
|
toWeb3JsPublicKey(bank.mint),
|
222
217
|
]);
|
223
218
|
const [assetShareValue, liabilityShareValue] =
|
@@ -275,7 +270,6 @@ interface BankSelection {
|
|
275
270
|
type BanksCache = { [group: string]: { [mint: string]: Bank } };
|
276
271
|
|
277
272
|
export async function getMarginfiAccountPositionState(
|
278
|
-
conn: Connection,
|
279
273
|
umi: Umi,
|
280
274
|
protocolAccount: { pk: PublicKey; data?: MarginfiAccount },
|
281
275
|
marginfiGroup?: PublicKey,
|
@@ -360,7 +354,6 @@ export async function getMarginfiAccountPositionState(
|
|
360
354
|
supply.mint = toWeb3JsPublicKey(supplyBank!.mint);
|
361
355
|
}
|
362
356
|
supplyUsage = await getTokenUsage(
|
363
|
-
conn,
|
364
357
|
supplyBank!,
|
365
358
|
true,
|
366
359
|
bytesToI80F48(supplyBalances[0].assetShares.value),
|
@@ -378,7 +371,6 @@ export async function getMarginfiAccountPositionState(
|
|
378
371
|
debt.mint = toWeb3JsPublicKey(debtBank!.mint);
|
379
372
|
}
|
380
373
|
debtUsage = await getTokenUsage(
|
381
|
-
conn,
|
382
374
|
debtBank!,
|
383
375
|
false,
|
384
376
|
bytesToI80F48(debtBalances[0].liabilityShares.value),
|
@@ -391,18 +383,8 @@ export async function getMarginfiAccountPositionState(
|
|
391
383
|
return undefined;
|
392
384
|
}
|
393
385
|
|
394
|
-
if (
|
395
|
-
!toWeb3JsPublicKey(supplyBank.group).equals(
|
396
|
-
new PublicKey(DEFAULT_MARGINFI_GROUP)
|
397
|
-
)
|
398
|
-
) {
|
399
|
-
// Temporarily disabled for now
|
400
|
-
return undefined;
|
401
|
-
}
|
402
|
-
|
403
386
|
if (!supplyUsage) {
|
404
387
|
supplyUsage = await getTokenUsage(
|
405
|
-
conn,
|
406
388
|
supplyBank,
|
407
389
|
true,
|
408
390
|
0,
|
@@ -419,7 +401,6 @@ export async function getMarginfiAccountPositionState(
|
|
419
401
|
|
420
402
|
if (!debtUsage) {
|
421
403
|
debtUsage = await getTokenUsage(
|
422
|
-
conn,
|
423
404
|
debtBank,
|
424
405
|
false,
|
425
406
|
0,
|
@@ -429,7 +410,6 @@ export async function getMarginfiAccountPositionState(
|
|
429
410
|
|
430
411
|
const supplyPrice = safeGetPrice(supply.mint!)!;
|
431
412
|
let [maxLtv, liqThreshold] = await getMaxLtvAndLiqThreshold(
|
432
|
-
conn,
|
433
413
|
umi,
|
434
414
|
marginfiGroup ?? new PublicKey(DEFAULT_MARGINFI_GROUP),
|
435
415
|
{
|
@@ -0,0 +1,154 @@
|
|
1
|
+
import { Connection, PublicKey } from "@solana/web3.js";
|
2
|
+
import { PublicKey as UmiPublicKey } from "@metaplex-foundation/umi";
|
3
|
+
import { PYTH_PRICE_FEED_IDS } from "../constants/pythConstants";
|
4
|
+
import { fromBaseUnit, toBaseUnit } from "./numberUtils";
|
5
|
+
import { PRICES } from "../constants/solautoConstants";
|
6
|
+
import { SWITCHBOARD_PRICE_FEED_IDS } from "../constants/switchboardConstants";
|
7
|
+
import {
|
8
|
+
currentUnixSeconds,
|
9
|
+
retryWithExponentialBackoff,
|
10
|
+
zip,
|
11
|
+
} from "./generalUtils";
|
12
|
+
import { getPullFeed } from "./switchboardUtils";
|
13
|
+
|
14
|
+
export async function fetchTokenPrices(
|
15
|
+
mints: PublicKey[]
|
16
|
+
): Promise<number[]> {
|
17
|
+
const currentTime = currentUnixSeconds();
|
18
|
+
if (
|
19
|
+
!mints.some(
|
20
|
+
(mint) =>
|
21
|
+
!(mint.toString() in PRICES) ||
|
22
|
+
currentTime - PRICES[mint.toString()].time > 3
|
23
|
+
)
|
24
|
+
) {
|
25
|
+
return mints.map((mint) => PRICES[mint.toString()].price);
|
26
|
+
}
|
27
|
+
|
28
|
+
const pythMints = mints.filter((x) => x.toString() in PYTH_PRICE_FEED_IDS);
|
29
|
+
const switchboardMints = mints.filter(
|
30
|
+
(x) => x.toString() in SWITCHBOARD_PRICE_FEED_IDS
|
31
|
+
);
|
32
|
+
|
33
|
+
const [pythData, switchboardData] = await Promise.all([
|
34
|
+
zip(pythMints, await getPythPrices(pythMints)),
|
35
|
+
zip(switchboardMints, await getJupTokenPrices(switchboardMints)),
|
36
|
+
]);
|
37
|
+
|
38
|
+
const prices = mints.map((mint) => {
|
39
|
+
const item = [...pythData, ...switchboardData].find((data) =>
|
40
|
+
data[0].equals(mint)
|
41
|
+
);
|
42
|
+
return item ? item[1] : 0;
|
43
|
+
});
|
44
|
+
|
45
|
+
for (var i = 0; i < mints.length; i++) {
|
46
|
+
PRICES[mints[i].toString()] = {
|
47
|
+
price: prices[i],
|
48
|
+
time: currentUnixSeconds(),
|
49
|
+
};
|
50
|
+
}
|
51
|
+
|
52
|
+
return prices;
|
53
|
+
}
|
54
|
+
|
55
|
+
export async function getPythPrices(mints: PublicKey[]) {
|
56
|
+
if (mints.length === 0) {
|
57
|
+
return [];
|
58
|
+
}
|
59
|
+
|
60
|
+
const priceFeedIds = mints.map(
|
61
|
+
(mint) => PYTH_PRICE_FEED_IDS[mint.toString()]
|
62
|
+
);
|
63
|
+
|
64
|
+
const getReq = async () =>
|
65
|
+
await fetch(
|
66
|
+
`https://hermes.pyth.network/v2/updates/price/latest?${priceFeedIds.map((x) => `ids%5B%5D=${x}`).join("&")}`
|
67
|
+
);
|
68
|
+
|
69
|
+
const prices: number[] = await retryWithExponentialBackoff(
|
70
|
+
async () => {
|
71
|
+
let resp = await getReq();
|
72
|
+
let status = resp.status;
|
73
|
+
if (status !== 200) {
|
74
|
+
throw new Error(JSON.stringify(resp));
|
75
|
+
}
|
76
|
+
|
77
|
+
const json = await resp.json();
|
78
|
+
const prices = json.parsed.map((x: any) => {
|
79
|
+
if (x.price.expo > 0) {
|
80
|
+
return Number(toBaseUnit(Number(x.price.price), x.price.expo));
|
81
|
+
} else if (x.price.expo < 0) {
|
82
|
+
return fromBaseUnit(BigInt(x.price.price), Math.abs(x.price.expo));
|
83
|
+
} else {
|
84
|
+
return Number(x.price.price);
|
85
|
+
}
|
86
|
+
});
|
87
|
+
|
88
|
+
return prices;
|
89
|
+
},
|
90
|
+
5,
|
91
|
+
200
|
92
|
+
);
|
93
|
+
|
94
|
+
return prices;
|
95
|
+
}
|
96
|
+
|
97
|
+
export async function getSwitchboardPrices(
|
98
|
+
conn: Connection,
|
99
|
+
mints: PublicKey[]
|
100
|
+
): Promise<{ mint: PublicKey; price: number; stale: boolean }[]> {
|
101
|
+
if (mints.length === 0) {
|
102
|
+
return [];
|
103
|
+
}
|
104
|
+
|
105
|
+
const currSlot = await retryWithExponentialBackoff(
|
106
|
+
async () => await conn.getSlot("confirmed"),
|
107
|
+
5
|
108
|
+
);
|
109
|
+
|
110
|
+
const results = await Promise.all(
|
111
|
+
mints.map(async (mint) => {
|
112
|
+
const feed = getPullFeed(conn, mint);
|
113
|
+
const result = await feed.loadData();
|
114
|
+
const price = Number(result.result.value) / Math.pow(10, 18);
|
115
|
+
const stale =
|
116
|
+
currSlot > result.result.slot.toNumber() + result.maxStaleness;
|
117
|
+
|
118
|
+
return { mint, price, stale };
|
119
|
+
})
|
120
|
+
);
|
121
|
+
|
122
|
+
return results;
|
123
|
+
}
|
124
|
+
|
125
|
+
export function safeGetPrice(
|
126
|
+
mint: PublicKey | UmiPublicKey | undefined
|
127
|
+
): number | undefined {
|
128
|
+
if (mint && mint?.toString() in PRICES) {
|
129
|
+
return PRICES[mint!.toString()].price;
|
130
|
+
}
|
131
|
+
return undefined;
|
132
|
+
}
|
133
|
+
|
134
|
+
export async function getJupTokenPrices(mints: PublicKey[]) {
|
135
|
+
if (mints.length == 0) {
|
136
|
+
return [];
|
137
|
+
}
|
138
|
+
|
139
|
+
const data = await retryWithExponentialBackoff(async () => {
|
140
|
+
const res = (
|
141
|
+
await fetch(
|
142
|
+
"https://api.jup.ag/price/v2?ids=" +
|
143
|
+
mints.map((x) => x.toString()).join(",")
|
144
|
+
)
|
145
|
+
).json();
|
146
|
+
return res;
|
147
|
+
}, 6);
|
148
|
+
|
149
|
+
const prices = Object.values(data.data as { [key: string]: any }).map(
|
150
|
+
(x) => parseFloat(x.price as string) as number
|
151
|
+
);
|
152
|
+
|
153
|
+
return prices;
|
154
|
+
}
|
package/src/utils/solanaUtils.ts
CHANGED
@@ -16,7 +16,6 @@ import {
|
|
16
16
|
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
|
17
17
|
import {
|
18
18
|
AddressLookupTableAccount,
|
19
|
-
Blockhash,
|
20
19
|
BlockhashWithExpiryBlockHeight,
|
21
20
|
ComputeBudgetProgram,
|
22
21
|
Connection,
|
@@ -318,7 +317,7 @@ async function spamSendTransactionUntilConfirmed(
|
|
318
317
|
connection: Connection,
|
319
318
|
transaction: Transaction | VersionedTransaction,
|
320
319
|
blockhash: BlockhashWithExpiryBlockHeight,
|
321
|
-
spamInterval: number =
|
320
|
+
spamInterval: number = 3000
|
322
321
|
): Promise<string> {
|
323
322
|
let transactionSignature: string | null = null;
|
324
323
|
|
@@ -22,7 +22,7 @@ import {
|
|
22
22
|
getSolautoPositionAccountDataSerializer,
|
23
23
|
getSolautoPositionSize,
|
24
24
|
} from "../../generated";
|
25
|
-
import { currentUnixSeconds
|
25
|
+
import { currentUnixSeconds } from "../generalUtils";
|
26
26
|
import {
|
27
27
|
fromBaseUnit,
|
28
28
|
getLiqUtilzationRateBps,
|
@@ -40,6 +40,7 @@ import {
|
|
40
40
|
getAllMarginfiAccountsByAuthority,
|
41
41
|
} from "../marginfiUtils";
|
42
42
|
import { RebalanceAction, SolautoPositionDetails } from "../../types/solauto";
|
43
|
+
import { fetchTokenPrices } from "../priceUtils";
|
43
44
|
|
44
45
|
export function createDynamicSolautoProgram(programId: PublicKey): Program {
|
45
46
|
return {
|
@@ -403,7 +404,7 @@ export async function positionStateWithLatestPrices(
|
|
403
404
|
debtPrice?: number
|
404
405
|
): Promise<PositionState> {
|
405
406
|
if (!supplyPrice || !debtPrice) {
|
406
|
-
[supplyPrice, debtPrice] = await fetchTokenPrices(
|
407
|
+
[supplyPrice, debtPrice] = await fetchTokenPrices([
|
407
408
|
toWeb3JsPublicKey(state.supply.mint),
|
408
409
|
toWeb3JsPublicKey(state.debt.mint),
|
409
410
|
]);
|
@@ -16,7 +16,7 @@ import {
|
|
16
16
|
import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
|
17
17
|
import { QuoteResponse } from "@jup-ag/api";
|
18
18
|
import { JupSwapDetails } from "../jupiterUtils";
|
19
|
-
import { currentUnixSeconds
|
19
|
+
import { currentUnixSeconds } from "../generalUtils";
|
20
20
|
import {
|
21
21
|
fromBaseUnit,
|
22
22
|
fromBps,
|
@@ -29,6 +29,7 @@ import {
|
|
29
29
|
} from "../numberUtils";
|
30
30
|
import { USD_DECIMALS } from "../../constants/generalAccounts";
|
31
31
|
import { RebalanceAction } from "../../types";
|
32
|
+
import { safeGetPrice } from "../priceUtils";
|
32
33
|
|
33
34
|
function getAdditionalAmountToDcaIn(dca: DCASettings): number {
|
34
35
|
if (dca.dcaInBaseUnit === BigInt(0)) {
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import { AnchorProvider, Idl, Program } from "@coral-xyz/anchor";
|
2
|
+
import switchboardIdl from "../idls/switchboard.json";
|
3
|
+
import {
|
4
|
+
Connection,
|
5
|
+
PublicKey,
|
6
|
+
Transaction,
|
7
|
+
VersionedTransaction,
|
8
|
+
} from "@solana/web3.js";
|
9
|
+
import { CrossbarClient, PullFeed } from "@switchboard-xyz/on-demand";
|
10
|
+
import { SWITCHBOARD_PRICE_FEED_IDS } from "../constants/switchboardConstants";
|
11
|
+
import { TransactionItemInputs } from "../types";
|
12
|
+
import { Signer, transactionBuilder } from "@metaplex-foundation/umi";
|
13
|
+
import {
|
14
|
+
fromWeb3JsInstruction,
|
15
|
+
toWeb3JsPublicKey,
|
16
|
+
} from "@metaplex-foundation/umi-web3js-adapters";
|
17
|
+
|
18
|
+
export function getPullFeed(
|
19
|
+
conn: Connection,
|
20
|
+
mint: PublicKey,
|
21
|
+
wallet?: PublicKey
|
22
|
+
) {
|
23
|
+
const dummyWallet = {
|
24
|
+
publicKey: wallet ?? new PublicKey("11111111111111111111111111111111"),
|
25
|
+
signTransaction: async <T extends Transaction | VersionedTransaction>(
|
26
|
+
tx: T
|
27
|
+
): Promise<T> => tx,
|
28
|
+
signAllTransactions: async <T extends Transaction | VersionedTransaction>(
|
29
|
+
txs: T[]
|
30
|
+
): Promise<T[]> => txs,
|
31
|
+
};
|
32
|
+
const provider = new AnchorProvider(
|
33
|
+
conn,
|
34
|
+
dummyWallet,
|
35
|
+
AnchorProvider.defaultOptions()
|
36
|
+
);
|
37
|
+
const program = new Program(switchboardIdl as Idl, provider);
|
38
|
+
|
39
|
+
return new PullFeed(
|
40
|
+
program,
|
41
|
+
new PublicKey(SWITCHBOARD_PRICE_FEED_IDS[mint.toString()])
|
42
|
+
);
|
43
|
+
}
|
44
|
+
|
45
|
+
export async function buildSwbSubmitResponseTx(
|
46
|
+
conn: Connection,
|
47
|
+
signer: Signer,
|
48
|
+
mint: PublicKey
|
49
|
+
): Promise<TransactionItemInputs | undefined> {
|
50
|
+
const crossbar = new CrossbarClient("https://crossbar.switchboard.xyz");
|
51
|
+
const feed = getPullFeed(conn, mint, toWeb3JsPublicKey(signer.publicKey));
|
52
|
+
const [pullIx, responses] = await feed.fetchUpdateIx({
|
53
|
+
crossbarClient: crossbar,
|
54
|
+
});
|
55
|
+
|
56
|
+
return {
|
57
|
+
tx: transactionBuilder().add({
|
58
|
+
bytesCreatedOnChain: 0,
|
59
|
+
instruction: fromWeb3JsInstruction(pullIx!),
|
60
|
+
signers: [signer],
|
61
|
+
}),
|
62
|
+
lookupTableAddresses: responses
|
63
|
+
.filter((x) => Boolean(x.oracle.lut?.key))
|
64
|
+
.map((x) => x.oracle.lut!.key.toString()),
|
65
|
+
};
|
66
|
+
}
|