Add script to calculate relayer losses during the hack
This commit is contained in:
parent
739c28414c
commit
8c97665093
4
data/relayerLosses.txt
Normal file
4
data/relayerLosses.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
0x864DF9CD806D58341f13602103Bf853066ff962a = 625894225496155734516 // ~ 625 TORN
|
||||||
|
0x5555555731006f71f121144534Ca7C8799F66AA3 = 43970301082908267318 // ~ 43 TORN
|
||||||
|
0x2Ee39Ff05643bC7cc9ed31B71e142429044A425C = 189640345451160934437 // ~ 189 TORN
|
||||||
|
0x03392600086874456E08D2bAc104380BCdEBCfC0 = 129757783603193410350 // ~ 129 TORN
|
14
package.json
14
package.json
@ -1,22 +1,26 @@
|
|||||||
{
|
{
|
||||||
"name": "forge-proposal-template",
|
"name": "proposal-33-compensate-relayer-losses",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"repository": "https://git.tornado.ws/Theo/forge-proposal-template",
|
"repository": "https://git.tornado.ws/Theo/proposal-33-compensate-relayer-losses",
|
||||||
"author": "Theo",
|
"author": "Theo",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"init": "cd lib && git clone --recurse-submodules https://github.com/foundry-rs/forge-std",
|
"init": "cd lib && git clone --recurse-submodules https://github.com/foundry-rs/forge-std",
|
||||||
"test:windows": ".\\.env.bat && forge test",
|
"test:windows": ".\\.env.bat && forge test",
|
||||||
"test:linux": ". .env && forge test",
|
"test:linux": ". .env && forge test",
|
||||||
"test:gas:windows": ".\\.env.bat && forge test --gas-report",
|
"test:gas:windows": ".\\.env.bat && forge test --gas-report",
|
||||||
"test:gas:linux": ". .env && forge test --gas-report"
|
"test:gas:linux": ". .env && forge test --gas-report",
|
||||||
|
"calculate": "npx ts-node scripts/calculateLosses.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ensdomains/ens-contracts": "^0.0.21",
|
"@ensdomains/ens-contracts": "^0.0.21",
|
||||||
"@openzeppelin/contracts": "^4.9.0",
|
"@openzeppelin/contracts": "^4.9.0",
|
||||||
"@openzeppelin/upgrades-core": "^1.26.2"
|
"@openzeppelin/upgrades-core": "^1.26.2",
|
||||||
|
"axios": "^1.5.1",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
|
"web3": "^4.2.0",
|
||||||
|
"web3-utils": "^4.0.7"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@gnosis.pm/ido-contracts": "^0.5.0",
|
"@gnosis.pm/ido-contracts": "^0.5.0",
|
||||||
|
67
scripts/calculateLosses.ts
Normal file
67
scripts/calculateLosses.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import * as dotenv from "dotenv";
|
||||||
|
import { Web3 } from "web3";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import axios from "axios";
|
||||||
|
import { RelayerLosses, TxReceipt } from "./types";
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const web3 = new Web3(process.env.MAINNET_RPC_URL);
|
||||||
|
const routerAddress = "0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b";
|
||||||
|
|
||||||
|
async function fetchFailedWithdrawals() {
|
||||||
|
const attackStartBlock = 18363087;
|
||||||
|
const attackEndBlock = 18366622;
|
||||||
|
|
||||||
|
let failedWithdrawalTransactions = [];
|
||||||
|
for (let blockNumber = attackStartBlock; blockNumber <= attackEndBlock; blockNumber++) {
|
||||||
|
let block = null;
|
||||||
|
while (block == null)
|
||||||
|
try {
|
||||||
|
block = await web3.eth.getBlock(blockNumber);
|
||||||
|
} catch (e) {}
|
||||||
|
const results = await Promise.allSettled(block.transactions.map((tx) => web3.eth.getTransactionReceipt(tx as string)));
|
||||||
|
const transactions = results.filter((r) => r.status === "fulfilled").map((r) => (r as PromiseFulfilledResult<TxReceipt>).value);
|
||||||
|
failedWithdrawalTransactions.push(...transactions.filter((tx) => tx.to === routerAddress.toLowerCase() && tx.status == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return failedWithdrawalTransactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function calculateLosses(failedTransactions: Array<TxReceipt>): Promise<RelayerLosses> {
|
||||||
|
return failedTransactions.reduce((acc, tx) => {
|
||||||
|
const gasExpenses = BigInt(tx.gasUsed) * BigInt(tx.effectiveGasPrice!);
|
||||||
|
acc[tx.from] = acc[tx.from] ? acc[tx.from] + gasExpenses : gasExpenses;
|
||||||
|
return acc;
|
||||||
|
}, {} as RelayerLosses);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getEthRateInTorn(): Promise<bigint> {
|
||||||
|
const api = "https://api.binance.com/api/v3/ticker/price?symbol=";
|
||||||
|
const fetchPrice = async (symbol: string) => Number((await axios.get(api + symbol)).data.price);
|
||||||
|
const tornInUsd = await fetchPrice("TORNBUSD");
|
||||||
|
const ethInUsd = await fetchPrice("ETHUSDT");
|
||||||
|
|
||||||
|
return BigInt(Math.floor(ethInUsd / tornInUsd));
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeData(relayerLossesInTorn: RelayerLosses) {
|
||||||
|
const data = Object.entries(relayerLossesInTorn)
|
||||||
|
.map(([relayer, amount]) => `${web3.utils.toChecksumAddress(relayer)} = ${amount.toString()} // ~ ${amount / 10n ** 18n} TORN`)
|
||||||
|
.join("\n");
|
||||||
|
fs.writeFileSync(path.join(".", "data", "relayerLosses.txt"), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const failedWithdrawals = await fetchFailedWithdrawals();
|
||||||
|
const relayerLosses = await calculateLosses(failedWithdrawals);
|
||||||
|
console.log(relayerLosses);
|
||||||
|
const ethRateInTorn = await getEthRateInTorn();
|
||||||
|
const relayerLossesInTorn = Object.fromEntries(
|
||||||
|
Object.entries(relayerLosses).map(([relayer, amounthInEth]) => [relayer, amounthInEth * ethRateInTorn])
|
||||||
|
);
|
||||||
|
writeData(relayerLossesInTorn);
|
||||||
|
console.log("All data about relayer losses written successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
4
scripts/types.ts
Normal file
4
scripts/types.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { Web3Eth } from "web3";
|
||||||
|
|
||||||
|
export type TxReceipt = Awaited<ReturnType<Web3Eth["getTransactionReceipt"]>>;
|
||||||
|
export type RelayerLosses = Record<string, bigint>;
|
@ -1,34 +1,31 @@
|
|||||||
type NodeVarObject = { [key: string]: string | number };
|
type NodeVarObject = { [key: string]: string };
|
||||||
|
type NodeVarArray = [string, string];
|
||||||
|
|
||||||
const solidityCodePadding = " ".repeat(8);
|
const solidityCodePadding = " ".repeat(8);
|
||||||
const pad = (decl: string, padding: string = solidityCodePadding) => padding + decl + "\n";
|
const pad = (decl: string, padding: string = solidityCodePadding) => padding + decl + "\n";
|
||||||
|
|
||||||
class DeclCalculator {
|
class DeclCalculator {
|
||||||
declType!: string | number;
|
|
||||||
padding!: string;
|
|
||||||
transformator!: Function;
|
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
declType: string | number,
|
private declType: string,
|
||||||
padding: string = solidityCodePadding,
|
private padding: string = solidityCodePadding,
|
||||||
transformator: Function = (
|
private transformator: Function = (
|
||||||
|
() => (x: any) =>
|
||||||
|
x
|
||||||
|
)(),
|
||||||
|
private variableNameChanger: Function = (
|
||||||
() => (x: any) =>
|
() => (x: any) =>
|
||||||
x
|
x
|
||||||
)()
|
)()
|
||||||
) {
|
) {}
|
||||||
this.declType = declType;
|
|
||||||
this.padding = padding || solidityCodePadding;
|
|
||||||
this.transformator = transformator;
|
|
||||||
}
|
|
||||||
|
|
||||||
private displayVariableName(varObj: NodeVarObject) {
|
private displayVariableName(varObj: NodeVarObject) {
|
||||||
return Object.keys(varObj)[0];
|
return Object.keys(varObj)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public calculateDecl = (varObj: NodeVarObject, type: string = "bytes32") => {
|
public calculateDecl = (varInfo: NodeVarObject | NodeVarArray, type: string = "bytes32") => {
|
||||||
const solidityVariableName = this.displayVariableName(varObj);
|
const solidityVariableName = this.variableNameChanger(Array.isArray(varInfo) ? varInfo[0] : this.displayVariableName(varInfo));
|
||||||
const solidityVariableValue = this.transformator(Object.values(varObj)[0]);
|
const solidityVariableValue = this.transformator(Array.isArray(varInfo) ? varInfo[1] : Object.values(varInfo)[0]);
|
||||||
const solidityDeclaration = `${this.declType || type} ${solidityVariableName} = ${solidityVariableValue.toString()};`;
|
const solidityDeclaration = `${this.declType || type} ${solidityVariableName} = ${solidityVariableValue};`;
|
||||||
|
|
||||||
return pad(solidityDeclaration, this.padding);
|
return pad(solidityDeclaration, this.padding);
|
||||||
};
|
};
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
/* Modules */
|
/* Modules */
|
||||||
"module": "NodeNext" /* Specify what module code is generated. */,
|
"module": "NodeNext" /* Specify what module code is generated. */,
|
||||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||||
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
"moduleResolution": "NodeNext" /* Specify how TypeScript looks up a file from a given module specifier. */,
|
||||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
|
Loading…
Reference in New Issue
Block a user