Skip to content

Instantly share code, notes, and snippets.

@rubpy
Created August 20, 2024 15:06
Show Gist options
  • Save rubpy/1db8114ce7d31110dcbf00d3b879e1ac to your computer and use it in GitHub Desktop.
Save rubpy/1db8114ce7d31110dcbf00d3b879e1ac to your computer and use it in GitHub Desktop.
// --- `@solana/web3.js` v1 ---
import web3 from '@solana/web3.js';
(async (rpcUrl: string) => {
const rpc = new web3.Connection(rpcUrl);
const subject = new web3.PublicKey('j1oAbxxiDUWvoHxEDhWE7THLjEkDQW2cSHYn2vttxTF');
const signatures = await rpc.getSignaturesForAddress(subject, {
limit: 1000,
}, 'finalized');
const mevProbability = estimateMevProbability(signatures);
console.log(
`Estimated MEV probability for address "${subject}":\n`,
mevProbability,
);
//
// --- OUTPUT (as of: Aug 20 2024 15:04:22 UTC) ---
//
// Estimated MEV probability for address "j1oAbxxiDUWvoHxEDhWE7THLjEkDQW2cSHYn2vttxTF":
// 0.9999685386458833
//
//
})(process.env.SOL_RPC_URL || 'https://mainnet.helius-rpc.com/?api-key=00000000-0000-0000-0000-000000000000');
// -----------------------------------------------------------------------------
/*
// --- `@solana/web3.js` v2: ---
import { createSolanaRpc, address } from '@solana/web3.js';
(async (rpcUrl: string) => {
const rpc = createSolanaRpc(rpcUrl);
const subject = address('j1oAbxxiDUWvoHxEDhWE7THLjEkDQW2cSHYn2vttxTF');
const signatures = await rpc.getSignaturesForAddress(subject, {
commitment: 'finalized',
limit: 1000,
}).send();
const mevProbability = estimateMevProbability(signatures);
console.log(
`Estimated MEV probability for address "${subject}":\n`,
mevProbability,
);
})(process.env.SOL_RPC_URL || 'https://mainnet.helius-rpc.com/?api-key=00000000-0000-0000-0000-000000000000');
*/
// -----------------------------------------------------------------------------
type GetSignaturesForAddressTransaction =
| { blockTime?: number | null; slot: number }
| { blockTime?: number | null; slot: bigint };
// NOTE: signatures must be sorted chronologically (in either ascending or
// descending order, based on `blockTime`/`slot`).
function estimateMevProbability<T extends GetSignaturesForAddressTransaction>(
signatures: readonly T[],
useBlockTime = false,
): number {
const dists: number[] = [];
let last = 0;
for (const entry of signatures) {
const n = Number(useBlockTime ? entry.blockTime : entry.slot);
if (isNaN(n) || n <= 0) continue;
if (last !== 0) {
dists.push(Math.abs(last - n));
}
last = n;
}
return _noiseToSignal(dists, useBlockTime ? 157.0 : 829.0, 2.0);
}
// -----------------------------------------------------------------------------
function _noiseToSignal(xs: number[], targetRatio: number, stdCutoff = 2.0): number {
if (xs.length <= 1 || targetRatio <= 0.0 || stdCutoff <= 0.0) return NaN;
let mean = _mean(xs);
let std = _std(xs, _variance(xs, mean));
const cutoff = stdCutoff * std;
xs = xs.filter(x => Math.abs(x - mean) < cutoff);
mean = _mean(xs);
std = _std(xs, _variance(xs, mean));
const coeff = (mean * std) / xs.length;
return 1 - Math.tanh(coeff / targetRatio);
}
function _sum(xs: number[], f?: (x: number) => number): number {
if (xs.length <= 0) return 0.0;
let sum = 0.0, comp = 0.0, t = 0.0, y = 0.0;
for (const x of xs) {
y = (f ? f(x) : x) - comp;
t = sum + y;
comp = (t - sum) - y;
sum = t;
}
return sum;
}
function _mean(xs: number[]): number {
return xs.length > 0 ? _sum(xs) / xs.length : 0.0;
}
function _variance(xs: number[], mean: number = NaN): number {
if (xs.length <= 1) return 0.0;
mean = isNaN(mean) ? _mean(xs) : mean;
return _sum(xs, x => { x -= mean; return x * x }) / (xs.length - 1);
}
function _std(xs: number[], variance: number = NaN): number {
if (xs.length <= 1) return 0.0;
variance = isNaN(variance) ? _variance(xs, NaN) : variance;
return Math.sqrt(variance);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment