Skip to content

Instantly share code, notes, and snippets.

@rubeniskov
Last active December 29, 2022 18:59
Show Gist options
  • Save rubeniskov/f5ad6c995005cce92468132ef90fa179 to your computer and use it in GitHub Desktop.
Save rubeniskov/f5ad6c995005cce92468132ef90fa179 to your computer and use it in GitHub Desktop.
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('&nbsp;&nbsp;'));
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