mev-inspect
Version:
A JS port of 'mev-inspect-py' optimised for ease of use.
124 lines • 4.61 kB
JavaScript
import { equalWithTolerance } from '../utils.js';
const MAX_TOKEN_AMOUNT_PERCENT_DIFFERENCE = 0.00001;
function getArbitrages(swaps) {
const arbitrages = [];
const groupedSwaps = groupSwapsByTransaction(swaps);
for (const hash in groupedSwaps) {
const swaps = groupedSwaps[hash];
const transactionArbitrages = getTransactionArbitrages(swaps);
for (const arbitrage of transactionArbitrages) {
arbitrages.push(arbitrage);
}
}
return arbitrages;
}
function getTransactionArbitrages(swaps) {
const arbitrages = [];
const startEndList = getStartEndSwaps(swaps);
const usedSwaps = [];
for (const startEnds of startEndList) {
const [start, ends] = startEnds;
if (usedSwaps.includes(start)) {
continue;
}
const unusedEnds = ends.filter((end) => !usedSwaps.includes(end));
const route = getShortestRoute(start, unusedEnds, swaps);
if (route.length > 0) {
const sender = route[0].transaction.from;
const beneficiary = route[0].from;
const profitAmount = route[route.length - 1].amountOut - route[0].amountIn;
const profitAsset = route[0].assetIn;
const arbitrage = {
swaps: route,
profit: {
amount: profitAmount,
asset: profitAsset,
},
arbitrager: {
sender,
beneficiary,
},
};
arbitrages.push(arbitrage);
for (const swap of route) {
usedSwaps.push(swap);
}
}
}
return arbitrages;
}
function groupSwapsByTransaction(swaps) {
return swaps.reduce((result, swap) => {
const hash = swap.transaction.hash;
if (!result[hash])
result[hash] = [];
result[hash].push(swap);
return result;
}, {});
}
function getShortestRoute(startSwap, endSwaps, allSwaps, maxRouteLength) {
if (endSwaps.length === 0) {
return [];
}
if (maxRouteLength && maxRouteLength < 2) {
return [];
}
for (const endSwap of endSwaps) {
if (swapOutsMatchSwapIns(startSwap, endSwap)) {
return [startSwap, endSwap];
}
}
if (maxRouteLength && maxRouteLength === 2) {
return [];
}
const otherSwaps = allSwaps.filter((swap) => startSwap !== swap && !endSwaps.includes(swap));
if (otherSwaps.length === 0) {
return [];
}
let shortestRemainingRoute = [];
let maxRemainingRouteLength = maxRouteLength ? maxRouteLength - 1 : undefined;
for (const nextSwap of otherSwaps) {
if (swapOutsMatchSwapIns(startSwap, nextSwap)) {
const shortestFromNext = getShortestRoute(nextSwap, endSwaps, otherSwaps, maxRemainingRouteLength);
if (shortestFromNext.length > 0 &&
(shortestRemainingRoute.length === 0 ||
shortestFromNext.length < shortestRemainingRoute.length)) {
shortestRemainingRoute = shortestFromNext;
maxRemainingRouteLength = shortestFromNext.length - 1;
}
}
}
return shortestRemainingRoute.length === 0
? []
: [startSwap, ...shortestRemainingRoute];
}
function getStartEndSwaps(swaps) {
const pools = swaps.map((swap) => swap.event.address);
const startEnds = [];
for (const index in swaps) {
const potentialStartSwap = swaps[index];
const endsForStart = [];
const remainingSwaps = swaps.filter((swap) => swap !== potentialStartSwap);
for (const potentialEndSwap of remainingSwaps) {
if (potentialStartSwap.assetIn.address ===
potentialEndSwap.assetOut.address &&
potentialStartSwap.from === potentialEndSwap.to &&
!pools.includes(potentialStartSwap.from)) {
endsForStart.push(potentialEndSwap);
}
}
if (endsForStart.length > 0) {
startEnds.push([potentialStartSwap, endsForStart]);
}
}
return startEnds;
}
function swapOutsMatchSwapIns(swapOut, swapIn) {
return (swapOut.assetOut.address === swapIn.assetIn.address &&
(swapOut.contract.address == swapIn.from ||
swapOut.to == swapIn.contract.address ||
swapOut.to == swapIn.from) &&
equalWithTolerance(swapOut.amountOut, swapIn.amountIn, MAX_TOKEN_AMOUNT_PERCENT_DIFFERENCE));
}
export { getArbitrages };
//# sourceMappingURL=arbitrage.js.map