Skip to content

Instantly share code, notes, and snippets.

@dbeal-eth
Created March 30, 2023 22:36
Embed
What would you like to do?
const ethers = require('ethers');
const address = `${process.argv[2]}`;
const api = `${process.env.PROVIDER_URL}`;
const provider = new ethers.providers.JsonRpcProvider(api);
const ABI = require('./Synthetix.json');
const synthetixAddress = '0x8700daec35af8ff88c16bdf0418774cb3d7599b4';
const synthetixUsdAddress = '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9';
const BATCH_SIZE = 1000000;
async function findBlockByNumber(time, start = 0, end = 'latest') {
if (end === 'latest') {
end = await provider.getBlockNumber();
}
if (end - start <= 1) {
return start;
}
const checkBlock = Math.floor((start + end) / 2);
console.log('check block', checkBlock);
const block = await provider.getBlock(checkBlock);
console.log(block.timestamp, '<', time);
if (block.timestamp < time) {
return await findBlockByNumber(time, checkBlock, end);
} else {
return await findBlockByNumber(time, start, checkBlock);
}
}
async function sumEvents(contract, filter, startBlock, endBlock) {
let summed = ethers.BigNumber.from(0);
for (let i = startBlock; i < endBlock; i += BATCH_SIZE) {
try {
console.log('querying', i, endBlock);
const events = await provider.getLogs({
fromBlock: i,
toBlock: i + BATCH_SIZE,
address: contract,
topics: filter
});
if (events.length) console.log(`found ${events.length} events`);
for (const e of events) {
// get the txn associated with this event to make sure it meets a standard
const txn = await provider.getTransaction(e.transactionHash);
//console.log(txn);
// must match function selector
if (
txn.data.startsWith('0xaf086c7e') || // issueMaxSynths
txn.data.startsWith('0x320223db') || // issue max synths on behalf
txn.data.startsWith('0x8a290014') || // issue synths
txn.data.startsWith('0xe8e09b8b') || // issue synths on behalf
txn.data.startsWith('0x295da87d') || // burn synths
txn.data.startsWith('0xc2bf3880') || // burn synths on behalf
txn.data.startsWith('0x9741fb22') || // burn synths to target
txn.data.startsWith('0x2c955fa7') // burn synths to target on behalf
) {
//console.log(e);
summed = summed.add(ethers.utils.defaultAbiCoder.decode(['uint256'], e.data)[0]);
} else {
console.log('skip non-matching selector');
}
}
} catch (err) {
console.log('skip', err);
}
}
return summed;
}
async function calculateStakerPnl(address, taxYear) {
const contract = new ethers.Contract(synthetixAddress, ABI, provider);
const startTime = Math.floor(Date.parse(taxYear) / 1000);
const endTime = taxYear + 31536000;
const startBlockTag = await findBlockByNumber(startTime);
const endBlockTag = await findBlockByNumber(endTime);
const startDebt = await contract.debtBalanceOf(address, ethers.utils.formatBytes32String('sUSD'), { blockTag: startBlockTag });
const endDebt = await contract.debtBalanceOf(address, ethers.utils.formatBytes32String('sUSD'), { blockTag: endBlockTag });
// figure out all the mints and burns in between
const minted = await sumEvents(synthetixUsdAddress, [ethers.utils.id('Issued(address,uint256)'), ethers.utils.defaultAbiCoder.encode(['address'], [address])], startBlockTag, endBlockTag);
const burned = await sumEvents(synthetixUsdAddress, [ethers.utils.id('Burned(address,uint256)'), ethers.utils.defaultAbiCoder.encode(['address'], [address])], startBlockTag, endBlockTag);
console.log('start debt', ethers.utils.formatEther(startDebt));
console.log('end debt', ethers.utils.formatEther(endDebt));
console.log('total mint', ethers.utils.formatEther(minted));
console.log('total burn', ethers.utils.formatEther(burned));
// add it all together
const total = minted.add(startDebt).sub(endDebt).sub(burned);
console.log('debt pool profit/loss:', ethers.utils.formatEther(total));
return total;
}
calculateStakerPnl(process.argv[2], process.argv[3]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment