@haven-fi/solauto-sdk 1.0.687 → 1.0.689
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/services/transactions/manager/clientTransactionsManager.js +2 -2
- package/dist/services/transactions/manager/transactionsManager.js +3 -1
- package/dist/utils/generalUtils.d.ts +1 -0
- package/dist/utils/generalUtils.d.ts.map +1 -1
- package/dist/utils/generalUtils.js +4 -0
- package/dist/utils/priceUtils.d.ts +3 -3
- package/dist/utils/priceUtils.d.ts.map +1 -1
- package/dist/utils/priceUtils.js +43 -47
- package/local/txSandbox.ts +67 -13
- package/package.json +1 -1
- package/src/services/rebalance/rebalanceSwapManager.ts +8 -7
- package/src/services/rebalance/rebalanceTxBuilder.ts +2 -1
- package/src/services/solauto/solautoClient.ts +1 -0
- package/src/services/transactions/manager/clientTransactionsManager.ts +3 -3
- package/src/services/transactions/manager/transactionsManager.ts +5 -5
- package/src/utils/generalUtils.ts +13 -2
- package/src/utils/priceUtils.ts +62 -72
- package/src/utils/switchboardUtils.ts +10 -5
@@ -25,8 +25,8 @@ class ClientTransactionsManager extends transactionsManager_1.TransactionsManage
|
|
25
25
|
];
|
26
26
|
if (txs.find((x) => x.oracleInteractor) && switchboardMints.length) {
|
27
27
|
this.txHandler.log("Requires oracle update(s)...");
|
28
|
-
const
|
29
|
-
txs.unshift(...
|
28
|
+
const oracleTxs = switchboardMints.map((x) => new types_1.TransactionItem(async () => await (0, utils_1.buildSwbSubmitResponseTx)(this.txHandler.connection, this.txHandler.signer, x), this.updateOracleTxName));
|
29
|
+
txs.unshift(...oracleTxs);
|
30
30
|
}
|
31
31
|
}
|
32
32
|
async addChoreTxs(txs, updateLutTx) {
|
@@ -260,7 +260,9 @@ class TransactionsManager {
|
|
260
260
|
}
|
261
261
|
else {
|
262
262
|
await Promise.all(itemSets.map((itemSet) => itemSet.reset()));
|
263
|
-
|
263
|
+
for (const itemSet of itemSets) {
|
264
|
+
await itemSet.refetchAll(attemptNum, prevError);
|
265
|
+
}
|
264
266
|
}
|
265
267
|
const newItemSets = await this.assembleTransactionSets(currentIndex !== undefined
|
266
268
|
? [
|
@@ -21,4 +21,5 @@ export declare function toEnumValue<E extends object>(enumObj: E, value: number)
|
|
21
21
|
export declare function customRpcCall(umi: Umi, method: string, params?: any): Promise<any>;
|
22
22
|
export declare function u16ToArrayBufferLE(value: number): Uint8Array;
|
23
23
|
export declare function validPubkey(pubkey?: PublicKey | UmiPublicKey | string): boolean;
|
24
|
+
export declare function createRecord<T>(keys: string[], values: T[]): Record<string, T>;
|
24
25
|
//# sourceMappingURL=generalUtils.d.ts.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"generalUtils.d.ts","sourceRoot":"","sources":["../../src/utils/generalUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,
|
1
|
+
{"version":3,"file":"generalUtils.d.ts","sourceRoot":"","sources":["../../src/utils/generalUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EACL,eAAe,EAEf,GAAG,EACH,SAAS,IAAI,YAAY,EAC1B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAc,SAAS,EAAE,MAAM,cAAc,CAAC;AAErD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,UAErD;AAED,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,UAE3D;AAED,wBAAgB,UAAU,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAI/C;AAED,wBAAgB,SAAS,CAAC,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAErD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAW1D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,yBAQ/C;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC,EAAE,EAAE,CAMlE;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAO1C;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,GAAG,EACR,EAAE,EAAE,SAAS,GACZ,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAEnE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAU1E;AAED,wBAAgB,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAS1D;AAED,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,KAAK,CAAC,CAAC;AAEjE,wBAAgB,2BAA2B,CAAC,CAAC,EAC3C,EAAE,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EACvD,OAAO,GAAE,MAAU,EACnB,KAAK,GAAE,MAAY,EACnB,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,CAAC,CAAC,CA8BZ;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAC1C,OAAO,EAAE,CAAC,EACV,KAAK,EAAE,MAAM,GACZ,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAUxB;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,gBAuBzE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAU5D;AAED,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,YAAY,GAAG,MAAM,WAErE;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAI9E"}
|
@@ -22,6 +22,7 @@ exports.toEnumValue = toEnumValue;
|
|
22
22
|
exports.customRpcCall = customRpcCall;
|
23
23
|
exports.u16ToArrayBufferLE = u16ToArrayBufferLE;
|
24
24
|
exports.validPubkey = validPubkey;
|
25
|
+
exports.createRecord = createRecord;
|
25
26
|
const axios_1 = __importDefault(require("axios"));
|
26
27
|
const web3_js_1 = require("@solana/web3.js");
|
27
28
|
const umi_1 = require("@metaplex-foundation/umi");
|
@@ -173,3 +174,6 @@ function u16ToArrayBufferLE(value) {
|
|
173
174
|
function validPubkey(pubkey) {
|
174
175
|
return Boolean(pubkey) && pubkey.toString() !== web3_js_1.PublicKey.default.toString();
|
175
176
|
}
|
177
|
+
function createRecord(keys, values) {
|
178
|
+
return Object.fromEntries(zip(keys, values).map(([k, v]) => [k.toString(), v]));
|
179
|
+
}
|
@@ -6,9 +6,9 @@ interface PriceResult {
|
|
6
6
|
emaPrice?: number;
|
7
7
|
}
|
8
8
|
export declare function fetchTokenPrices(mints: PublicKey[], priceType?: PriceType): Promise<number[]>;
|
9
|
-
export declare function getPythPrices(mints: PublicKey[]
|
10
|
-
export declare function getSwitchboardPrices(mints: PublicKey[]): Promise<PriceResult
|
11
|
-
export declare function getJupTokenPrices(mints: PublicKey[]): Promise<PriceResult
|
9
|
+
export declare function getPythPrices(mints: PublicKey[]): Promise<Record<string, PriceResult>>;
|
10
|
+
export declare function getSwitchboardPrices(mints: PublicKey[]): Promise<Record<string, PriceResult>>;
|
11
|
+
export declare function getJupTokenPrices(mints: PublicKey[]): Promise<Record<string, PriceResult>>;
|
12
12
|
export declare function safeGetPrice(mint: PublicKey | UmiPublicKey | string | undefined, priceType?: PriceType): number | undefined;
|
13
13
|
export {};
|
14
14
|
//# sourceMappingURL=priceUtils.d.ts.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"priceUtils.d.ts","sourceRoot":"","sources":["../../src/utils/priceUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,IAAI,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAerE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,UAAU,WAAW;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,SAAS,EAAE,EAClB,SAAS,GAAE,SAA8B,GACxC,OAAO,CAAC,MAAM,EAAE,CAAC,
|
1
|
+
{"version":3,"file":"priceUtils.d.ts","sourceRoot":"","sources":["../../src/utils/priceUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,IAAI,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAerE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,UAAU,WAAW;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,SAAS,EAAE,EAClB,SAAS,GAAE,SAA8B,GACxC,OAAO,CAAC,MAAM,EAAE,CAAC,CAkDnB;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,EAAE,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAkDtC;AAkBD,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,SAAS,EAAE,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CA+CtC;AAED,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,SAAS,EAAE,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAiBtC;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,EACnD,SAAS,GAAE,SAA8B,GACxC,MAAM,GAAG,SAAS,CAQpB"}
|
package/dist/utils/priceUtils.js
CHANGED
@@ -37,42 +37,42 @@ const jupiterUtils_1 = require("./jupiterUtils");
|
|
37
37
|
const generated_1 = require("../generated");
|
38
38
|
async function fetchTokenPrices(mints, priceType = generated_1.PriceType.Realtime) {
|
39
39
|
const currentTime = (0, generalUtils_1.currentUnixSeconds)();
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
}
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
(
|
56
|
-
]);
|
57
|
-
const prices = mints.map((mint) => {
|
58
|
-
const item = [...pythData, ...switchboardData, ...jupData].find((data) => data[0].equals(mint));
|
59
|
-
return item ? item[1] : { realtimePrice: 0 };
|
60
|
-
});
|
61
|
-
for (var i = 0; i < mints.length; i++) {
|
62
|
-
const realtimePrice = prices[i].realtimePrice;
|
63
|
-
constants_1.PRICES[mints[i].toString()] = {
|
40
|
+
const mintStrs = mints.map((x) => x.toString());
|
41
|
+
const cachedPrices = Object.fromEntries(Object.entries(constants_1.PRICES).filter(([mint, price]) => mintStrs.includes(mint) && currentTime - price.time <= 3));
|
42
|
+
const newMints = mintStrs
|
43
|
+
.filter((x) => !Object.keys(cachedPrices).includes(x))
|
44
|
+
.map((x) => new web3_js_1.PublicKey(x));
|
45
|
+
const pythMints = newMints.filter((x) => x.toString() in constants_1.PYTH_PRICE_FEED_IDS);
|
46
|
+
const switchboardMints = newMints.filter((x) => x.toString() in Object.keys(constants_1.SWITCHBOARD_PRICE_FEED_IDS));
|
47
|
+
const otherMints = newMints.filter((x) => !pythMints.includes(x) && !switchboardMints.includes(x));
|
48
|
+
const newPrices = Object.assign({}, ...(await Promise.all([
|
49
|
+
getPythPrices(pythMints),
|
50
|
+
getSwitchboardPrices(switchboardMints),
|
51
|
+
getJupTokenPrices(otherMints),
|
52
|
+
])));
|
53
|
+
for (const mint of newMints) {
|
54
|
+
const realtimePrice = newPrices[mint.toString()].realtimePrice;
|
55
|
+
constants_1.PRICES[mint.toString()] = {
|
64
56
|
realtimePrice,
|
65
|
-
emaPrice:
|
57
|
+
emaPrice: newPrices[mint.toString()].emaPrice ?? realtimePrice,
|
66
58
|
time: (0, generalUtils_1.currentUnixSeconds)(),
|
67
59
|
};
|
68
60
|
}
|
69
|
-
|
70
|
-
|
71
|
-
|
61
|
+
const prices = {
|
62
|
+
...constants_1.PRICES,
|
63
|
+
...newPrices,
|
64
|
+
};
|
65
|
+
return mints.map((x) => {
|
66
|
+
const priceResult = prices[x.toString()];
|
67
|
+
const realtimePrice = priceResult.realtimePrice;
|
68
|
+
return priceType === generated_1.PriceType.Ema
|
69
|
+
? (priceResult.emaPrice ?? realtimePrice)
|
70
|
+
: realtimePrice;
|
71
|
+
});
|
72
72
|
}
|
73
|
-
async function getPythPrices(mints
|
73
|
+
async function getPythPrices(mints) {
|
74
74
|
if (mints.length === 0) {
|
75
|
-
return
|
75
|
+
return {};
|
76
76
|
}
|
77
77
|
const priceFeedIds = mints.map((mint) => constants_1.PYTH_PRICE_FEED_IDS[mint.toString()]);
|
78
78
|
const getReq = async () => await fetch(`https://hermes.pyth.network/v2/updates/price/latest?${priceFeedIds.map((x) => `ids%5B%5D=${x}`).join("&")}`);
|
@@ -102,7 +102,7 @@ async function getPythPrices(mints, priceType) {
|
|
102
102
|
});
|
103
103
|
return prices;
|
104
104
|
}, 5, 200);
|
105
|
-
return prices;
|
105
|
+
return (0, generalUtils_1.createRecord)(mints.map((x) => x.toString()), prices);
|
106
106
|
}
|
107
107
|
function getSortedPriceData(prices, mints) {
|
108
108
|
const sortedPrices = {};
|
@@ -116,7 +116,7 @@ function getSortedPriceData(prices, mints) {
|
|
116
116
|
}
|
117
117
|
async function getSwitchboardPrices(mints) {
|
118
118
|
if (mints.length === 0) {
|
119
|
-
return
|
119
|
+
return {};
|
120
120
|
}
|
121
121
|
const { CrossbarClient } = SwbCommon;
|
122
122
|
const crossbar = CrossbarClient.default();
|
@@ -133,7 +133,7 @@ async function getSwitchboardPrices(mints) {
|
|
133
133
|
for (const item of resp) {
|
134
134
|
for (const [k, v] of Object.entries(constants_1.SWITCHBOARD_PRICE_FEED_IDS)) {
|
135
135
|
if (item.feedHash === v.feedHash) {
|
136
|
-
finalMap[k] = Number(item.results[0]);
|
136
|
+
finalMap[k] = { realtimePrice: Number(item.results[0]) };
|
137
137
|
}
|
138
138
|
}
|
139
139
|
}
|
@@ -144,25 +144,21 @@ async function getSwitchboardPrices(mints) {
|
|
144
144
|
(0, generalUtils_1.consoleLog)("Failed to fetch Switchboard prices after multiple retries");
|
145
145
|
}
|
146
146
|
const missingMints = mints.filter((x) => !prices[x.toString()]);
|
147
|
-
const jupPrices =
|
148
|
-
|
149
|
-
return acc;
|
150
|
-
}, {});
|
151
|
-
return Object.values(getSortedPriceData({ ...prices, ...jupPrices }, mints)).map((x) => {
|
152
|
-
return { realtimePrice: x };
|
153
|
-
});
|
147
|
+
const jupPrices = await getJupTokenPrices(missingMints.map((x) => new web3_js_1.PublicKey(x)));
|
148
|
+
return { ...prices, ...jupPrices };
|
154
149
|
}
|
155
150
|
async function getJupTokenPrices(mints) {
|
156
151
|
if (mints.length == 0) {
|
157
|
-
return
|
152
|
+
return {};
|
158
153
|
}
|
159
154
|
const data = getSortedPriceData(await (0, jupiterUtils_1.getJupPriceData)(mints), mints);
|
160
|
-
const prices = Object.
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
155
|
+
const prices = Object.fromEntries(Object.entries(data).map(([mint, x]) => [
|
156
|
+
mint,
|
157
|
+
x !== null && typeof x === "object" && "price" in x
|
158
|
+
? { realtimePrice: parseFloat(x.price) }
|
159
|
+
: { realtimePrice: 0 },
|
160
|
+
]));
|
161
|
+
return prices;
|
166
162
|
}
|
167
163
|
function safeGetPrice(mint, priceType = generated_1.PriceType.Realtime) {
|
168
164
|
if (mint && mint?.toString() in constants_1.PRICES) {
|
package/local/txSandbox.ts
CHANGED
@@ -1,16 +1,21 @@
|
|
1
1
|
import { Keypair, PublicKey } from "@solana/web3.js";
|
2
|
-
import { createSignerFromKeypair } from "@metaplex-foundation/umi";
|
2
|
+
import { createSignerFromKeypair, publicKey } from "@metaplex-foundation/umi";
|
3
3
|
import { fromWeb3JsKeypair } from "@metaplex-foundation/umi-web3js-adapters";
|
4
4
|
import {
|
5
|
+
bytesToI80F48,
|
5
6
|
ClientTransactionsManager,
|
6
7
|
consoleLog,
|
8
|
+
fetchBank,
|
9
|
+
fetchMarginfiAccount,
|
7
10
|
getBatches,
|
8
11
|
getClient,
|
12
|
+
getLiqUtilzationRateBps,
|
9
13
|
getPositionExBulk,
|
10
14
|
getSolanaRpcConnection,
|
11
15
|
getSolautoManagedPositions,
|
12
16
|
LendingPlatform,
|
13
17
|
LOCAL_IRONFORGE_API_URL,
|
18
|
+
marginfiAccountEmpty,
|
14
19
|
PriceType,
|
15
20
|
PriorityFeeSetting,
|
16
21
|
ProgramEnv,
|
@@ -23,7 +28,7 @@ import {
|
|
23
28
|
import { getSecretKey } from "./shared";
|
24
29
|
|
25
30
|
const payForTransaction = false;
|
26
|
-
const testProgram =
|
31
|
+
const testProgram = true;
|
27
32
|
const lpEnv: ProgramEnv = "Prod";
|
28
33
|
|
29
34
|
let [, umi] = getSolanaRpcConnection(
|
@@ -47,23 +52,72 @@ export async function main() {
|
|
47
52
|
});
|
48
53
|
|
49
54
|
await client.initializeExistingSolautoPosition({
|
50
|
-
positionId:
|
51
|
-
authority: new PublicKey("
|
55
|
+
positionId: 2,
|
56
|
+
authority: new PublicKey("rC5dMP5dmSsfQ66rynzfFzuc122Eex9h1RJHVDkeH6D"),
|
52
57
|
// lpUserAccount: new PublicKey(
|
53
58
|
// "GEokw9jqbh6d1xUNA3qaeYFFetbSR5Y1nt7C3chwwgSz"
|
54
59
|
// ),
|
55
60
|
});
|
56
61
|
|
57
|
-
const
|
62
|
+
const supplyBank = await fetchBank(
|
63
|
+
umi,
|
64
|
+
publicKey("6cgYhBFWCc5sNHxkvSRhd5H9AdAHR41zKwuF37HmLry5")
|
65
|
+
);
|
66
|
+
const debtBank = await fetchBank(
|
67
|
+
umi,
|
68
|
+
publicKey("3J5rKmCi7JXG6qmiobFJyAidVTnnNAMGj4jomfBxKGRM")
|
69
|
+
);
|
70
|
+
const supplyWeight = bytesToI80F48(supplyBank.config.assetWeightInit.value);
|
71
|
+
const debtWeight = bytesToI80F48(debtBank.config.liabilityWeightInit.value);
|
58
72
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
73
|
+
console.log(
|
74
|
+
getLiqUtilzationRateBps(
|
75
|
+
34.36833665228071,
|
76
|
+
23.61750715267401,
|
77
|
+
client.pos.state.liqThresholdBps
|
78
|
+
),
|
79
|
+
34.36833665228071 * supplyWeight,
|
80
|
+
23.61750715267401 * debtWeight
|
81
|
+
);
|
82
|
+
console.log(
|
83
|
+
getLiqUtilzationRateBps(
|
84
|
+
34.328721976,
|
85
|
+
23.575158311,
|
86
|
+
client.pos.state.liqThresholdBps
|
87
|
+
),
|
88
|
+
34.328721976 * supplyWeight,
|
89
|
+
23.575158311 * debtWeight
|
90
|
+
);
|
91
|
+
console.log(client.pos.maxBoostToBps);
|
92
|
+
|
93
|
+
// const debtBank = await fetchBank(
|
94
|
+
// umi,
|
95
|
+
// publicKey("3J5rKmCi7JXG6qmiobFJyAidVTnnNAMGj4jomfBxKGRM")
|
96
|
+
// );
|
97
|
+
// const supplyBank = await fetchBank(
|
98
|
+
// umi,
|
99
|
+
// publicKey("6cgYhBFWCc5sNHxkvSRhd5H9AdAHR41zKwuF37HmLry5")
|
100
|
+
// );
|
101
|
+
|
102
|
+
// console.log(
|
103
|
+
// bytesToI80F48(supplyBank.config.assetWeightInit.value),
|
104
|
+
// bytesToI80F48(debtBank.config.liabilityWeightInit.value)
|
105
|
+
// );
|
106
|
+
|
107
|
+
// await client.pos.refreshPositionState();
|
108
|
+
|
109
|
+
// console.log(await client.pos.utilizationRateBpsDrift());
|
110
|
+
|
111
|
+
// const transactionItems = [rebalance(client)];
|
112
|
+
|
113
|
+
// const txManager = new ClientTransactionsManager({
|
114
|
+
// txHandler: client,
|
115
|
+
// txRunType: payForTransaction ? "normal" : "only-simulate",
|
116
|
+
// priorityFeeSetting: PriorityFeeSetting.Default,
|
117
|
+
// retryConfig: { totalRetries: 5 },
|
118
|
+
// });
|
119
|
+
// const statuses = await txManager.send(transactionItems);
|
120
|
+
// consoleLog(statuses);
|
67
121
|
}
|
68
122
|
|
69
123
|
async function refreshAll() {
|
package/package.json
CHANGED
@@ -4,7 +4,7 @@ import { FlashLoanRequirements } from "../../types";
|
|
4
4
|
import { SolautoClient } from "../solauto";
|
5
5
|
import { JupSwapManager, SwapParams, SwapInput } from "../swap";
|
6
6
|
import { RebalanceValues } from "./rebalanceValues";
|
7
|
-
import { RebalanceDirection, TokenType } from "../../generated";
|
7
|
+
import { PriceType, RebalanceDirection, TokenType } from "../../generated";
|
8
8
|
import {
|
9
9
|
consoleLog,
|
10
10
|
fromBaseUnit,
|
@@ -25,7 +25,8 @@ export class RebalanceSwapManager {
|
|
25
25
|
private client: SolautoClient,
|
26
26
|
private values: RebalanceValues,
|
27
27
|
private flRequirements?: FlashLoanRequirements,
|
28
|
-
private targetLiqUtilizationRateBps?: number
|
28
|
+
private targetLiqUtilizationRateBps?: number,
|
29
|
+
private priceType?: PriceType
|
29
30
|
) {
|
30
31
|
this.jupSwapManager = new JupSwapManager(client.signer);
|
31
32
|
}
|
@@ -39,16 +40,16 @@ export class RebalanceSwapManager {
|
|
39
40
|
}
|
40
41
|
|
41
42
|
private postRebalanceLiqUtilizationRateBps(swapOutputAmount?: bigint) {
|
42
|
-
let supplyUsd = this.client.pos.supplyUsd();
|
43
|
+
let supplyUsd = this.client.pos.supplyUsd(this.priceType);
|
43
44
|
// TODO: add token balance change
|
44
|
-
let debtUsd = this.client.pos.debtUsd();
|
45
|
+
let debtUsd = this.client.pos.debtUsd(this.priceType);
|
45
46
|
|
46
47
|
const outputToken = this.isBoost()
|
47
48
|
? this.client.pos.supplyMint
|
48
49
|
: this.client.pos.debtMint;
|
49
50
|
const swapOutputUsd = swapOutputAmount
|
50
51
|
? fromBaseUnit(swapOutputAmount, tokenInfo(outputToken).decimals) *
|
51
|
-
(safeGetPrice(outputToken) ?? 0)
|
52
|
+
(safeGetPrice(outputToken, this.priceType) ?? 0)
|
52
53
|
: this.usdToSwap();
|
53
54
|
|
54
55
|
supplyUsd = this.isBoost()
|
@@ -110,7 +111,7 @@ export class RebalanceSwapManager {
|
|
110
111
|
: this.client.pos.state.debt;
|
111
112
|
|
112
113
|
let inputAmount = toBaseUnit(
|
113
|
-
this.usdToSwap() / safeGetPrice(input.mint)!,
|
114
|
+
this.usdToSwap() / safeGetPrice(input.mint, this.priceType)!,
|
114
115
|
input.decimals
|
115
116
|
);
|
116
117
|
|
@@ -139,7 +140,7 @@ export class RebalanceSwapManager {
|
|
139
140
|
)
|
140
141
|
)
|
141
142
|
: toBaseUnit(
|
142
|
-
this.usdToSwap() / safeGetPrice(output.mint)!,
|
143
|
+
this.usdToSwap() / safeGetPrice(output.mint, this.priceType)!,
|
143
144
|
output.decimals
|
144
145
|
);
|
145
146
|
|
@@ -287,7 +287,8 @@ export class RebalanceTxBuilder {
|
|
287
287
|
this.client,
|
288
288
|
this.values,
|
289
289
|
this.flRequirements,
|
290
|
-
this.targetLiqUtilizationRateBps
|
290
|
+
this.targetLiqUtilizationRateBps,
|
291
|
+
this.priceType
|
291
292
|
);
|
292
293
|
await this.swapManager.setSwapParams(attemptNum);
|
293
294
|
|
@@ -179,6 +179,7 @@ export abstract class SolautoClient extends ReferralStateManager {
|
|
179
179
|
|
180
180
|
this.log("Position state: ", this.pos.state);
|
181
181
|
this.log("Position settings: ", this.pos.settings);
|
182
|
+
this.log("Public key:", this.pos.publicKey.toString());
|
182
183
|
this.log("Supply mint:", this.pos.supplyMint.toString());
|
183
184
|
this.log("Debt mint:", this.pos.debtMint.toString());
|
184
185
|
this.log("LP pool:", this.pos.lpPoolAccount.toString());
|
@@ -45,11 +45,11 @@ export class ClientTransactionsManager extends TransactionsManager<SolautoClient
|
|
45
45
|
|
46
46
|
if (txs.find((x) => x.oracleInteractor) && switchboardMints.length) {
|
47
47
|
this.txHandler.log("Requires oracle update(s)...");
|
48
|
-
const
|
48
|
+
const oracleTxs = switchboardMints.map(
|
49
49
|
(x) =>
|
50
50
|
new TransactionItem(
|
51
51
|
async () =>
|
52
|
-
buildSwbSubmitResponseTx(
|
52
|
+
await buildSwbSubmitResponseTx(
|
53
53
|
this.txHandler.connection,
|
54
54
|
this.txHandler.signer,
|
55
55
|
x
|
@@ -57,7 +57,7 @@ export class ClientTransactionsManager extends TransactionsManager<SolautoClient
|
|
57
57
|
this.updateOracleTxName
|
58
58
|
)
|
59
59
|
);
|
60
|
-
txs.unshift(...
|
60
|
+
txs.unshift(...oracleTxs);
|
61
61
|
}
|
62
62
|
}
|
63
63
|
|
@@ -48,7 +48,7 @@ interface RetryConfig {
|
|
48
48
|
retryDelay?: number;
|
49
49
|
}
|
50
50
|
|
51
|
-
export interface
|
51
|
+
export interface TransactionsManagerArgs<T extends TxHandler> {
|
52
52
|
txHandler: T;
|
53
53
|
statusCallback?: (statuses: TransactionManagerStatuses) => void;
|
54
54
|
txRunType?: TransactionRunType;
|
@@ -73,7 +73,7 @@ export class TransactionsManager<T extends TxHandler> {
|
|
73
73
|
|
74
74
|
updateOracleTxName = "update oracle";
|
75
75
|
|
76
|
-
constructor(args:
|
76
|
+
constructor(args: TransactionsManagerArgs<T>) {
|
77
77
|
this.txHandler = args.txHandler;
|
78
78
|
this.statusCallback = args.statusCallback;
|
79
79
|
this.txRunType = args.txRunType;
|
@@ -470,9 +470,9 @@ export class TransactionsManager<T extends TxHandler> {
|
|
470
470
|
await itemSet.refetchAll(attemptNum, prevError);
|
471
471
|
} else {
|
472
472
|
await Promise.all(itemSets.map((itemSet) => itemSet.reset()));
|
473
|
-
|
474
|
-
|
475
|
-
|
473
|
+
for (const itemSet of itemSets) {
|
474
|
+
await itemSet.refetchAll(attemptNum, prevError);
|
475
|
+
}
|
476
476
|
}
|
477
477
|
|
478
478
|
const newItemSets = await this.assembleTransactionSets(
|
@@ -1,6 +1,11 @@
|
|
1
1
|
import axios from "axios";
|
2
2
|
import { PublicKey } from "@solana/web3.js";
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
MaybeRpcAccount,
|
5
|
+
publicKey,
|
6
|
+
Umi,
|
7
|
+
PublicKey as UmiPublicKey,
|
8
|
+
} from "@metaplex-foundation/umi";
|
4
9
|
import { TOKEN_INFO, TokenInfo } from "../constants";
|
5
10
|
|
6
11
|
export function buildHeliusApiUrl(heliusApiKey: string) {
|
@@ -199,4 +204,10 @@ export function u16ToArrayBufferLE(value: number): Uint8Array {
|
|
199
204
|
|
200
205
|
export function validPubkey(pubkey?: PublicKey | UmiPublicKey | string) {
|
201
206
|
return Boolean(pubkey) && pubkey!.toString() !== PublicKey.default.toString();
|
202
|
-
}
|
207
|
+
}
|
208
|
+
|
209
|
+
export function createRecord<T>(keys: string[], values: T[]): Record<string, T> {
|
210
|
+
return Object.fromEntries(
|
211
|
+
zip(keys, values).map(([k, v]) => [k.toString(), v])
|
212
|
+
);
|
213
|
+
}
|
package/src/utils/priceUtils.ts
CHANGED
@@ -9,9 +9,9 @@ import {
|
|
9
9
|
import { fromBaseUnit, toBaseUnit } from "./numberUtils";
|
10
10
|
import {
|
11
11
|
consoleLog,
|
12
|
+
createRecord,
|
12
13
|
currentUnixSeconds,
|
13
14
|
retryWithExponentialBackoff,
|
14
|
-
zip,
|
15
15
|
} from "./generalUtils";
|
16
16
|
import { getJupPriceData } from "./jupiterUtils";
|
17
17
|
import { PriceType } from "../generated";
|
@@ -26,64 +26,61 @@ export async function fetchTokenPrices(
|
|
26
26
|
priceType: PriceType = PriceType.Realtime
|
27
27
|
): Promise<number[]> {
|
28
28
|
const currentTime = currentUnixSeconds();
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
currentTime -
|
29
|
+
const mintStrs = mints.map((x) => x.toString());
|
30
|
+
const cachedPrices: Record<string, PriceResult> = Object.fromEntries(
|
31
|
+
Object.entries(PRICES).filter(
|
32
|
+
([mint, price]) =>
|
33
|
+
mintStrs.includes(mint) && currentTime - price.time <= 3
|
34
34
|
)
|
35
|
-
)
|
36
|
-
return mints.map((mint) => {
|
37
|
-
const priceData = PRICES[mint.toString()];
|
38
|
-
return priceType === PriceType.Ema
|
39
|
-
? priceData.emaPrice
|
40
|
-
: priceData.realtimePrice;
|
41
|
-
});
|
42
|
-
}
|
35
|
+
);
|
43
36
|
|
44
|
-
const
|
45
|
-
|
37
|
+
const newMints = mintStrs
|
38
|
+
.filter((x) => !Object.keys(cachedPrices).includes(x))
|
39
|
+
.map((x) => new PublicKey(x));
|
40
|
+
const pythMints = newMints.filter((x) => x.toString() in PYTH_PRICE_FEED_IDS);
|
41
|
+
const switchboardMints = newMints.filter(
|
46
42
|
(x) => x.toString() in Object.keys(SWITCHBOARD_PRICE_FEED_IDS)
|
47
43
|
);
|
48
|
-
const otherMints =
|
44
|
+
const otherMints = newMints.filter(
|
49
45
|
(x) => !pythMints.includes(x) && !switchboardMints.includes(x)
|
50
46
|
);
|
47
|
+
const newPrices: Record<string, PriceResult> = Object.assign(
|
48
|
+
{},
|
49
|
+
...(await Promise.all([
|
50
|
+
getPythPrices(pythMints),
|
51
|
+
getSwitchboardPrices(switchboardMints),
|
52
|
+
getJupTokenPrices(otherMints),
|
53
|
+
]))
|
54
|
+
);
|
51
55
|
|
52
|
-
const
|
53
|
-
|
54
|
-
|
55
|
-
zip(otherMints, await getJupTokenPrices(otherMints)),
|
56
|
-
]);
|
57
|
-
|
58
|
-
const prices = mints.map((mint) => {
|
59
|
-
const item = [...pythData, ...switchboardData, ...jupData].find((data) =>
|
60
|
-
data[0].equals(mint)
|
61
|
-
);
|
62
|
-
return item ? item[1] : { realtimePrice: 0 };
|
63
|
-
});
|
64
|
-
|
65
|
-
for (var i = 0; i < mints.length; i++) {
|
66
|
-
const realtimePrice = prices[i].realtimePrice;
|
67
|
-
PRICES[mints[i].toString()] = {
|
56
|
+
for (const mint of newMints) {
|
57
|
+
const realtimePrice = newPrices[mint.toString()].realtimePrice;
|
58
|
+
PRICES[mint.toString()] = {
|
68
59
|
realtimePrice,
|
69
|
-
emaPrice:
|
60
|
+
emaPrice: newPrices[mint.toString()].emaPrice ?? realtimePrice,
|
70
61
|
time: currentUnixSeconds(),
|
71
62
|
};
|
72
63
|
}
|
73
64
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
65
|
+
const prices: Record<string, PriceResult> = {
|
66
|
+
...PRICES,
|
67
|
+
...newPrices,
|
68
|
+
};
|
69
|
+
|
70
|
+
return mints.map((x) => {
|
71
|
+
const priceResult = prices[x.toString()];
|
72
|
+
const realtimePrice = priceResult.realtimePrice;
|
73
|
+
return priceType === PriceType.Ema
|
74
|
+
? (priceResult.emaPrice ?? realtimePrice)
|
75
|
+
: realtimePrice;
|
76
|
+
});
|
79
77
|
}
|
80
78
|
|
81
79
|
export async function getPythPrices(
|
82
|
-
mints: PublicKey[]
|
83
|
-
|
84
|
-
): Promise<PriceResult[]> {
|
80
|
+
mints: PublicKey[]
|
81
|
+
): Promise<Record<string, PriceResult>> {
|
85
82
|
if (mints.length === 0) {
|
86
|
-
return
|
83
|
+
return {};
|
87
84
|
}
|
88
85
|
|
89
86
|
const priceFeedIds = mints.map(
|
@@ -127,7 +124,10 @@ export async function getPythPrices(
|
|
127
124
|
200
|
128
125
|
);
|
129
126
|
|
130
|
-
return
|
127
|
+
return createRecord(
|
128
|
+
mints.map((x) => x.toString()),
|
129
|
+
prices
|
130
|
+
);
|
131
131
|
}
|
132
132
|
|
133
133
|
function getSortedPriceData(
|
@@ -148,15 +148,15 @@ function getSortedPriceData(
|
|
148
148
|
|
149
149
|
export async function getSwitchboardPrices(
|
150
150
|
mints: PublicKey[]
|
151
|
-
): Promise<PriceResult
|
151
|
+
): Promise<Record<string, PriceResult>> {
|
152
152
|
if (mints.length === 0) {
|
153
|
-
return
|
153
|
+
return {};
|
154
154
|
}
|
155
155
|
|
156
156
|
const { CrossbarClient } = SwbCommon;
|
157
157
|
const crossbar = CrossbarClient.default();
|
158
158
|
|
159
|
-
let prices: Record<string,
|
159
|
+
let prices: Record<string, PriceResult> = {};
|
160
160
|
try {
|
161
161
|
prices = await retryWithExponentialBackoff(
|
162
162
|
async () => {
|
@@ -172,11 +172,11 @@ export async function getSwitchboardPrices(
|
|
172
172
|
throw new Error("Unable to fetch Switchboard prices");
|
173
173
|
}
|
174
174
|
|
175
|
-
const finalMap: Record<string,
|
175
|
+
const finalMap: Record<string, PriceResult> = {};
|
176
176
|
for (const item of resp) {
|
177
177
|
for (const [k, v] of Object.entries(SWITCHBOARD_PRICE_FEED_IDS)) {
|
178
178
|
if (item.feedHash === v.feedHash) {
|
179
|
-
finalMap[k] = Number(item.results[0]);
|
179
|
+
finalMap[k] = { realtimePrice: Number(item.results[0]) };
|
180
180
|
}
|
181
181
|
}
|
182
182
|
}
|
@@ -190,42 +190,32 @@ export async function getSwitchboardPrices(
|
|
190
190
|
}
|
191
191
|
|
192
192
|
const missingMints = mints.filter((x) => !prices[x.toString()]);
|
193
|
-
const jupPrices =
|
194
|
-
missingMints
|
195
|
-
await getJupTokenPrices(missingMints.map((x) => new PublicKey(x)))
|
196
|
-
).reduce(
|
197
|
-
(acc, [key, value]) => {
|
198
|
-
acc[key.toString()] = value.realtimePrice;
|
199
|
-
return acc;
|
200
|
-
},
|
201
|
-
{} as Record<string, number>
|
193
|
+
const jupPrices = await getJupTokenPrices(
|
194
|
+
missingMints.map((x) => new PublicKey(x))
|
202
195
|
);
|
203
196
|
|
204
|
-
return
|
205
|
-
getSortedPriceData({ ...prices, ...jupPrices }, mints)
|
206
|
-
).map((x) => {
|
207
|
-
return { realtimePrice: x };
|
208
|
-
});
|
197
|
+
return { ...prices, ...jupPrices };
|
209
198
|
}
|
210
199
|
|
211
200
|
export async function getJupTokenPrices(
|
212
201
|
mints: PublicKey[]
|
213
|
-
): Promise<PriceResult
|
202
|
+
): Promise<Record<string, PriceResult>> {
|
214
203
|
if (mints.length == 0) {
|
215
|
-
return
|
204
|
+
return {};
|
216
205
|
}
|
217
206
|
|
218
207
|
const data = getSortedPriceData(await getJupPriceData(mints), mints);
|
219
208
|
|
220
|
-
const prices = Object.
|
221
|
-
|
222
|
-
|
223
|
-
|
209
|
+
const prices: Record<string, PriceResult> = Object.fromEntries(
|
210
|
+
Object.entries(data).map(([mint, x]) => [
|
211
|
+
mint,
|
212
|
+
x !== null && typeof x === "object" && "price" in x
|
213
|
+
? { realtimePrice: parseFloat(x.price as string) }
|
214
|
+
: { realtimePrice: 0 },
|
215
|
+
])
|
224
216
|
);
|
225
217
|
|
226
|
-
return prices
|
227
|
-
return { realtimePrice: x };
|
228
|
-
});
|
218
|
+
return prices;
|
229
219
|
}
|
230
220
|
|
231
221
|
export function safeGetPrice(
|
@@ -53,12 +53,17 @@ export async function buildSwbSubmitResponseTx(
|
|
53
53
|
): Promise<TransactionItemInputs | undefined> {
|
54
54
|
const feed = getPullFeed(conn, mint, toWeb3JsPublicKey(signer.publicKey));
|
55
55
|
const [pullIx, responses] = await retryWithExponentialBackoff(
|
56
|
-
async () =>
|
57
|
-
await feed.fetchUpdateIx({
|
56
|
+
async () => {
|
57
|
+
const res = await feed.fetchUpdateIx({
|
58
58
|
chain: "solana",
|
59
59
|
network: "mainnet-beta",
|
60
|
-
})
|
61
|
-
|
60
|
+
});
|
61
|
+
if (!res[1] || !res[1][0].value) {
|
62
|
+
throw new Error("Unable to fetch Switchboard pull IX");
|
63
|
+
}
|
64
|
+
return res;
|
65
|
+
},
|
66
|
+
3,
|
62
67
|
200
|
63
68
|
);
|
64
69
|
|
@@ -111,4 +116,4 @@ export async function getSwitchboardFeedData(
|
|
111
116
|
|
112
117
|
export function isSwitchboardMint(mint: PublicKey | string) {
|
113
118
|
return Object.keys(SWITCHBOARD_PRICE_FEED_IDS).includes(mint.toString());
|
114
|
-
}
|
119
|
+
}
|