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:
parent
dbd12a63a2
commit
55a6540a13
86
abi/MultiCallABI.json
Normal file
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"
|
||||||
|
}
|
||||||
|
]
|
1
data/stakersLockedBalanceSum.txt
Normal file
1
data/stakersLockedBalanceSum.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Locked balance of stakers to who we want restore rewards: 392252226884126520706939 (~ 392252.23 TORN)
|
73
package.json
73
package.json
@ -1,39 +1,38 @@
|
|||||||
{
|
{
|
||||||
"name": "proposal-25-restore-rewards",
|
"name": "proposal-25-restore-rewards",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Proposal to restore old rewards value (as before hack) after redeploying Staking contract",
|
"description": "Proposal to restore old rewards value (as before hack) after redeploying Staking contract",
|
||||||
"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": {
|
},
|
||||||
"type": "git",
|
"repository": {
|
||||||
"url": "https://git.tornado.ws/Theo/proposal-25-restore-rewards"
|
"type": "git",
|
||||||
},
|
"url": "https://git.tornado.ws/Theo/proposal-25-restore-rewards"
|
||||||
"author": "Theo",
|
},
|
||||||
"license": "MIT",
|
"author": "Theo",
|
||||||
"dependencies": {
|
"license": "MIT",
|
||||||
"@openzeppelin/contracts": "^3.2.0-rc.0",
|
"dependencies": {
|
||||||
"@openzeppelin/upgrades-core": "^1.0.1",
|
"@openzeppelin/contracts": "^3.2.0-rc.0",
|
||||||
"bignumber.js": "^9.1.1",
|
"@openzeppelin/upgrades-core": "^1.0.1",
|
||||||
"dotenv": "^16.1.4",
|
"bignumber.js": "^9.1.1",
|
||||||
"torn-token": "^1.0.4",
|
"dotenv": "^16.1.4",
|
||||||
"web3": "^1.10.0",
|
"torn-token": "^1.0.4",
|
||||||
"web3-utils": "^1.10.0"
|
"web3": "^1.10.0",
|
||||||
},
|
"web3-utils": "^1.10.0"
|
||||||
"devDependencies": {
|
},
|
||||||
"@nomiclabs/hardhat-ethers": "^2.2.3",
|
"devDependencies": {
|
||||||
"@nomiclabs/hardhat-waffle": "^2.0.6",
|
"@nomiclabs/hardhat-ethers": "^2.2.3",
|
||||||
"chai": "^4.3.7",
|
"@nomiclabs/hardhat-waffle": "^2.0.6",
|
||||||
"ethereum-waffle": "^4.0.10",
|
"chai": "^4.3.7",
|
||||||
"ethers": "^6.5.1",
|
"ethereum-waffle": "^4.0.10",
|
||||||
"@nomicfoundation/hardhat-foundry": "^1.0.1",
|
"ethers": "^5",
|
||||||
"@nomicfoundation/hardhat-verify": "^1.0.1",
|
"@nomicfoundation/hardhat-foundry": "^1.0.1",
|
||||||
"@nomiclabs/hardhat-ethers": "^2.2.3",
|
"@nomicfoundation/hardhat-verify": "^1.0.1",
|
||||||
"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"
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
21
test/stakersLockedBalanceSum.ts
Normal file
21
test/stakersLockedBalanceSum.ts
Normal file
@ -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));
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user