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)

@ -5,7 +5,8 @@
"scripts": { "scripts": {
"computeRewards": "npx ts-node scripts/writeStakersData.ts", "computeRewards": "npx ts-node scripts/writeStakersData.ts",
"test": "forge test -vvv --fork-url https://rpc.mevblocker.io --fork-block-number 17466009", "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" "testGas": "forge test -vvv --fork-url https://rpc.mevblocker.io --gas-report",
"testStakersLocked": "npx ts-node test/stakersLockedBalanceSum.ts"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -27,11 +28,9 @@
"@nomiclabs/hardhat-waffle": "^2.0.6", "@nomiclabs/hardhat-waffle": "^2.0.6",
"chai": "^4.3.7", "chai": "^4.3.7",
"ethereum-waffle": "^4.0.10", "ethereum-waffle": "^4.0.10",
"ethers": "^6.5.1", "ethers": "^5",
"@nomicfoundation/hardhat-foundry": "^1.0.1", "@nomicfoundation/hardhat-foundry": "^1.0.1",
"@nomicfoundation/hardhat-verify": "^1.0.1", "@nomicfoundation/hardhat-verify": "^1.0.1",
"@nomiclabs/hardhat-ethers": "^2.2.3",
"ethers": "^5",
"hardhat": "^2.15.0", "hardhat": "^2.15.0",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^5.1.3" "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 governanceAddress = "0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce";
export const oldStakingAddress = "0x2FC93484614a34f26F7970CBB94615bA109BB4bf"; export const oldStakingAddress = "0x2FC93484614a34f26F7970CBB94615bA109BB4bf";
export const multicallContractAddress = "0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441";
export const currentProposalVotingStartBlock = 17492630;
export const governanceDeployedBlock = 11474695; export const governanceDeployedBlock = 11474695;
export const governanceRewardsProposalBlock = 14173399; 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 { EthAddress, IStaker } from "./@types/staker";
import * as dotenv from "dotenv"; 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 GovernanceAbi from "../abi/GovernanceAbi.json";
import StakingAbi from "../abi/StakingABI.json"; import StakingAbi from "../abi/StakingABI.json";
import MulticallABI from "../abi/MultiCallABI.json";
dotenv.config(); dotenv.config();
@ -41,16 +42,25 @@ export async function getStakersWithRewardsBeforeHack(): Promise<Array<IStaker>>
const stakingContract = new web3.eth.Contract(StakingAbi as AbiItem[], oldStakingAddress); const stakingContract = new web3.eth.Contract(StakingAbi as AbiItem[], oldStakingAddress);
const stakersAddresses = await getAddressesStakersWithProssibleRewards(); const stakersAddresses = await getAddressesStakersWithProssibleRewards();
let stakersWithRewardsBeforeHack: Array<IStaker> = []; const multiCallQuery: Array<[string, object]> = stakersAddresses.map((stakerAddress) =>
for (const address of stakersAddresses) { stakingContract.methods.checkReward(stakerAddress).encodeABI()
// Check stakers rewards on previous block before hack );
const rewardsBeforeHack = await stakingContract.methods.checkReward(address).call({}, hackBlock - 1); const stakersWithRewardsBeforeHackRawData = await useMultiCall(oldStakingAddress, multiCallQuery, hackBlock - 1);
// Discard stakers with rewards less than 1 TORN, because accruing each reward requires paying gas const stakersWithRewardsBeforeHack: Array<IStaker> = stakersWithRewardsBeforeHackRawData.map((reward, index) => ({
if (BigNumber(rewardsBeforeHack).div(1e18).isGreaterThan(1)) address: stakersAddresses[index],
stakersWithRewardsBeforeHack.push({ address, rewardBalance: BigNumber(rewardsBeforeHack) }); 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>> { export async function getStakersWithdrawedAfterHack(): Promise<Array<EthAddress>> {
@ -61,3 +71,30 @@ export async function getStakersWithdrawedAfterHack(): Promise<Array<EthAddress>
return stakersWithdrawedAfterHack; 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));
}