first commit

This commit is contained in:
windknow 2023-11-19 13:51:07 +00:00
commit 35bb437ad9
15 changed files with 6591 additions and 0 deletions

14
.gitignore vendored Normal file

@ -0,0 +1,14 @@
node_modules
.env
# Hardhat files
/cache
/artifacts
# TypeChain files
/typechain
/typechain-types
# solidity-coverage files
/coverage
/coverage.json

7
README.md Normal file

@ -0,0 +1,7 @@
# Proposal 38
Compensating for relayers being incorrectly burned torn in oracle attacks.
Deploy: `npx hardhat run --network mainnet script/deploy.js`
Tests: `npx hardhat test`
Dont forget to fill env file

35
contracts/Proposal.sol Executable file

@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
// import { IGovernance } from "@interfaces/IGovernance.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract Proposal {
IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);
address public constant me = 0x9b78e16456c8C0A1FE386894c6f1E7F7C0104ADC;
struct RelayerLoss {
address relayer;
uint256 lostAmountInTorn;
}
function executeProposal() public {
// Data from 'data/relayerLosses.txt', generated by 'scripts/calculateLosses.ts'
RelayerLoss[6] memory relayerLosses = [
RelayerLoss(0x076D4E32C6A5D888fC4658281539c94E778C796d, 2_380_374_405_606_021_763_001),
RelayerLoss(0x28907F21F43B419F34226d6f10aCbCf1832b1D4d, 4_468_422_129_821_830_327_039),
RelayerLoss(0x2Ee39Ff05643bC7cc9ed31B71e142429044A425C, 3_040_216_359_787_595_395_455),
RelayerLoss(0xb0Cdc0AB2D454F2360d4629d519819E13DBE816A, 103_765_685_766_980_694_091),
RelayerLoss(0xb578603D3fB9216158c29488c1A902Dd0300c115, 816_848_044_379_099_935_494),
RelayerLoss(0xD8f1Eb586Ecb93745392EE254a028f1F67E1437E, 6_688_475_991_532_709_870_006)
];
for (uint256 i = 0; i < relayerLosses.length; i++) {
TORN.transfer(relayerLosses[i].relayer, relayerLosses[i].lostAmountInTorn);
}
// Deployment and execution cost
TORN.transfer(me, 50 ether);
}
}

6
data/relayerLosses.txt Executable file

@ -0,0 +1,6 @@
0x076D4E32C6A5D888fC4658281539c94E778C796d = 2380374405606021763001 // 2380.374405606021763001 TORN
0x28907F21F43B419F34226d6f10aCbCf1832b1D4d = 4468422129821830327039 // 4468.422129821830327039 TORN
0x2Ee39Ff05643bC7cc9ed31B71e142429044A425C = 3040216359787595395455 // 3040.216359787595395455 TORN
0xb0Cdc0AB2D454F2360d4629d519819E13DBE816A = 103765685766980694091 // 103.765685766980694091 TORN
0xb578603D3fB9216158c29488c1A902Dd0300c115 = 816848044379099935494 // 816.848044379099935494 TORN
0xD8f1Eb586Ecb93745392EE254a028f1F67E1437E = 6688475991532709870006 // 6688.475991532709870006 TORN

30
hardhat.config.ts Normal file

@ -0,0 +1,30 @@
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import dotenv from 'dotenv';
dotenv.config();
const config: HardhatUserConfig = {
solidity: "0.8.19",
mocha: {
timeout: 100000000,
},
networks: {
mainnet: {
url: "https://eth.llamarpc.com",
accounts: [process.env.REAL_PK!],
},
hardhat: {
forking: {
url: "https://eth.llamarpc.com",
enabled: true,
},
chainId: 1
}
},
etherscan: {
apiKey: process.env.ETHERSCAN_KEY,
},
};
export default config;

26
package.json Normal file

@ -0,0 +1,26 @@
{
"name": "proposal-38",
"license": "MIT",
"devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "^2.0.0",
"@nomicfoundation/hardhat-ethers": "^3.0.0",
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
"@nomicfoundation/hardhat-toolbox": "^4.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.0",
"@openzeppelin/contracts": "4.9.3",
"@typechain/ethers-v6": "^0.5.0",
"@typechain/hardhat": "^9.0.0",
"@types/chai": "^4.2.0",
"@types/mocha": ">=9.1.0",
"@types/node": ">=16.0.0",
"chai": "^4.2.0",
"dotenv": "^16.3.1",
"ethers": "^6.4.0",
"hardhat": "^2.19.1",
"hardhat-gas-reporter": "^1.0.8",
"solidity-coverage": "^0.8.0",
"ts-node": ">=8.0.0",
"typechain": "^8.3.0",
"typescript": ">=4.5.0"
}
}

@ -0,0 +1,719 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "_torn",
"type": "address"
},
{
"internalType": "address",
"name": "_governance",
"type": "address"
},
{
"internalType": "address",
"name": "_ens",
"type": "address"
},
{
"internalType": "address",
"name": "_staking",
"type": "address"
},
{
"internalType": "address",
"name": "_feeManager",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "minStakeAmount",
"type": "uint256"
}
],
"name": "MinimumStakeAmount",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "relayer",
"type": "address"
}
],
"name": "RelayerBalanceNullified",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bytes32",
"name": "relayer",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "string",
"name": "ensName",
"type": "string"
},
{
"indexed": false,
"internalType": "address",
"name": "relayerAddress",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "stakedAmount",
"type": "uint256"
}
],
"name": "RelayerRegistered",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "relayer",
"type": "address"
}
],
"name": "RelayerUnregistered",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "tornadoRouter",
"type": "address"
}
],
"name": "RouterRegistered",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amountStakeAdded",
"type": "uint256"
}
],
"name": "StakeAddedToRelayer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amountBurned",
"type": "uint256"
}
],
"name": "StakeBurned",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "worker",
"type": "address"
}
],
"name": "WorkerRegistered",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "worker",
"type": "address"
}
],
"name": "WorkerUnregistered",
"type": "event"
},
{
"inputs": [
{
"internalType": "bytes32[]",
"name": "domains",
"type": "bytes32[]"
}
],
"name": "bulkResolve",
"outputs": [
{
"internalType": "address[]",
"name": "result",
"type": "address[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"internalType": "contract ITornadoInstance",
"name": "pool",
"type": "address"
}
],
"name": "burn",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "ens",
"outputs": [
{
"internalType": "contract IENS",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feeManager",
"outputs": [
{
"internalType": "contract IFeeManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "relayer",
"type": "address"
}
],
"name": "getRelayerBalance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "relayer",
"type": "address"
}
],
"name": "getRelayerEnsHash",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "governance",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "_tornadoRouter",
"type": "bytes32"
}
],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "toResolve",
"type": "address"
}
],
"name": "isRelayer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"internalType": "address",
"name": "toResolve",
"type": "address"
}
],
"name": "isRelayerRegistered",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "minStakeAmount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "relayer",
"type": "address"
}
],
"name": "nullifyBalance",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "ensName",
"type": "string"
},
{
"internalType": "uint256",
"name": "stake",
"type": "uint256"
},
{
"internalType": "address[]",
"name": "workersToRegister",
"type": "address[]"
}
],
"name": "register",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "ensName",
"type": "string"
},
{
"internalType": "uint256",
"name": "stake",
"type": "uint256"
},
{
"internalType": "address[]",
"name": "workersToRegister",
"type": "address[]"
},
{
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "registerPermit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"internalType": "string",
"name": "ensName",
"type": "string"
},
{
"internalType": "uint256",
"name": "stake",
"type": "uint256"
}
],
"name": "registerRelayerAdmin",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"internalType": "address",
"name": "worker",
"type": "address"
}
],
"name": "registerWorker",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "relayers",
"outputs": [
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "ensHash",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
}
],
"name": "resolve",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "minAmount",
"type": "uint256"
}
],
"name": "setMinStakeAmount",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "tornadoRouterAddress",
"type": "address"
}
],
"name": "setTornadoRouter",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"internalType": "uint256",
"name": "stake",
"type": "uint256"
}
],
"name": "stakeToRelayer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"internalType": "uint256",
"name": "stake",
"type": "uint256"
},
{
"internalType": "address",
"name": "staker",
"type": "address"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "stakeToRelayerPermit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "staking",
"outputs": [
{
"internalType": "contract ITornadoStakingRewards",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "torn",
"outputs": [
{
"internalType": "contract TORN",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tornadoRouter",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "relayer",
"type": "address"
}
],
"name": "unregisterRelayer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "worker",
"type": "address"
}
],
"name": "unregisterWorker",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "workers",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]

@ -0,0 +1,519 @@
[
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "_newOperator",
"type": "address"
}
],
"name": "changeOperator",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "nullifierHashes",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes",
"name": "_proof",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "_root",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "_nullifierHash",
"type": "bytes32"
},
{
"internalType": "address payable",
"name": "_recipient",
"type": "address"
},
{
"internalType": "address payable",
"name": "_relayer",
"type": "address"
},
{
"internalType": "uint256",
"name": "_fee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_refund",
"type": "uint256"
}
],
"name": "withdraw",
"outputs": [],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "verifier",
"outputs": [
{
"internalType": "contract IVerifier",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "_left",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "_right",
"type": "bytes32"
}
],
"name": "hashLeftRight",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "pure",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "FIELD_SIZE",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "levels",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "operator",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "_root",
"type": "bytes32"
}
],
"name": "isKnownRoot",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "commitments",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "denomination",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "currentRootIndex",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "_newVerifier",
"type": "address"
}
],
"name": "updateVerifier",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32[]",
"name": "_nullifierHashes",
"type": "bytes32[]"
}
],
"name": "isSpentArray",
"outputs": [
{
"internalType": "bool[]",
"name": "spent",
"type": "bool[]"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes32",
"name": "_commitment",
"type": "bytes32"
}
],
"name": "deposit",
"outputs": [],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getLastRoot",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "roots",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "ROOT_HISTORY_SIZE",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "_nullifierHash",
"type": "bytes32"
}
],
"name": "isSpent",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "zeros",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "ZERO_VALUE",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "filledSubtrees",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "nextIndex",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract IVerifier",
"name": "_verifier",
"type": "address"
},
{
"internalType": "uint256",
"name": "_denomination",
"type": "uint256"
},
{
"internalType": "uint32",
"name": "_merkleTreeHeight",
"type": "uint32"
},
{
"internalType": "address",
"name": "_operator",
"type": "address"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "commitment",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "uint32",
"name": "leafIndex",
"type": "uint32"
},
{
"indexed": false,
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"name": "Deposit",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "nullifierHash",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "fee",
"type": "uint256"
}
],
"name": "Withdrawal",
"type": "event"
}
]

100
scripts/calculateLosses.ts Executable file

@ -0,0 +1,100 @@
import { ethers } from 'ethers';
import relayerAbi from './abi/RelayerRegistry.abi.json';
import tornethAbi from './abi/TornadoCash_eth.abi.json';
const RPC = 'https://eth.llamarpc.com';
// Affected TORN instance
const tornAddrs = [
// ETH
{
// 1
addr: '0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936',
from: 18577000,
to: 18583515,
},
{
// 10
addr: '0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF',
from: 18577000,
to: 18583515,
},
{
// 100
addr: '0xA160cdAB225685dA1d56aa342Ad8841c3b53f291',
from: 18577000,
to: 18583515,
},
// DAI
{
// 10000
addr: '0x07687e702b410Fa43f4cB4Af7FA097918ffD2730',
from: 18577000,
to: 18591849,
},
];
const relayerAddr = '0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2';
const relayerFrom = 18577000;
const relayerTo = 18591849;
type BurnInfo = {
relayer: string;
toBurn: bigint;
};
async function getBurnInfos() {
const p = new ethers.JsonRpcProvider(RPC);
const cont = new ethers.Contract(relayerAddr, relayerAbi, p);
const filter = cont.filters.StakeBurned();
let ret: { [key: string]: BurnInfo } = {};
const events = await cont.queryFilter(filter, relayerFrom, relayerTo);
for (const e of events) {
// decode StakeBurned data
const [ relayer, toBurn ] = ethers.AbiCoder.defaultAbiCoder().decode(
['address', 'uint256'],
e.data
);
ret[e.transactionHash] = {
relayer,
toBurn,
};
}
await p.destroy();
return ret;
}
async function main() {
const burnInfo = await getBurnInfos();
let relayerLost: { [key: string]: bigint } = {};
await Promise.all(tornAddrs.map(async ({ addr, from, to }) => {
const p = new ethers.JsonRpcProvider(RPC);
const cont = new ethers.Contract(addr, tornethAbi, p);
const filter = cont.filters.Withdrawal();
const events = await cont.queryFilter(filter, from, to);
for (const e of events) {
const info = burnInfo[e.transactionHash];
if (!info) continue;
if (!(info.relayer in relayerLost)) {
relayerLost[info.relayer] = BigInt(0);
}
relayerLost[info.relayer] += info.toBurn;
}
await p.destroy();
}));
for (const key in relayerLost) {
console.log(`${key} = ${relayerLost[key]} // ${ethers.formatEther(relayerLost[key])} TORN`);
}
}
main().catch(console.error);

15
scripts/deploy.ts Normal file

@ -0,0 +1,15 @@
import { ethers } from "hardhat";
async function main() {
const proposal = await ethers.deployContract("Proposal", []);
await proposal.waitForDeployment();
console.log(`Proposal deployed to ${proposal.target}`);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

41
test/Proposal.ts Normal file

@ -0,0 +1,41 @@
import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers";
import { ethers } from "hardhat";
import { expect } from "chai";
import { deployAndExecuteProposal } from "./utils";
const tornAddr = "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C";
const myAddr = "0x9b78e16456c8C0A1FE386894c6f1E7F7C0104ADC";
describe("Proposal", function () {
describe("Deployment", async function () {
it("Relayer Got Compensation", async function () {
const TORN = await ethers.getContractAt("IERC20", tornAddr);
// Data from 'data/relayerLosses.txt', generated by 'scripts/calculateLosses.ts'
const relayerLosses: { [key: string]: bigint } = {
'0x076D4E32C6A5D888fC4658281539c94E778C796d': BigInt('2380374405606021763001'), // 2380.374405606021763001 TORN
'0x28907F21F43B419F34226d6f10aCbCf1832b1D4d': BigInt('4468422129821830327039'), // 4468.422129821830327039 TORN
'0x2Ee39Ff05643bC7cc9ed31B71e142429044A425C': BigInt('3040216359787595395455'), // 3040.216359787595395455 TORN
'0xb0Cdc0AB2D454F2360d4629d519819E13DBE816A': BigInt('103765685766980694091'), // 103.765685766980694091 TORN
'0xb578603D3fB9216158c29488c1A902Dd0300c115': BigInt('816848044379099935494'), // 816.848044379099935494 TORN
'0xD8f1Eb586Ecb93745392EE254a028f1F67E1437E': BigInt('6688475991532709870006') // 6688.475991532709870006 TORN
}
let relayerBalance: { [key: string]: bigint } = {};
for (const addr in relayerLosses) {
relayerBalance[addr] = await TORN.balanceOf(addr);
}
const myBalance = await TORN.balanceOf(myAddr);
await loadFixture(deployAndExecuteProposal);
for (const addr in relayerBalance) {
const balance = await TORN.balanceOf(addr);
expect(balance).to.equal(relayerBalance[addr] + relayerLosses[addr]);
}
expect(await TORN.balanceOf(myAddr)).to.equal(myBalance + BigInt(5e19));
});
});
})

1003
test/abi/governance.abi.json Normal file

File diff suppressed because it is too large Load Diff

33
test/utils.ts Normal file

@ -0,0 +1,33 @@
import { ethers, network } from "hardhat";
import { time } from "@nomicfoundation/hardhat-toolbox/network-helpers";
import governanceAbi from "./abi/governance.abi.json";
const governanceAddr = "0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce";
async function getManyEth(addr: string) {
await network.provider.send("hardhat_setBalance", [addr, "0x111166630153555558483537"]);
}
async function deployAndExecuteProposal() {
const proposal = await ethers.deployContract("Proposal", []);
await proposal.waitForDeployment();
const bigStakerAddr = "0xE4143f6377AEcd7193b9731d1C28815b57C4f5Ab";
await getManyEth(bigStakerAddr);
const stakerSigner = await ethers.getImpersonatedSigner(bigStakerAddr);
const governanceContract = await ethers.getContractAt(
governanceAbi,
governanceAddr,
stakerSigner,
);
await governanceContract.propose(proposal.target, "");
const proposalId = await governanceContract.proposalCount();
await time.increase(60 * 60);
await governanceContract.castVote(proposalId, true);
await time.increase(60 * 60 * 24 * 7 + 60);
await governanceContract.execute(proposalId);
}
module.exports = {
deployAndExecuteProposal,
};

11
tsconfig.json Normal file

@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true
}
}

4032
yarn.lock Normal file

File diff suppressed because it is too large Load Diff