Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Synthetix top holders lock status
<header><img src="https://developer.synthetix.io/api/img/logo.png" /></header>
<main>
<p class="helper-text">Note: these results aren't strictly ordered to increase performance.</p>
<button name="begin">Begin</button>
<button name="stop">Stop</button>
<ul>
<li id="tokenHolders"><strong>Token Holders:</strong><var>?</var></li>
<li id="neverIssued"><strong>Never Issued:</strong><var>?</var></li>
<li id="snxPrice"><strong>SNX Price:</strong><var>?</var></li>
<li id="snxTotal"><strong>SNX Total:</strong><var>?</var></li>
<li id="snxLocked"><strong>SNX Locked:</strong><var>?</var></li>
<li id="pcentLocked"><strong>&nbsp;</strong><var>?</var></li>
<li id="networkRatio"><strong>Network Ratio:</strong><var>?</var></li>
<li id="activeRatio"><strong>Active Ratio:</strong><var>?</var></li>
</ul>
<table>
<thead><tr><th></th><th>Address</th><th>Balance</th><th>CRatio</th><th>Locked SNX</th><th>Debt Balance</th></tr></thead>
<tbody><tr><td colspan="100" class="helper-text">Press Begin to start....</td></tr></tbody>
</table>
</main>
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 60e3; // 1min timeout on for loop below
const snxjs = new SynthetixJs.SynthetixJs();
const toUtf8Bytes = SynthetixJs.SynthetixJs.utils.formatBytes32String;
const tbody = document.querySelector('tbody');
const beginBtn = document.querySelector('button[name="begin"]');
const stopBtn = document.querySelector('button[name="stop"]');
const tokenHoldersTarget = document.querySelector('#tokenHolders var');
const snxPriceTarget = document.querySelector('#snxPrice var');
const snxTotalTarget = document.querySelector('#snxTotal var');
const snxLockedTarget = document.querySelector('#snxLocked var');
const pcentLockedTarget = document.querySelector('#pcentLocked var');
const neverIssuedTarget = document.querySelector('#neverIssued var');
const networkRatioTarget = document.querySelector('#networkRatio var');
const activeRatioTarget = document.querySelector('#activeRatio var');
let stopFlag = false;
stopBtn.addEventListener('click', () => stopFlag = true);
const lookup = async () => {
tbody.innerHTML = '<img src="https://media.giphy.com/media/TvLuZ00OIADoQ/giphy.gif" width=150 />';
beginBtn.setAttribute('disabled', true);
try {
tokenHoldersTarget.innerHTML = 0;
neverIssuedTarget.innerHTML = 0;
tbody.innerHTML = '<tr><td colspan=100 class="helper-text">loading...</td></tr>';
const holders = await snxData.snx.holders({ max: 24000 });
const escrowContracts = await Promise.all([snxjs.Synthetix.escrow(), snxjs.Synthetix.rewardEscrow()]);
const exchangeWalletsReq = await fetch('https://raw.githubusercontent.com/whjn011205/ScoreEngine/fe383336db49a7601f1793b8ad570a8d0a8fb6df/src/data/goodList.json');
const exchangeWallets = await exchangeWalletsReq.json();
// const issuanceRatioToAvoidPenalty = Number(snxjs.utils.formatEther(await snxjs.FeePool.getPenaltyThresholdRatio()));
const issuanceRatio = Number(snxjs.utils.formatEther(await snxjs.SynthetixState.issuanceRatio()));
const results = holders.filter(({ address }) => escrowContracts.indexOf(address.toLowerCase()) === -1);
//const sleep = ms => new Promise(res => setTimeout(res, ms));
const usdToSnxPrice = snxjs.utils.formatEther(await snxjs.Depot.usdToSnxPrice());
snxPriceTarget.innerHTML = `$${Number(usdToSnxPrice).toFixed(5)}`;
let snxTotal = 0;
let snxLocked = 0;
let activeStakers = 0;
let snxHolders = 0;
let networkRatio = [];
let activeRatio = [];
const updateTotals = ({ balance, lockedSnx, collateralRatio }) => {
snxHolders++;
if (Number(collateralRatio) > 0) {
activeStakers++;
activeRatio.push(Number(collateralRatio));
}
networkRatio.push(Number(collateralRatio));
neverIssuedTarget.innerHTML = Number(neverIssuedTarget.innerHTML) + (Number(lockedSnx) > 0 ? 0 : 1);
tokenHoldersTarget.innerHTML = Number(tokenHoldersTarget.innerHTML) + 1;
snxTotal += Number(balance);
snxLocked += Number(lockedSnx);
snxTotalTarget.innerHTML = `${numbro(snxTotal).format('0,000.00')} (${numbro(snxTotal * usdToSnxPrice).format('0,0.00')} USD)`;
snxLockedTarget.innerHTML = `${numbro(snxLocked).format('0,000.00')} (${numbro(snxLocked * usdToSnxPrice).format('0,0.00')} USD)`;
pcentLockedTarget.innerHTML=`${Number(snxLocked/snxTotal*100).toFixed(2)}%`;
networkRatioTarget.innerHTML=`${Number(1/(networkRatio.reduce((a,b) => a+b)/snxHolders)*100).toFixed(2)}%`;
activeRatioTarget.innerHTML=`${Number(1/(activeRatio.reduce((a,b) => a+b)/activeStakers)*100).toFixed(2)}%`;
}
let row = 1;
for (const { address, collateral } of results) {
const addressLink = `<a target="_blank" href="https://etherscan.io/address/${address}">${address}</a>`;
const promises = await Promise.all([snxjs.Synthetix.collateral(address), snxjs.Synthetix.collateralisationRatio(address), snxjs.Synthetix.debtBalanceOf(address, toUtf8Bytes('sUSD'))]);
const [ balance, collateralRatio,debtBalance] = promises.map(snxjs.utils.formatEther);
// ignore if 0 balance
console.log('skip');
if (Number(balance) <= 0) continue;
const exchangeWallet = exchangeWallets.find(({ exchange }) => exchange.toLowerCase() === address.toLowerCase());
const balanceFormatted = numbro(balance).format('0,000.00');
const balanceUSD = numbro(balance * usdToSnxPrice);
const balanceUSDFormatted = numbro(balanceUSD).format('0,000.00');
const lockedSnx = balance * Math.min(1, collateralRatio/issuanceRatio);
updateTotals({ balance, lockedSnx, collateralRatio });
const currentCRatio = collateralRatio > 0 ? (1/collateralRatio)*100 : 0;
tbody.innerHTML += `<tr><td>${row++}.</td><td>${exchangeWallet ? `<strong>${exchangeWallet.name}</strong>` : ''} ${addressLink}</td><td>${balanceFormatted} ($${balanceUSDFormatted})</td><td>${Math.round(currentCRatio)}%</td><td>${Math.round((lockedSnx/balance) * 100)}%</td><td>${numbro(debtBalance).format('0,0.00')}</tr>`;
if (stopFlag) break;
}
stopFlag = false;
} catch (err) {
tbody.innerHTML = `<span style="color:red">${err}</span>`;
}
tbody.querySelector('tr:first-child>td').innerHTML = '';
beginBtn.removeAttribute('disabled');
};
beginBtn.addEventListener('click', () => {
lookup();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.29/browser-polyfill.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.29/browser.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/synthetix-js/dist/main.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/numbro/2.0.5/numbro.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/synthetix-data@1.3.8/index.min.js"></script>
@snx-color: #0b0816;
body {
font-family: 'Helvetica';
font-size: 14px;
padding: 0;
margin: 0;
}
header {
background-color: @snx-color;
min-height: 50px;
img {
padding: 8px;
}
}
main {
padding: 20px;
}
button {
margin: 0px 0px 20px 0;
padding: 10px 20px;
font-size: 14px;
border-radius: 4px;
background-color: @snx-color;
color: white;
font-weight: bold;
opacity: 0.8;
cursor: pointer;
&:hover {
opacity: 1
}
&[name=stop] {
background-color: red;
}
}
ul {
padding: 0;
li {
padding-left: 3px;
list-style-type: none;
strong {
display: inline-block;
width: 120px;
}
}
}
var {
font-family: "Courier New", "Courier";
font-size: 18px;
}
.leaderboard {
display: flex;
> * {
width: 50%;
}
}
a {
color: @snx-color;
}
table {
width: 100%;
th, td {
text-align: right;
}
tr:nth-child(even) {
background-color: #e8e8e8;
}
}
.helper-text {
color: #aaa;
font-size: 12px;
}
.error-text {
color: red;
}
.success-text {
color: green;
a {
color: darkgreen;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.