Last active
December 29, 2022 18:59
-
-
Save rubeniskov/f5ad6c995005cce92468132ef90fa179 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const global = window.global || window.globalThis || window | |
const getElementByXpath = (path) => | |
document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; | |
// PendingYield__YieldAmount-sc-l0vtma-3 | |
const getPendingYieldYieldAmountElement = () => getElementByXpath('//*[@id="root"]/div/div[3]/section/section/div/section/section/section/div'); | |
// WhirlpoolsPortfolioStats__StyledStats-sc-gdki87-0 | |
const getWhirlpoolsPortfolioStatsStyledStatsElement = () => getElementByXpath('/html/body/div[1]/div/div[3]/section/section/div'); | |
// WhirlpoolsPortfolioStats__PortfolioValue-sc-1k4obia-1 | |
const getWhirlpoolsPortfolioStatsPortfolioValueElement = () => getElementByXpath('//*[@id="root"]/div/div[3]/section/section/div/span'); | |
// WhirlpoolsPortfolioCards__WhirlpoolsContainer-sc0jebqfq-0 | |
const getWhirlpoolsPortfolioCardsWhirlpoolsContainerElement = () => getElementByXpath('/html/body/div[1]/div/div[3]/section/section/section[4]'); | |
// WhirlpoolsPortfolioStats__HarvestSection-sc-1k4obia-2 gamQii | |
const getWhirlpoolsPortfolioStatsHarvestSectionElement = () => getElementByXpath('/html/body/div[1]/div/div[3]/section/section/div/section'); | |
const insertAfter = (newNode, existingNode) => { | |
existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling); | |
} | |
const getTransaction = async (tx) => { | |
return (await fetch(`https://api.solscan.io/transaction?tx=${tx}&cluster=`, { | |
method: 'GET' | |
})).json() | |
} | |
const getFiber = (dom) => { | |
const key = Object.keys(dom).find(key=>{ | |
return key.startsWith("__reactFiber$") // react 17+ | |
|| key.startsWith("__reactInternalInstance$"); // react <17 | |
}); | |
return dom[key] ?? null; | |
} | |
const getProps = (dom) => { | |
const key = Object.keys(dom).find(key=>{ | |
return key.startsWith("__reactProps$") // react 17+ | |
|| key.startsWith("__reactInternalInstance$"); // react <17 | |
}); | |
return dom[key] ?? null; | |
} | |
const getState = () => { | |
const payload = localStorage.getItem('counter-state'); | |
return payload ? JSON.parse(payload) : null | |
}; | |
const setState = (state) => { | |
localStorage.setItem('counter-state', JSON.stringify(state)) | |
}; | |
const findComponent = (root, predicate, acc) => { | |
let retNode = null; | |
for (let leaf of [root.child, root.sibling]) { | |
if (leaf) { | |
const matched = predicate(leaf) ? leaf : null; | |
if (acc) { | |
if (matched) { | |
acc.push(leaf); | |
} | |
findComponent(leaf, predicate, acc); | |
} else { | |
return matched ?? findComponent(leaf, predicate) | |
} | |
} | |
} | |
return acc ?? retNode; | |
} | |
const setSolValue = (value) => { | |
const counterElem = document.getElementById('counter') ?? document.createElement('span'); | |
const actualCounterElem = getWhirlpoolsPortfolioStatsPortfolioValueElement(); | |
if (!document.getElementById('counter') && actualCounterElem) { | |
insertAfter(counterElem, actualCounterElem) | |
counterElem.setAttribute('id', 'counter'); | |
counterElem.setAttribute('class', ['counters', actualCounterElem.getAttribute('class').split(' ').slice(1)].join(' ')); | |
} | |
counterElem.innerHTML = value; | |
} | |
const setUsdValue = (value) => { | |
const counterElem = getWhirlpoolsPortfolioStatsPortfolioValueElement(); | |
if (counterElem) { | |
counterElem.innerHTML = value; | |
} | |
} | |
const setPendingYield = (value) => { | |
const counterElem = getPendingYieldYieldAmountElement(); | |
if (counterElem) { | |
counterElem.setAttribute('style', 'white-space: nowrap; width: 200px;') | |
counterElem.innerHTML = value; | |
} | |
} | |
clearInterval(global.counterTimer); | |
let lastTickTimer = new Date().getTime(); | |
let lastPendingYieldTotalValue = null; | |
let avgIter = 0; | |
let { | |
deltaEarnings = 0, | |
avgEarnings = 0 | |
} = getState() ?? {}; | |
global.counterTimer = setInterval(async () => { | |
const portfolioRootElem = getWhirlpoolsPortfolioCardsWhirlpoolsContainerElement(); | |
if (!portfolioRootElem) return | |
const portfolioRootNode = getFiber(portfolioRootElem); | |
const [solValue, usdValue] = findComponent(portfolioRootNode, ({ key }) => /portfolio-card/.test(key), []).map(({ pendingProps }) => pendingProps).filter(({ tokenSymbolA }) => tokenSymbolA === 'SOL').reduce((acc, { positionUsdValue, nonInvertedPrice }) => { | |
return [acc[0] + positionUsdValue / nonInvertedPrice, acc[1] + positionUsdValue / 1] | |
}, [0, 0]); | |
setSolValue(new Intl.NumberFormat('en-EN', { currency: 'SOL', style: 'currency' }).format(solValue)) | |
setUsdValue(new Intl.NumberFormat('en-EN', { currency: 'USD', style: 'currency' }).format(usdValue)); | |
const harvestRootNode = getFiber(getWhirlpoolsPortfolioStatsHarvestSectionElement()) | |
let curPendingYieldTotalValue = harvestRootNode.child.pendingProps.pendingYieldData.usdTotalValue; | |
if (lastPendingYieldTotalValue === null) { | |
lastPendingYieldTotalValue = curPendingYieldTotalValue; | |
} | |
if (curPendingYieldTotalValue > lastPendingYieldTotalValue) { | |
let curTickTime = new Date().getTime(); | |
deltaEarnings = (curPendingYieldTotalValue - lastPendingYieldTotalValue) / (curTickTime - lastTickTimer) | |
lastTickTimer = curTickTime; | |
lastPendingYieldTotalValue = curPendingYieldTotalValue; | |
// https://jrsinclair.com/articles/2019/five-ways-to-average-with-js-reduce/ | |
avgEarnings = (deltaEarnings + avgIter * avgEarnings) / (avgIter + 1) | |
avgIter += 1; | |
} | |
const hourUnit = avgEarnings * 60000 < 0.01; | |
setPendingYield([ | |
`${new Intl.NumberFormat('en-EN', { currency: 'USD', style: 'currency' }).format(avgEarnings * (hourUnit ? 3600000 : 60000))}/${hourUnit ? 'hr' : 'min'}`, | |
`${new Intl.NumberFormat('en-EN', { currency: 'USD', style: 'currency' }).format(curPendingYieldTotalValue)}/all` | |
].join(' ')); | |
setState({ | |
deltaEarnings, | |
avgEarnings | |
}) | |
}, 1000); | |
(async () => { | |
console.log(await getTransaction("3otDFXx8hiDDUiSivrTkftH2HrhpzMsXkBmSWHvJdGZkKAU14aCpCXvAhQnybVSCELyVp6dCf23Pywmz7J2NXLGK")) | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment