|
// ==UserScript== |
|
// @name Uniswap liquidity Tick evaluator |
|
// @description Evaluates the value of a tick |
|
// @author Qwerty <qwerty@qwerty.xyz> |
|
// @namespace qwerty.xyz |
|
// @version 0.1 |
|
// @match https://info.uniswap.org/ |
|
// @include /^https?://info.uniswap.org/#/pools/0x.+/ |
|
// @icon https://www.google.com/s2/favicons?sz=64&domain=uniswap.org |
|
// @icon https://qwerty.xyz/favicon.png |
|
// @grant none |
|
// @run-at document-idle |
|
// ==/UserScript== |
|
|
|
(function () { |
|
'use strict' |
|
|
|
if (!new RegExp('#/pools/0x.+').test(location.hash)) return |
|
|
|
|
|
const tooltipRowsParent = '.sc-kLokBR.hzNkKE' |
|
const tooltipRows = '.sc-fFSPgy.dJEZYO' |
|
const buttonsContainer = 'button.sc-cGKJUV.jYkPqK' |
|
|
|
|
|
let observer |
|
|
|
function enhance() { |
|
// -------------------------------------------------------------------------------------------------------------------- |
|
|
|
// Tooltip enhancement -------------------------------------------------------- |
|
|
|
if (observer) observer.disconnect() |
|
|
|
observer = new MutationObserver((mutations) => { |
|
const mutation = mutations.find(mutation => mutation.type === 'characterData') |
|
if (!mutation) return |
|
|
|
const parent = mutation.target.parentElement?.closest(tooltipRowsParent) |
|
|
|
// get tooltip rows and extract values |
|
let nodes = [...parent.querySelectorAll(tooltipRows)] // should be 6 |
|
let isMM = nodes[5].childNodes[2].textContent === 'MM' |
|
let [mm_price, usd_price, locked] = nodes.filter((_, index) => index % 2 === 1).map(element => parseFloat(element.textContent) * (element.textContent.includes('k') ? 1000 : 1)) |
|
|
|
// insert new row with calculated tick value |
|
let value = isMM ? mm_price * locked | 0 : locked |
|
let myRow = parent?.querySelector('.myRow') |
|
if (!myRow) parent?.insertAdjacentHTML('afterbegin', `<div class="sc-kfYpNk sc-bCwene sc-iwaiBT eAjRWn vwKsb bfQFKp"><div class="css-1kt4f20">VALUE</div><div class="myRow css-1kt4f20">${value} $</div></div>`) |
|
else myRow.textContent = `${value} $` |
|
}) |
|
|
|
const config = { |
|
characterData: true, // Observe changes to text content |
|
childList: false, // Observe direct children being added/removed |
|
subtree: true, // Observe all descendants |
|
} |
|
|
|
const targetNode = document.querySelector('.recharts-tooltip-wrapper') |
|
if (targetNode) observer.observe(targetNode, config) |
|
console.info('Observer registered!') |
|
|
|
// Graph enhancement ---------------------------------------------------------- |
|
|
|
const nodesParent = document.querySelector('.recharts-responsive-container .recharts-bar-rectangles') |
|
const getNodeList = () => nodesParent.querySelectorAll('.recharts-bar-rectangle rect') |
|
|
|
let buttons = undefined |
|
let isLogScale = false |
|
const inactive = 'jsuRGI' |
|
const active = 'kHobyx' |
|
|
|
setTimeout(() => { |
|
buttons = addButtons() |
|
}) |
|
|
|
const maxHeight = 310 |
|
const maxLogHeight = linearToLogScale(maxHeight) |
|
const scaleFactor = maxHeight / maxLogHeight |
|
|
|
function setLinearScale() { |
|
getNodeList().forEach(node => { |
|
const height = parseFloat(node.dataset.height) |
|
const y = parseFloat(node.dataset.y) |
|
if (!isNaN(height)) { |
|
node.setAttribute('height', height) |
|
node.setAttribute('y', y) |
|
} |
|
}) |
|
} |
|
|
|
function linearToLogScale(linearHeight, scale = 1) { |
|
const logBase = 10 |
|
return scale * Math.log(linearHeight + 1) / Math.log(logBase) |
|
} |
|
|
|
function setLogScale() { |
|
// Loop through the nodes and recalculate and scale heights |
|
getNodeList().forEach(node => { |
|
const height = parseFloat(node.getAttribute('height')) |
|
const y = parseFloat(node.getAttribute('y')) |
|
if (!isNaN(height)) { |
|
node.dataset.height ??= height |
|
node.dataset.y ??= y |
|
|
|
const scaledHeight = linearToLogScale(height, scaleFactor) |
|
|
|
node.setAttribute('height', scaledHeight) |
|
node.setAttribute('y', maxHeight - scaledHeight) |
|
} |
|
}) |
|
} |
|
|
|
function toggleLogScale(state = !isLogScale) { |
|
isLogScale = state |
|
|
|
if (isLogScale) { |
|
buttons.children[0].classList.add(inactive) |
|
buttons.children[1].classList.add(active) |
|
buttons.children[0].classList.remove(active) |
|
buttons.children[1].classList.remove(inactive) |
|
removeLines() |
|
for (let i = 50; i <= maxHeight; i += 50) drawLine(linearToLogScale(i, scaleFactor)) |
|
setLogScale() |
|
} else { |
|
buttons.children[0].classList.add(active) |
|
buttons.children[1].classList.add(inactive) |
|
buttons.children[0].classList.remove(inactive) |
|
buttons.children[1].classList.remove(active) |
|
removeLines() |
|
for (let i = 50; i <= maxHeight; i += 50) drawLine(i) |
|
setLinearScale() |
|
} |
|
} |
|
|
|
window.q_toggleLogScale = toggleLogScale |
|
|
|
function addButtons() { |
|
const target = document.querySelector(buttonsContainer) |
|
if (target) { |
|
|
|
const buttonsHtml = ` |
|
<button width="240px" class="sc-cGKJUV jYkPqK"> |
|
<span onclick="q_toggleLogScale(false)" font-size="12px" class="sc-Jsfmu ${isLogScale ? inactive : active}">Linear</span> |
|
<span onclick="q_toggleLogScale(true)" font-size="12px" class="sc-Jsfmu ${isLogScale ? active : inactive}">Log</span> |
|
</button> |
|
` |
|
target.insertAdjacentHTML('beforebegin', buttonsHtml) |
|
return target.previousElementSibling |
|
} |
|
} |
|
|
|
function drawLine(y) { |
|
const line = `<g class="myLine"><g><rect x="0" y="${maxHeight - y}" fill="rgba(255, 255, 0, 0.5)" width="100%" height="1"></rect></g></g>` |
|
nodesParent.insertAdjacentHTML('afterbegin', line) |
|
} |
|
|
|
function removeLines() { |
|
const childrenToRemove = nodesParent.querySelectorAll('.myLine') |
|
childrenToRemove.forEach(child => { |
|
nodesParent.removeChild(child) |
|
}) |
|
} |
|
|
|
// -------------------------------------------------------------------------------------------------------------------- |
|
} |
|
|
|
|
|
function checkForElement(mutationsList, pageObserver) { |
|
const condition = true |
|
&& document.querySelectorAll('.recharts-tooltip-wrapper .sc-fFSPgy.dJEZYO').length // should be 6 |
|
&& document.querySelector('.recharts-responsive-container .recharts-bar-rectangles') |
|
if (!!condition) { |
|
pageObserver.disconnect() |
|
enhance() |
|
} |
|
} |
|
new MutationObserver(checkForElement).observe(document.body, { childList: true, subtree: true }) |
|
console.info('Waiting for target element...') |
|
})() |