@haven-fi/solauto-sdk 1.0.309 → 1.0.311
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/constants/solautoConstants.d.ts +1 -0
- package/dist/constants/solautoConstants.d.ts.map +1 -1
- package/dist/constants/solautoConstants.js +2 -7
- package/dist/transactions/transactionsManager.d.ts +3 -1
- package/dist/transactions/transactionsManager.d.ts.map +1 -1
- package/dist/transactions/transactionsManager.js +35 -7
- package/dist/utils/jitoUtils.d.ts +5 -0
- package/dist/utils/jitoUtils.d.ts.map +1 -1
- package/dist/utils/jitoUtils.js +98 -151
- package/dist/utils/priceUtils.d.ts +1 -6
- package/dist/utils/priceUtils.d.ts.map +1 -1
- package/dist/utils/priceUtils.js +3 -17
- package/dist/utils/solanaUtils.js +1 -1
- package/dist/utils/switchboardUtils.d.ts +6 -0
- package/dist/utils/switchboardUtils.d.ts.map +1 -1
- package/dist/utils/switchboardUtils.js +27 -2
- package/package.json +2 -1
- package/src/constants/solautoConstants.ts +1 -6
- package/src/transactions/transactionsManager.ts +74 -7
- package/src/utils/jitoUtils.ts +166 -166
- package/src/utils/priceUtils.ts +6 -32
- package/src/utils/solanaUtils.ts +1 -1
- package/src/utils/switchboardUtils.ts +58 -3
- package/tests/transactions/solautoMarginfi.ts +27 -26
@@ -28,8 +28,8 @@ import {
|
|
28
28
|
TransactionExpiredBlockheightExceededError,
|
29
29
|
} from "@solana/web3.js";
|
30
30
|
import { SWITCHBOARD_PRICE_FEED_IDS } from "../constants/switchboardConstants";
|
31
|
-
import { buildSwbSubmitResponseTx,
|
32
|
-
|
31
|
+
import { buildSwbSubmitResponseTx, getSwitchboardFeedData } from "../utils";
|
32
|
+
import { sendJitoBundledTransactions } from "../utils/jitoUtils";
|
33
33
|
|
34
34
|
const CHORES_TX_NAME = "account chores";
|
35
35
|
|
@@ -220,6 +220,7 @@ export class TransactionsManager {
|
|
220
220
|
private statusCallback?: (statuses: TransactionManagerStatuses) => void,
|
221
221
|
private txType?: TransactionRunType,
|
222
222
|
private priorityFeeSetting: PriorityFeeSetting = PriorityFeeSetting.Min,
|
223
|
+
private atomically: boolean = false,
|
223
224
|
private errorsToThrow?: ErrorsToThrow,
|
224
225
|
private retries: number = 4,
|
225
226
|
private retryDelay: number = 150
|
@@ -404,7 +405,7 @@ export class TransactionsManager {
|
|
404
405
|
(x) => SWITCHBOARD_PRICE_FEED_IDS[x] === swbOracle
|
405
406
|
)!
|
406
407
|
);
|
407
|
-
const stale = (await
|
408
|
+
const stale = (await getSwitchboardFeedData(client.connection, [mint]))[0]
|
408
409
|
.stale;
|
409
410
|
|
410
411
|
if (stale) {
|
@@ -489,15 +490,81 @@ export class TransactionsManager {
|
|
489
490
|
return [];
|
490
491
|
}
|
491
492
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
currentIndex
|
493
|
+
if (itemSets.length > 1 && this.atomically) {
|
494
|
+
await this.processTransactionsAtomically(itemSets);
|
495
|
+
} else {
|
496
|
+
let currentIndex = 0;
|
497
|
+
while (currentIndex < itemSets.length) {
|
498
|
+
await this.processTransactionSet(itemSets, currentIndex);
|
499
|
+
currentIndex++;
|
500
|
+
}
|
496
501
|
}
|
497
502
|
|
498
503
|
return this.statuses;
|
499
504
|
}
|
500
505
|
|
506
|
+
private async processTransactionsAtomically(itemSets: TransactionSet[]) {
|
507
|
+
let num = 0;
|
508
|
+
|
509
|
+
await retryWithExponentialBackoff(
|
510
|
+
async (attemptNum) => {
|
511
|
+
num = attemptNum;
|
512
|
+
|
513
|
+
let transactions = [];
|
514
|
+
for (const set of itemSets) {
|
515
|
+
transactions.push(await set.getSingleTransaction());
|
516
|
+
}
|
517
|
+
|
518
|
+
itemSets.forEach((x) =>
|
519
|
+
this.updateStatus(x.name(), TransactionStatus.Processing, attemptNum)
|
520
|
+
);
|
521
|
+
const txSigs = await sendJitoBundledTransactions(
|
522
|
+
this.txHandler.umi,
|
523
|
+
this.txHandler.signer,
|
524
|
+
transactions,
|
525
|
+
false,
|
526
|
+
this.priorityFeeSetting
|
527
|
+
);
|
528
|
+
if (txSigs) {
|
529
|
+
itemSets.forEach((x, i) =>
|
530
|
+
this.updateStatus(
|
531
|
+
x.name(),
|
532
|
+
TransactionStatus.Successful,
|
533
|
+
attemptNum,
|
534
|
+
txSigs[i]
|
535
|
+
)
|
536
|
+
);
|
537
|
+
} else {
|
538
|
+
itemSets.forEach((x) =>
|
539
|
+
this.updateStatus(
|
540
|
+
x.name(),
|
541
|
+
TransactionStatus.Failed,
|
542
|
+
attemptNum,
|
543
|
+
undefined,
|
544
|
+
true
|
545
|
+
)
|
546
|
+
);
|
547
|
+
throw new Error("Unknown error");
|
548
|
+
}
|
549
|
+
},
|
550
|
+
this.retries,
|
551
|
+
this.retryDelay,
|
552
|
+
this.errorsToThrow
|
553
|
+
).catch((e: Error) => {
|
554
|
+
itemSets.forEach((x) =>
|
555
|
+
this.updateStatus(
|
556
|
+
x.name(),
|
557
|
+
TransactionStatus.Failed,
|
558
|
+
num,
|
559
|
+
undefined,
|
560
|
+
true,
|
561
|
+
e.message
|
562
|
+
)
|
563
|
+
);
|
564
|
+
throw e;
|
565
|
+
});
|
566
|
+
}
|
567
|
+
|
501
568
|
private async processTransactionSet(
|
502
569
|
itemSets: TransactionSet[],
|
503
570
|
currentIndex: number
|
package/src/utils/jitoUtils.ts
CHANGED
@@ -1,68 +1,47 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
// let isLeaderSlot = false;
|
46
|
-
|
47
|
-
// while (!isLeaderSlot) {
|
48
|
-
// const nextLeader = await searcher.getNextScheduledLeader();
|
49
|
-
// const numSlots = nextLeader.nextLeaderSlot - nextLeader.currentSlot;
|
50
|
-
// isLeaderSlot = numSlots <= distanceFromJitoSlot && numSlots > 1;
|
51
|
-
// consoleLog(`Next jito leader slot in ${numSlots} slots`);
|
52
|
-
// await new Promise((r) => setTimeout(r, 500));
|
53
|
-
// }
|
54
|
-
// }
|
55
|
-
|
56
|
-
// async function getTipInstruction(
|
57
|
-
// client: SolautoClient,
|
58
|
-
// tipLamports: number
|
59
|
-
// ): Promise<WrappedInstruction> {
|
60
|
-
// return systemTransferUmiIx(
|
61
|
-
// client.signer,
|
62
|
-
// await getRandomTipAccount(),
|
63
|
-
// BigInt(tipLamports)
|
64
|
-
// );
|
65
|
-
// }
|
1
|
+
import { PublicKey, VersionedTransaction } from "@solana/web3.js";
|
2
|
+
import { toWeb3JsTransaction } from "@metaplex-foundation/umi-web3js-adapters";
|
3
|
+
import { JITO_BLOCK_ENGINE } from "../constants/solautoConstants";
|
4
|
+
import {
|
5
|
+
Signer,
|
6
|
+
TransactionBuilder,
|
7
|
+
Umi,
|
8
|
+
WrappedInstruction,
|
9
|
+
} from "@metaplex-foundation/umi";
|
10
|
+
import {
|
11
|
+
assembleFinalTransaction,
|
12
|
+
getComputeUnitPriceEstimate,
|
13
|
+
systemTransferUmiIx,
|
14
|
+
} from "./solanaUtils";
|
15
|
+
import { consoleLog } from "./generalUtils";
|
16
|
+
import { PriorityFeeSetting } from "../types";
|
17
|
+
import axios from "axios";
|
18
|
+
import base58 from "bs58";
|
19
|
+
|
20
|
+
export async function getRandomTipAccount(): Promise<PublicKey> {
|
21
|
+
const tipAccounts = [
|
22
|
+
"96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5",
|
23
|
+
"HFqU5x63VTqvQss8hp11i4wVV8bD44PvwucfZ2bU7gRe",
|
24
|
+
"Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY",
|
25
|
+
"ADaUMid9yfUytqMBgopwjb2DTLSokTSzL1zt6iGPaS49",
|
26
|
+
"DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh",
|
27
|
+
"ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt",
|
28
|
+
"DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL",
|
29
|
+
"3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT",
|
30
|
+
];
|
31
|
+
const randomInt = Math.floor(Math.random() * tipAccounts.length);
|
32
|
+
return new PublicKey(tipAccounts[randomInt]);
|
33
|
+
}
|
34
|
+
|
35
|
+
async function getTipInstruction(
|
36
|
+
signer: Signer,
|
37
|
+
tipLamports: number
|
38
|
+
): Promise<WrappedInstruction> {
|
39
|
+
return systemTransferUmiIx(
|
40
|
+
signer,
|
41
|
+
await getRandomTipAccount(),
|
42
|
+
BigInt(tipLamports)
|
43
|
+
);
|
44
|
+
}
|
66
45
|
|
67
46
|
// async function simulateJitoBundle(
|
68
47
|
// txs: VersionedTransaction[]
|
@@ -85,104 +64,125 @@
|
|
85
64
|
// return simulationResult.value.transactionResults;
|
86
65
|
// }
|
87
66
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
67
|
+
async function umiToVersionedTransactions(
|
68
|
+
umi: Umi,
|
69
|
+
signer: Signer,
|
70
|
+
txs: TransactionBuilder[],
|
71
|
+
feeEstimates: number[],
|
72
|
+
computeUnitLimits?: number[]
|
73
|
+
): Promise<VersionedTransaction[]> {
|
74
|
+
return await Promise.all(
|
75
|
+
txs.map(async (tx, i) => {
|
76
|
+
const versionedTx = toWeb3JsTransaction(
|
77
|
+
await (
|
78
|
+
await assembleFinalTransaction(
|
79
|
+
signer,
|
80
|
+
tx,
|
81
|
+
feeEstimates[i],
|
82
|
+
computeUnitLimits ? computeUnitLimits[i] : undefined
|
83
|
+
).setLatestBlockhash(umi)
|
84
|
+
).buildAndSign(umi)
|
85
|
+
);
|
86
|
+
return versionedTx;
|
87
|
+
})
|
88
|
+
);
|
89
|
+
}
|
90
|
+
|
91
|
+
async function getBundleStatus(bundleId: string) {
|
92
|
+
const res = await axios.post(`${JITO_BLOCK_ENGINE}/api/v1/bundles`, {
|
93
|
+
jsonrpc: "2.0",
|
94
|
+
id: 1,
|
95
|
+
method: "getBundleStatuses",
|
96
|
+
params: [[bundleId]],
|
97
|
+
});
|
98
|
+
if (res.data.error) {
|
99
|
+
throw new Error(`Failed to get bundle status: ${res.data.error}`);
|
100
|
+
}
|
101
|
+
|
102
|
+
return res.data.result;
|
103
|
+
}
|
104
|
+
|
105
|
+
async function pollBundleStatus(
|
106
|
+
bundleId: string,
|
107
|
+
interval = 1000,
|
108
|
+
timeout = 40000
|
109
|
+
): Promise<string[]> {
|
110
|
+
const endTime = Date.now() + timeout;
|
111
|
+
while (Date.now() < endTime) {
|
112
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
113
|
+
const statuses = await getBundleStatus(bundleId);
|
114
|
+
if (statuses?.value?.length > 0) {
|
115
|
+
const status = statuses.value[0].confirmation_status;
|
116
|
+
if (status === "confirmed") {
|
117
|
+
return statuses?.value[0].transactions as string[];
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
return [];
|
122
|
+
}
|
123
|
+
|
124
|
+
async function sendJitoBundle(transactions: string[]): Promise<string[]> {
|
125
|
+
consoleLog("Sending bundle...");
|
126
|
+
const resp = await axios.post<{ result: string }>(
|
127
|
+
`${JITO_BLOCK_ENGINE}/api/v1/bundles`,
|
128
|
+
{
|
129
|
+
jsonrpc: "2.0",
|
130
|
+
id: 1,
|
131
|
+
method: "sendBundle",
|
132
|
+
params: [transactions],
|
133
|
+
}
|
134
|
+
);
|
135
|
+
|
136
|
+
const bundleId = resp.data.result;
|
137
|
+
return await pollBundleStatus(bundleId);
|
138
|
+
}
|
139
|
+
|
140
|
+
export async function sendJitoBundledTransactions(
|
141
|
+
umi: Umi,
|
142
|
+
signer: Signer,
|
143
|
+
txs: TransactionBuilder[],
|
144
|
+
simulateOnly?: boolean,
|
145
|
+
priorityFeeSetting: PriorityFeeSetting = PriorityFeeSetting.Min
|
146
|
+
): Promise<string[] | undefined> {
|
147
|
+
consoleLog("Sending Jito bundle...");
|
148
|
+
consoleLog("Transactions: ", txs.length);
|
149
|
+
consoleLog(
|
150
|
+
"Transaction sizes: ",
|
151
|
+
txs.map((x) => x.getTransactionSize(umi))
|
152
|
+
);
|
153
|
+
|
154
|
+
txs[0] = txs[0].prepend(await getTipInstruction(signer, 150_000));
|
155
|
+
const feeEstimates = await Promise.all(
|
156
|
+
txs.map(
|
157
|
+
async (x) =>
|
158
|
+
(await getComputeUnitPriceEstimate(umi, x, priorityFeeSetting)) ??
|
159
|
+
1000000
|
160
|
+
)
|
161
|
+
);
|
162
|
+
|
163
|
+
let builtTxs = await umiToVersionedTransactions(
|
164
|
+
umi,
|
165
|
+
signer,
|
166
|
+
txs,
|
167
|
+
feeEstimates
|
168
|
+
// Array(txs.length).fill(1_400_000)
|
169
|
+
);
|
170
|
+
// // TODO: Skip over this for now, and instead don't specify a compute unit limit in the final bundle transactions
|
171
|
+
// const simulationResults = await simulateJitoBundle(builtTxs);
|
172
|
+
|
173
|
+
if (!simulateOnly) {
|
174
|
+
// let builtTxs = await umiToVersionedTransactions(
|
175
|
+
// client.signer,
|
176
|
+
// txs,
|
177
|
+
// feeEstimates,
|
178
|
+
// simulationResults.map((x) => x.unitsConsumed! * 1.15)
|
179
|
+
// );
|
180
|
+
|
181
|
+
const txSigs = await sendJitoBundle(
|
182
|
+
builtTxs.map((x) => base58.encode(x.serialize()))
|
183
|
+
);
|
184
|
+
return txSigs.length > 0 ? txSigs : undefined;
|
185
|
+
}
|
186
|
+
|
187
|
+
return undefined;
|
188
|
+
}
|
package/src/utils/priceUtils.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
2
2
|
import { PublicKey as UmiPublicKey } from "@metaplex-foundation/umi";
|
3
3
|
import { PYTH_PRICE_FEED_IDS } from "../constants/pythConstants";
|
4
4
|
import { fromBaseUnit, toBaseUnit } from "./numberUtils";
|
@@ -9,7 +9,7 @@ import {
|
|
9
9
|
retryWithExponentialBackoff,
|
10
10
|
zip,
|
11
11
|
} from "./generalUtils";
|
12
|
-
import {
|
12
|
+
import { getSwitchboardPrices } from "./switchboardUtils";
|
13
13
|
|
14
14
|
export async function fetchTokenPrices(
|
15
15
|
mints: PublicKey[]
|
@@ -32,7 +32,7 @@ export async function fetchTokenPrices(
|
|
32
32
|
|
33
33
|
const [pythData, switchboardData] = await Promise.all([
|
34
34
|
zip(pythMints, await getPythPrices(pythMints)),
|
35
|
-
zip(switchboardMints, await
|
35
|
+
zip(switchboardMints, await getSwitchboardPrices(switchboardMints)),
|
36
36
|
]);
|
37
37
|
|
38
38
|
const prices = mints.map((mint) => {
|
@@ -94,34 +94,6 @@ export async function getPythPrices(mints: PublicKey[]) {
|
|
94
94
|
return prices;
|
95
95
|
}
|
96
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
97
|
export function safeGetPrice(
|
126
98
|
mint: PublicKey | UmiPublicKey | undefined
|
127
99
|
): number | undefined {
|
@@ -140,12 +112,14 @@ export async function getJupTokenPrices(mints: PublicKey[]) {
|
|
140
112
|
const res = (
|
141
113
|
await fetch(
|
142
114
|
"https://api.jup.ag/price/v2?ids=" +
|
143
|
-
mints.map((x) => x.toString()).join(",")
|
115
|
+
mints.map((x) => x.toString()).join(",") + "&showExtraInfo=true"
|
144
116
|
)
|
145
117
|
).json();
|
146
118
|
return res;
|
147
119
|
}, 6);
|
148
120
|
|
121
|
+
console.log(data.data[mints[0].toString()].extraInfo.quotedPrice);
|
122
|
+
|
149
123
|
const prices = Object.values(data.data as { [key: string]: any }).map(
|
150
124
|
(x) => parseFloat(x.price as string) as number
|
151
125
|
);
|
package/src/utils/solanaUtils.ts
CHANGED
@@ -317,7 +317,7 @@ async function spamSendTransactionUntilConfirmed(
|
|
317
317
|
connection: Connection,
|
318
318
|
transaction: Transaction | VersionedTransaction,
|
319
319
|
blockhash: BlockhashWithExpiryBlockHeight,
|
320
|
-
spamInterval: number =
|
320
|
+
spamInterval: number = 1500
|
321
321
|
): Promise<string> {
|
322
322
|
let transactionSignature: string | null = null;
|
323
323
|
|
@@ -14,6 +14,7 @@ import {
|
|
14
14
|
fromWeb3JsInstruction,
|
15
15
|
toWeb3JsPublicKey,
|
16
16
|
} from "@metaplex-foundation/umi-web3js-adapters";
|
17
|
+
import { retryWithExponentialBackoff } from "./generalUtils";
|
17
18
|
|
18
19
|
export function getPullFeed(
|
19
20
|
conn: Connection,
|
@@ -49,9 +50,14 @@ export async function buildSwbSubmitResponseTx(
|
|
49
50
|
): Promise<TransactionItemInputs | undefined> {
|
50
51
|
const crossbar = new CrossbarClient("https://crossbar.switchboard.xyz");
|
51
52
|
const feed = getPullFeed(conn, mint, toWeb3JsPublicKey(signer.publicKey));
|
52
|
-
const [pullIx, responses] = await
|
53
|
-
|
54
|
-
|
53
|
+
const [pullIx, responses] = await retryWithExponentialBackoff(
|
54
|
+
async () =>
|
55
|
+
await feed.fetchUpdateIx({
|
56
|
+
crossbarClient: crossbar,
|
57
|
+
}),
|
58
|
+
8,
|
59
|
+
200
|
60
|
+
);
|
55
61
|
|
56
62
|
return {
|
57
63
|
tx: transactionBuilder().add({
|
@@ -64,3 +70,52 @@ export async function buildSwbSubmitResponseTx(
|
|
64
70
|
.map((x) => x.oracle.lut!.key.toString()),
|
65
71
|
};
|
66
72
|
}
|
73
|
+
|
74
|
+
export async function getSwitchboardFeedData(
|
75
|
+
conn: Connection,
|
76
|
+
mints: PublicKey[]
|
77
|
+
): Promise<{ mint: PublicKey; price: number; stale: boolean }[]> {
|
78
|
+
if (mints.length === 0) {
|
79
|
+
return [];
|
80
|
+
}
|
81
|
+
|
82
|
+
const currSlot = await retryWithExponentialBackoff(
|
83
|
+
async () => await conn.getSlot("confirmed"),
|
84
|
+
5
|
85
|
+
);
|
86
|
+
|
87
|
+
const results = await Promise.all(
|
88
|
+
mints.map(async (mint) => {
|
89
|
+
const feed = getPullFeed(conn, mint);
|
90
|
+
const result = await feed.loadData();
|
91
|
+
const price = Number(result.result.value) / Math.pow(10, 18);
|
92
|
+
const stale =
|
93
|
+
currSlot > result.result.slot.toNumber() + result.maxStaleness;
|
94
|
+
|
95
|
+
return { mint, price, stale };
|
96
|
+
})
|
97
|
+
);
|
98
|
+
|
99
|
+
return results;
|
100
|
+
}
|
101
|
+
|
102
|
+
export async function getSwitchboardPrices(
|
103
|
+
mints: PublicKey[]
|
104
|
+
): Promise<number[]> {
|
105
|
+
if (mints.length === 0) {
|
106
|
+
return [];
|
107
|
+
}
|
108
|
+
|
109
|
+
const crossbar = new CrossbarClient("https://crossbar.switchboard.xyz");
|
110
|
+
const results = await retryWithExponentialBackoff(
|
111
|
+
async () =>
|
112
|
+
await crossbar.simulateSolanaFeeds(
|
113
|
+
"mainnet",
|
114
|
+
mints.map((x) => SWITCHBOARD_PRICE_FEED_IDS[x.toString()])
|
115
|
+
),
|
116
|
+
8,
|
117
|
+
200
|
118
|
+
);
|
119
|
+
|
120
|
+
return results.flatMap((x) => x.results[0]);
|
121
|
+
}
|