use live data to estimate cu

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
microwavedcola1 2024-07-30 20:45:51 +02:00
parent ff0dfb9a2f
commit 28ace32c17
3 changed files with 123 additions and 17 deletions

View File

@ -0,0 +1,90 @@
import WebSocket from 'ws';
export function manageFeeWebSocket(
wsUrl: string,
rollingWindowSize: number = 120,
onMeanCalculated: (mean: number | null) => void,
): () => void {
let ws: WebSocket | null = null;
const recentValues: number[] = [];
function calculateRollingMean(values: number[]): number | null {
if (values.length === 0) return null;
const sum = values.reduce((acc, val) => acc + val, 0);
return Math.floor(sum / values.length);
}
function connectWebSocket(): void {
try {
ws = new WebSocket(wsUrl);
ws.addEventListener('open', () => {
try {
// console.log('Fee WebSocket opened');
const message = JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'blockPrioritizationFeesSubscribe',
interval: 30,
});
ws?.send(message);
} catch (error) {
console.error('Error in open event:', error);
onMeanCalculated(null);
}
});
ws.addEventListener('close', () => {
// console.log('Fee WebSocket closed');
ws = null;
// Attempt to reconnect after a delay
setTimeout(connectWebSocket, 5000);
});
ws.addEventListener('error', (error) => {
console.log('Fee WebSocket error:', error);
onMeanCalculated(-1);
ws?.close();
});
ws.addEventListener('message', (event: MessageEvent): void => {
try {
const parsedData = JSON.parse(event.data as string);
const value = parsedData?.params?.result?.value.by_tx[15];
if (value !== undefined && typeof value === 'number') {
recentValues.push(value);
if (recentValues.length > rollingWindowSize) {
recentValues.shift();
}
const rollingMean = calculateRollingMean(recentValues);
onMeanCalculated(rollingMean);
}
} catch (error) {
console.error('Error processing message:', error);
onMeanCalculated(-1);
}
});
} catch (error) {
console.error('Error in connectWebSocket:', error);
onMeanCalculated(-1);
// Attempt to reconnect after a delay
setTimeout(connectWebSocket, 5000);
}
}
// Start the WebSocket connection
connectWebSocket();
// Return a function to close the WebSocket
return () => {
if (ws) {
try {
ws.close();
} catch (error) {
console.error('Error closing WebSocket:', error);
}
}
};
}

View File

@ -27,24 +27,35 @@ import { MangoClient } from '../src/client';
import { MANGO_V4_ID, MANGO_V4_MAIN_GROUP } from '../src/constants';
import { ZERO_I80F48 } from '../src/numbers/I80F48';
import { createComputeBudgetIx } from '../src/utils/rpc';
import { manageFeeWebSocket } from './manageFeeWs';
const CLUSTER: Cluster =
(process.env.CLUSTER_OVERRIDE as Cluster) || 'mainnet-beta';
const CLUSTER_URL =
process.env.CLUSTER_URL_OVERRIDE || process.env.MB_CLUSTER_URL;
const CLUSTER_URL_2 =
process.env.CLUSTER_URL_OVERRIDE || process.env.MB_CLUSTER_URL_2;
const LITE_RPC_URL = process.env.MB_CLUSTER_URL;
const CLUSTER_URL_2 = process.env.MB_CLUSTER_URL_2;
const LITE_RPC_URL = process.env.LITE_RPC_URL;
const USER_KEYPAIR =
process.env.USER_KEYPAIR_OVERRIDE || process.env.MB_PAYER_KEYPAIR;
const GROUP = process.env.GROUP_OVERRIDE || MANGO_V4_MAIN_GROUP.toBase58();
const SLEEP_MS = Number(process.env.SLEEP_MS) || 50_000; //
const COMPUTE_UNIT_PRICE = Number(process.env.COMPUTE_UNIT_PRICE) || 150_000; //
const SLEEP_MS = Number(process.env.SLEEP_MS) || 50_000;
console.log(`Starting with ${SLEEP_MS}`);
console.log(`${CLUSTER_URL}`);
console.log(
`Starting with sleep ${SLEEP_MS}ms, cluster ${CLUSTER_URL}, cluster2 ${CLUSTER_URL_2}, liteRpcUrl ${LITE_RPC_URL}`,
);
// TODO use mangolana to send txs
let lamportsPerCu: number | null = null;
try {
const wsUrl = new URL(
process.env.LITE_RPC_URL!.replace('https', 'wss'),
).toString();
manageFeeWebSocket(wsUrl, 10, (mean) => {
lamportsPerCu = mean;
});
} catch (error) {
console.error('Error in main execution:', error);
}
interface OracleInterface {
oracle: {
@ -137,20 +148,25 @@ interface OracleInterface {
const ixsChunks = chunk(shuffle(pullIxs), 2, false);
try {
// use dont await, fire and forget
// dont await, fire and forget
sendSignAndConfirmTransactions({
connection,
wallet: new Wallet(user),
backupConnections: [
new Connection(LITE_RPC_URL!, 'recent'),
new Connection(CLUSTER_URL_2!, 'recent'),
...(CLUSTER_URL_2
? [new Connection(LITE_RPC_URL!, 'recent')]
: []),
...(CLUSTER_URL_2
? [new Connection(CLUSTER_URL_2!, 'recent')]
: []),
],
transactionInstructions: ixsChunks.map((txChunk) => ({
instructionsSet: [
{
signers: [],
transactionInstruction:
createComputeBudgetIx(COMPUTE_UNIT_PRICE),
transactionInstruction: createComputeBudgetIx(
Math.max(lamportsPerCu ?? 150_000, 150_000),
),
},
...txChunk.map((tx) => ({
signers: [],
@ -168,14 +184,14 @@ interface OracleInterface {
callbacks: {
afterEveryTxSend: function (data) {
console.log(
` - https://solscan.io/tx/${data['txid']}, in ${(Date.now() - start) / 1000}s`,
` - https://solscan.io/tx/${data['txid']}, in ${(Date.now() - start) / 1000}s, lamportsPerCu ${lamportsPerCu}`,
);
},
},
});
} catch (error) {
console.log(
`Error in sending tx, ${JSON.stringify(error.message)}, https://solscan.io/tx/${error['txid']}, in ${(Date.now() - start) / 1000}s`,
`Error in sending tx, ${JSON.stringify(error.message)}, https://solscan.io/tx/${error['txid']}, in ${(Date.now() - start) / 1000}s, lamportsPerCu ${lamportsPerCu}`,
);
}

View File

@ -226,7 +226,7 @@ async function updateTokenParams(): Promise<void> {
);
if (maybeSbOracle.length > 0) {
console.log(` - ${bank.name} ${maybeSbOracle[0][0]}`);
builder.fallbackOracle(new PublicKey(maybeSbOracle[0][1]));
builder.fallbackOracle(PublicKey.default);
change = true;
} else {
return;
@ -572,7 +572,7 @@ async function updateTokenParams(): Promise<void> {
tokenOwnerRecord,
PROPOSAL_TITLE
? PROPOSAL_TITLE
: 'Set sb on demand oracles as fallback oracles in mango-v4',
: 'Reset sb on demand oracles as fallback oracles in mango-v4',
PROPOSAL_LINK ?? '',
Object.values(proposals).length,
instructions,