Add calculation of stakers (to who we propose to restore rewards) locked balances & use multicall, speed up scripts execution more than 10 times

This commit is contained in:
Theo 2023-06-19 09:05:39 -07:00
parent dbd12a63a2
commit 55a6540a13
6 changed files with 195 additions and 48 deletions

86
abi/MultiCallABI.json Normal file

@ -0,0 +1,86 @@
[
{
"constant": true,
"inputs": [],
"name": "getCurrentBlockTimestamp",
"outputs": [{ "name": "timestamp", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"components": [
{ "name": "target", "type": "address" },
{ "name": "callData", "type": "bytes" }
],
"name": "calls",
"type": "tuple[]"
}
],
"name": "aggregate",
"outputs": [
{ "name": "blockNumber", "type": "uint256" },
{ "name": "returnData", "type": "bytes[]" }
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getLastBlockHash",
"outputs": [{ "name": "blockHash", "type": "bytes32" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [{ "name": "addr", "type": "address" }],
"name": "getEthBalance",
"outputs": [{ "name": "balance", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getCurrentBlockDifficulty",
"outputs": [{ "name": "difficulty", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getCurrentBlockGasLimit",
"outputs": [{ "name": "gaslimit", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getCurrentBlockCoinbase",
"outputs": [{ "name": "coinbase", "type": "address" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [{ "name": "blockNumber", "type": "uint256" }],
"name": "getBlockHash",
"outputs": [{ "name": "blockHash", "type": "bytes32" }],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]

@ -0,0 +1 @@
Locked balance of stakers to who we want restore rewards: 392252226884126520706939 (~ 392252.23 TORN)

@ -1,39 +1,38 @@
{
"name": "proposal-25-restore-rewards",
"version": "1.0.0",
"description": "Proposal to restore old rewards value (as before hack) after redeploying Staking contract",
"scripts": {
"computeRewards": "npx ts-node scripts/writeStakersData.ts",
"test": "forge test -vvv --fork-url https://rpc.mevblocker.io --fork-block-number 17466009",
"testGas": "forge test -vvv --fork-url https://rpc.mevblocker.io --gas-report"
},
"repository": {
"type": "git",
"url": "https://git.tornado.ws/Theo/proposal-25-restore-rewards"
},
"author": "Theo",
"license": "MIT",
"dependencies": {
"@openzeppelin/contracts": "^3.2.0-rc.0",
"@openzeppelin/upgrades-core": "^1.0.1",
"bignumber.js": "^9.1.1",
"dotenv": "^16.1.4",
"torn-token": "^1.0.4",
"web3": "^1.10.0",
"web3-utils": "^1.10.0"
},
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.2.3",
"@nomiclabs/hardhat-waffle": "^2.0.6",
"chai": "^4.3.7",
"ethereum-waffle": "^4.0.10",
"ethers": "^6.5.1",
"@nomicfoundation/hardhat-foundry": "^1.0.1",
"@nomicfoundation/hardhat-verify": "^1.0.1",
"@nomiclabs/hardhat-ethers": "^2.2.3",
"ethers": "^5",
"hardhat": "^2.15.0",
"ts-node": "^10.9.1",
"typescript": "^5.1.3"
}
"name": "proposal-25-restore-rewards",
"version": "1.0.0",
"description": "Proposal to restore old rewards value (as before hack) after redeploying Staking contract",
"scripts": {
"computeRewards": "npx ts-node scripts/writeStakersData.ts",
"test": "forge test -vvv --fork-url https://rpc.mevblocker.io --fork-block-number 17466009",
"testGas": "forge test -vvv --fork-url https://rpc.mevblocker.io --gas-report",
"testStakersLocked": "npx ts-node test/stakersLockedBalanceSum.ts"
},
"repository": {
"type": "git",
"url": "https://git.tornado.ws/Theo/proposal-25-restore-rewards"
},
"author": "Theo",
"license": "MIT",
"dependencies": {
"@openzeppelin/contracts": "^3.2.0-rc.0",
"@openzeppelin/upgrades-core": "^1.0.1",
"bignumber.js": "^9.1.1",
"dotenv": "^16.1.4",
"torn-token": "^1.0.4",
"web3": "^1.10.0",
"web3-utils": "^1.10.0"
},
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.2.3",
"@nomiclabs/hardhat-waffle": "^2.0.6",
"chai": "^4.3.7",
"ethereum-waffle": "^4.0.10",
"ethers": "^5",
"@nomicfoundation/hardhat-foundry": "^1.0.1",
"@nomicfoundation/hardhat-verify": "^1.0.1",
"hardhat": "^2.15.0",
"ts-node": "^10.9.1",
"typescript": "^5.1.3"
}
}

@ -0,0 +1,21 @@
import fs from "fs";
import path from "path";
import { getStakersLockedBalancesSum } from "../utils/stakers";
import { currentProposalVotingStartBlock } from "../utils/config";
async function main() {
const stakersLockedBalanceSum = await getStakersLockedBalancesSum(currentProposalVotingStartBlock);
console.log("Locked balance of stakers to who we want restore rewards:", stakersLockedBalanceSum.div(1e18).toFixed(2), "TORN");
fs.writeFileSync(
path.join("data", "stakersLockedBalanceSum.txt"),
`Locked balance of stakers to who we want restore rewards: ${stakersLockedBalanceSum.toString(10)} (~ ${stakersLockedBalanceSum
.div(1e18)
.toFixed(2)} TORN)`,
{ encoding: "utf-8" }
);
}
main();

@ -1,5 +1,8 @@
export const governanceAddress = "0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce";
export const oldStakingAddress = "0x2FC93484614a34f26F7970CBB94615bA109BB4bf";
export const multicallContractAddress = "0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441";
export const currentProposalVotingStartBlock = 17492630;
export const governanceDeployedBlock = 11474695;
export const governanceRewardsProposalBlock = 14173399;
export const hackBlock = 17299139;
export const hackBlock = 17299139;

@ -4,10 +4,11 @@ import { AbiItem } from "web3-utils";
import { EthAddress, IStaker } from "./@types/staker";
import * as dotenv from "dotenv";
import { governanceAddress, governanceRewardsProposalBlock, hackBlock, oldStakingAddress } from "./config";
import { governanceAddress, governanceRewardsProposalBlock, hackBlock, oldStakingAddress, multicallContractAddress } from "./config";
import GovernanceAbi from "../abi/GovernanceAbi.json";
import StakingAbi from "../abi/StakingABI.json";
import MulticallABI from "../abi/MultiCallABI.json";
dotenv.config();
@ -41,16 +42,25 @@ export async function getStakersWithRewardsBeforeHack(): Promise<Array<IStaker>>
const stakingContract = new web3.eth.Contract(StakingAbi as AbiItem[], oldStakingAddress);
const stakersAddresses = await getAddressesStakersWithProssibleRewards();
let stakersWithRewardsBeforeHack: Array<IStaker> = [];
for (const address of stakersAddresses) {
// Check stakers rewards on previous block before hack
const rewardsBeforeHack = await stakingContract.methods.checkReward(address).call({}, hackBlock - 1);
// Discard stakers with rewards less than 1 TORN, because accruing each reward requires paying gas
if (BigNumber(rewardsBeforeHack).div(1e18).isGreaterThan(1))
stakersWithRewardsBeforeHack.push({ address, rewardBalance: BigNumber(rewardsBeforeHack) });
}
const multiCallQuery: Array<[string, object]> = stakersAddresses.map((stakerAddress) =>
stakingContract.methods.checkReward(stakerAddress).encodeABI()
);
const stakersWithRewardsBeforeHackRawData = await useMultiCall(oldStakingAddress, multiCallQuery, hackBlock - 1);
const stakersWithRewardsBeforeHack: Array<IStaker> = stakersWithRewardsBeforeHackRawData.map((reward, index) => ({
address: stakersAddresses[index],
rewardBalance: BigNumber(reward),
}));
return stakersWithRewardsBeforeHack;
// let stakersWithRewardsBeforeHack: Array<IStaker> = [];
// for (const address of stakersAddresses) {
// // Check stakers rewards on previous block before hack
// const rewardsBeforeHack = await stakingContract.methods.checkReward(address).call({}, hackBlock - 1);
// // Discard stakers with rewards less than 1 TORN, because accruing each reward requires paying gas
// if (BigNumber(rewardsBeforeHack).div(1e18).isGreaterThan(1))
// stakersWithRewardsBeforeHack.push({ address, rewardBalance: BigNumber(rewardsBeforeHack) });
// }
return stakersWithRewardsBeforeHack.filter((staker) => staker.rewardBalance.div(1e18).isGreaterThan(1));
}
export async function getStakersWithdrawedAfterHack(): Promise<Array<EthAddress>> {
@ -61,3 +71,30 @@ export async function getStakersWithdrawedAfterHack(): Promise<Array<EthAddress>
return stakersWithdrawedAfterHack;
}
async function useMultiCall(contractAddress: EthAddress, queryArray: Array<Object>, callBlock?: number): Promise<any[]> {
const multiCallContract = new web3.eth.Contract(MulticallABI as AbiItem[], multicallContractAddress);
const multicallQueryArray = queryArray.map((query) => [contractAddress, query]);
const { returnData } = await multiCallContract.methods.aggregate(multicallQueryArray).call({}, callBlock);
return returnData;
}
export async function getStakersLockedBalancesSum(block: number): Promise<BigNumber> {
const governanceContract = new web3.eth.Contract(GovernanceAbi as AbiItem[], governanceAddress);
// All stakers who had more than 1 TORN in rewards at the time of the hack
const stakersWithRewardsBeforeHack = await getStakersWithRewardsBeforeHack();
// Stakers who withdrew rewards from the time of hack until the balance of the old Staking contract was nullified
const stakersWithdrawedAfterHack = await getStakersWithdrawedAfterHack();
// It makes no sense to restore awards to those who already withdrew them from the old Staking contract
const stakersToRestoreRewards = stakersWithRewardsBeforeHack.filter((staker) => !stakersWithdrawedAfterHack.includes(staker.address));
const lockedBalances: Array<string> = await useMultiCall(
governanceAddress,
stakersToRestoreRewards.map((staker) => governanceContract.methods.lockedBalance(staker.address).encodeABI()),
block
);
return lockedBalances.reduce((acc, cur) => acc.plus(BigNumber(cur)), BigNumber(0));
}