Skip to content

Instantly share code, notes, and snippets.

@ackvf
Last active January 26, 2024 05:10
Show Gist options
  • Save ackvf/0e1d09dc4f156a5ba262455104a8feae to your computer and use it in GitHub Desktop.
Save ackvf/0e1d09dc4f156a5ba262455104a8feae to your computer and use it in GitHub Desktop.
Crypto scripts

Crypto scripts

Uniswap Pools range convertor (link👇)

Calculate and display pool ranges in USD

displaying usd ranges

Uniswap liquidity Tick evaluator (link👇)

Evaluates the value of a tick

Before
image

After
image

Use as UserScript with Greasemonkey / Tampermonkey

Create new Bookmark

add new bookmark page

Edit Bookmark details

  • Name: for example "Uniswap USD"
  • URL: Copy line 4 from Uniswap_Pools_USD_ranges.js
    The one starting with:

    javascript: ...

edit bookmark

// To verify that the code of the bookmarklet is indeed what is executed, you can run this code and compare the output.
function toBase64Bookmarklet(fn){
const text = fn.toString()
const body = text.slice(text.indexOf('{') + 1, text.lastIndexOf('}'))
const bstring = `javascript:eval(atob('${btoa(body)}'))`
copy(bstring)
return bstring
}
function enhance() {} // <- replace this with the code of the actual enhance function.
toBase64Bookmarklet(enhance) // returns javascript:eval(atob('ewogIC8vIG1hZGUgYnk...
// ==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...')
})()
// ==UserScript==
// @name Uniswap Pools range convertor
// @description Calculate and display pool ranges in USD
// @author Qwerty <qwerty@qwerty.xyz>
// @namespace qwerty.xyz
// @version 0.1
// @match https://app.uniswap.org/pools
// @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'
const ETH_USDC = 2222 // adjust the bookmarklet with current ETH reate whenever you want
const priceRow = '.PositionListItem__RangeLineItem-sc-c6f6cae0-2'
const priceCell = '.PositionListItem__RangeText-sc-c6f6cae0-4'
const tickerSpan = '.HoverInlineText__TextWrapper-sc-707bc6e1-0'
function enhance() {
// --------------------------------------------------------------------------------------------------------------------
const sp = document.createElement("span")
const rows = document.querySelectorAll(priceRow)
rows.forEach(row => {
let valid = false
const clone = row.cloneNode(true)
const cells = clone.querySelectorAll(priceCell)
cells.forEach(cell => {
// ticker pair eg. MM / USDC
const [A, B] = Array.from(cell.querySelectorAll(tickerSpan), el => el.innerText)
if (["USDC", "USDT", "USD"].includes(A)) return
const [label, price, ticker/*, per, ticker2*/] = cell.childNodes
let replacedPrice = price.textContent.replaceAll(/[^0-9-.∞]/g, '')
if (replacedPrice == '∞') replacedPrice = Infinity
const parsedPrice = parseFloat(replacedPrice)
switch (B) {
case "USD":
case "USDC":
case "USDT":
price.innerText = 1 / parsedPrice
break
case "WETH":
case "ETH":
price.innerText = 1 / parsedPrice * ETH_USDC
break
default:
price.innerText = 1 / parsedPrice
console.error('Non-recognized pair:', B, A, 'Only ETH and USD are supported in a pair.')
// return valid = false
}
ticker.innerText = ' USD'
cell.replaceChildren(label, price, ticker)
valid = true
})
if (!valid) return
// swap Min Max ranges
const t1 = clone.children[0].childNodes[1]
const t2 = clone.children[3].childNodes[1]
clone.children[0].replaceChild(sp, t1)
clone.children[3].replaceChild(t1, t2)
clone.children[0].replaceChild(t2, sp)
if (valid) row.after(clone)
})
// --------------------------------------------------------------------------------------------------------------------
}
let timeout
function checkForElement(mutationsList, observer) {
if (document.querySelectorAll(priceRow).length) {
// debounce by 1 second until after last row is rendered
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
observer.disconnect()
enhance()
}, 1000)
}
}
new MutationObserver(checkForElement).observe(document, { childList: true, subtree: true })
console.info('Waiting for target element...')
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment