├── .editorconfig ├── .env.example ├── .eslintrc ├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── contracts ├── Aggregator.sol ├── GovernanceAggregator.sol ├── MinerAggregator.sol ├── PriceAggregator.sol └── SwapAggregator.sol ├── migrations └── 1_deploy_aggregator.js ├── package.json ├── scripts └── ganacheHelper.js ├── truffle.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | PRIVATE_KEY= 2 | INFURA_TOKEN= 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "browser": true, 5 | "es6": true, 6 | "mocha": true 7 | }, 8 | "extends": ["eslint:recommended", "plugin:prettier/recommended", "prettier"], 9 | "globals": { 10 | "Atomics": "readonly", 11 | "SharedArrayBuffer": "readonly" 12 | }, 13 | "parser": "babel-eslint", 14 | "parserOptions": { 15 | "ecmaVersion": 2018 16 | }, 17 | "rules": { 18 | "indent": ["error", 2], 19 | "linebreak-style": ["error", "unix"], 20 | "quotes": ["error", "single"], 21 | "semi": ["error", "never"], 22 | "object-curly-spacing": ["error", "always"], 23 | "comma-dangle": ["error", "always-multiline"], 24 | "require-await": "error", 25 | "prettier/prettier": ["error", { "printWidth": 110 }] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: ['*'] 6 | tags: ['v[0-9]+.[0-9]+.[0-9]+'] 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | - uses: actions/setup-node@v1 16 | with: 17 | node-version: 12 18 | - run: yarn install 19 | - run: yarn lint 20 | - name: Telegram Failure Notification 21 | uses: appleboy/telegram-action@0.0.7 22 | if: failure() 23 | with: 24 | message: ❗ Build failed for [${{ github.repository }}](https://github.com/${{ github.repository }}/actions) because of ${{ github.actor }} 25 | format: markdown 26 | to: ${{ secrets.TELEGRAM_CHAT_ID }} 27 | token: ${{ secrets.TELEGRAM_BOT_TOKEN }} 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .env 4 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 12 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .idea 3 | build 4 | scripts 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "bracketSpacing": true, 5 | "semi": false, 6 | "printWidth": 110, 7 | "overrides": [ 8 | { 9 | "files": "*.sol", 10 | "options": { 11 | "singleQuote": false, 12 | "printWidth": 130 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Truffle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tornado smart contract data aggregator [![Build Status](https://github.com/tornadocash/tornado-aggregator/workflows/build/badge.svg)](https://github.com/tornadocash/tornado-aggregator/actions) 2 | 3 | ## Dependencies 4 | 5 | 1. node 12 6 | 2. yarn 7 | 8 | ## Start 9 | 10 | ```bash 11 | $ yarn 12 | $ cp .env.example .env 13 | $ yarn test 14 | ``` 15 | 16 | ## Deploying 17 | 18 | Deploy to Kovan: 19 | 20 | ```bash 21 | $ yarn deploy:kovan 22 | ``` 23 | -------------------------------------------------------------------------------- /contracts/Aggregator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.12; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "./GovernanceAggregator.sol"; 7 | import "./PriceAggregator.sol"; 8 | import "./SwapAggregator.sol"; 9 | import "./MinerAggregator.sol"; 10 | import "torn-token/contracts/ENS.sol"; 11 | 12 | contract Aggregator is EnsResolve, GovernanceAggregator, PriceAggregator, SwapAggregator, MinerAggregator { 13 | function miningData( 14 | Miner miner, 15 | address[] calldata instances, 16 | RewardSwap swap 17 | ) 18 | external 19 | view 20 | returns ( 21 | uint256[] memory _rates, 22 | uint256 balance, 23 | uint256 poolWeight 24 | ) 25 | { 26 | _rates = minerRates(miner, instances); 27 | (balance, poolWeight) = swapState(swap); 28 | } 29 | 30 | function marketData( 31 | address[] calldata fromTokens, 32 | uint256[] calldata oneUnitAmounts, 33 | RewardSwap swap 34 | ) external view returns (uint256[] memory prices, uint256 balance) { 35 | prices = getPricesInETH(fromTokens, oneUnitAmounts); 36 | balance = swap.tornVirtualBalance(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/GovernanceAggregator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.12; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "tornado-governance/contracts/Governance.sol"; 7 | 8 | contract GovernanceAggregator { 9 | struct Proposal { 10 | address proposer; 11 | address target; 12 | uint256 startTime; 13 | uint256 endTime; 14 | uint256 forVotes; 15 | uint256 againstVotes; 16 | bool executed; 17 | bool extended; 18 | Governance.ProposalState state; 19 | } 20 | 21 | function getAllProposals(Governance governance) public view returns (Proposal[] memory proposals) { 22 | proposals = new Proposal[](governance.proposalCount()); 23 | 24 | for (uint256 i = 0; i < proposals.length; i++) { 25 | ( 26 | address proposer, 27 | address target, 28 | uint256 startTime, 29 | uint256 endTime, 30 | uint256 forVotes, 31 | uint256 againstVotes, 32 | bool executed, 33 | bool extended 34 | ) = governance.proposals(i + 1); 35 | 36 | proposals[i] = Proposal({ 37 | proposer: proposer, 38 | target: target, 39 | startTime: startTime, 40 | endTime: endTime, 41 | forVotes: forVotes, 42 | againstVotes: againstVotes, 43 | executed: executed, 44 | extended: extended, 45 | state: governance.state(i + 1) 46 | }); 47 | } 48 | } 49 | 50 | function getGovernanceBalances(Governance governance, address[] calldata accs) public view returns (uint256[] memory amounts) { 51 | amounts = new uint256[](accs.length); 52 | for (uint256 i = 0; i < accs.length; i++) { 53 | amounts[i] = governance.lockedBalance(accs[i]); 54 | } 55 | } 56 | 57 | function getUserData(Governance governance, address account) 58 | public 59 | view 60 | returns ( 61 | uint256 balance, 62 | uint256 latestProposalId, 63 | uint256 latestProposalIdState, 64 | uint256 timelock, 65 | address delegatee 66 | ) 67 | { 68 | // Core core = Core(address(governance)); 69 | balance = governance.lockedBalance(account); 70 | latestProposalId = governance.latestProposalIds(account); 71 | if (latestProposalId != 0) { 72 | latestProposalIdState = uint256(governance.state(latestProposalId)); 73 | } 74 | timelock = governance.canWithdrawAfter(account); 75 | delegatee = governance.delegatedTo(account); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /contracts/MinerAggregator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.12; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "tornado-anonymity-mining/contracts/Miner.sol"; 7 | 8 | contract MinerAggregator { 9 | function minerRates(Miner miner, address[] calldata instances) public view returns (uint256[] memory _rates) { 10 | _rates = new uint256[](instances.length); 11 | for (uint256 i = 0; i < _rates.length; i++) { 12 | _rates[i] = miner.rates(instances[i]); 13 | } 14 | } 15 | 16 | function areClaimedNotes(Miner miner, bytes32[] calldata _rewardNullifiers) external view returns (bool[] memory result) { 17 | result = new bool[](_rewardNullifiers.length); 18 | for (uint256 i = 0; i < _rewardNullifiers.length; i++) { 19 | result[i] = miner.rewardNullifiers(_rewardNullifiers[i]); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/PriceAggregator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.6.12; 4 | 5 | import "torn-token/contracts/ENS.sol"; 6 | 7 | interface OneSplit { 8 | function getExpectedReturn( 9 | address fromToken, 10 | address destToken, 11 | uint256 amount, 12 | uint256 parts, 13 | uint256 flags // See contants in IOneSplit.sol 14 | ) external view returns (uint256 returnAmount, uint256[] memory distribution); 15 | } 16 | 17 | contract PriceAggregator is EnsResolve { 18 | bytes32 nameHash = 0xabbae16ab822a7a0970b116c997c681cea9944854b55e1c441a9a788a2c6fc20; // 1split.eth - https://etherscan.io/enslookup?q=1split.eth 19 | 20 | function getPricesInETH(address[] memory fromTokens, uint256[] memory oneUnitAmounts) 21 | public 22 | view 23 | returns (uint256[] memory prices) 24 | { 25 | OneSplit split = OneSplit(resolve(nameHash)); 26 | 27 | prices = new uint256[](fromTokens.length); 28 | for (uint256 i = 0; i < fromTokens.length; i++) { 29 | (uint256 price, ) = split.getExpectedReturn( 30 | fromTokens[i], 31 | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, 32 | oneUnitAmounts[i], 33 | 1, 34 | 0 35 | ); 36 | prices[i] = price; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/SwapAggregator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.6.12; 4 | pragma experimental ABIEncoderV2; 5 | 6 | import "tornado-anonymity-mining/contracts/RewardSwap.sol"; 7 | 8 | contract SwapAggregator { 9 | function swapState(RewardSwap swap) public view returns (uint256 balance, uint256 poolWeight) { 10 | balance = swap.tornVirtualBalance(); 11 | poolWeight = swap.poolWeight(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /migrations/1_deploy_aggregator.js: -------------------------------------------------------------------------------- 1 | /* global artifacts */ 2 | const Aggregator = artifacts.require('Aggregator') 3 | 4 | module.exports = function (deployer) { 5 | return deployer.then(async () => { 6 | const aggregator = await deployer.deploy(Aggregator) 7 | 8 | console.log('Aggregator :', aggregator.address) 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tornado-aggregator", 3 | "version": "1.0.0", 4 | "description": "A template with all preferred configs", 5 | "main": "index.js", 6 | "scripts": { 7 | "compile": "yarn truffle compile", 8 | "test": "yarn truffle test", 9 | "test:stacktrace": "yarn test --stacktrace", 10 | "eslint": "eslint --ext .js --ignore-path .gitignore .", 11 | "prettier:check": "prettier --check . --config .prettierrc", 12 | "prettier:fix": "prettier --write . --config .prettierrc", 13 | "lint": "yarn eslint && yarn prettier:check", 14 | "deploy:mainnet": "truffle migrate --network mainnet", 15 | "deploy:kovan": "truffle migrate --network kovan", 16 | "deploy:goerli": "truffle migrate --network goerli", 17 | "deploy:dev": "truffle migrate --skip-dry-run --network development" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/peppersec/solidity-template.git" 22 | }, 23 | "author": "peppersec.com ", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/peppersec/solidity-template/issues" 27 | }, 28 | "homepage": "https://github.com/peppersec/solidity-template#readme", 29 | "devDependencies": { 30 | "@openzeppelin/contracts": "^3.1.0", 31 | "babel-eslint": "^10.1.0", 32 | "bn-chai": "^1.0.1", 33 | "chai": "^4.2.0", 34 | "chai-as-promised": "^7.1.1", 35 | "dotenv": "^8.2.0", 36 | "eslint": "^7.5.0", 37 | "eslint-config-prettier": "^6.11.0", 38 | "eslint-plugin-prettier": "^3.1.4", 39 | "prettier": "^2.1.1", 40 | "prettier-plugin-solidity": "^1.0.0-alpha.54", 41 | "solhint-plugin-prettier": "^0.0.4", 42 | "truffle": "^5.1.29", 43 | "truffle-flattener": "^1.4.4", 44 | "truffle-hdwallet-provider": "^1.0.17", 45 | "truffle-plugin-verify": "^0.3.11", 46 | "web3": "^1.2.11" 47 | }, 48 | "dependencies": { 49 | "tornado-anonymity-mining": "^1.0.0", 50 | "tornado-governance": "v1.0.0", 51 | "torn-token": "^1.0.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /scripts/ganacheHelper.js: -------------------------------------------------------------------------------- 1 | // This module is used only for tests 2 | function send(method, params = []) { 3 | return new Promise((resolve, reject) => { 4 | // eslint-disable-next-line no-undef 5 | web3.currentProvider.send( 6 | { 7 | jsonrpc: '2.0', 8 | id: Date.now(), 9 | method, 10 | params, 11 | }, 12 | (err, res) => { 13 | return err ? reject(err) : resolve(res) 14 | }, 15 | ) 16 | }) 17 | } 18 | 19 | const takeSnapshot = async () => { 20 | return await send('evm_snapshot') 21 | } 22 | 23 | const traceTransaction = async (tx) => { 24 | return await send('debug_traceTransaction', [tx, {}]) 25 | } 26 | 27 | const revertSnapshot = async (id) => { 28 | await send('evm_revert', [id]) 29 | } 30 | 31 | const mineBlock = async (timestamp) => { 32 | await send('evm_mine', [timestamp]) 33 | } 34 | 35 | const increaseTime = async (seconds) => { 36 | await send('evm_increaseTime', [seconds]) 37 | } 38 | 39 | const minerStop = async () => { 40 | await send('miner_stop', []) 41 | } 42 | 43 | const minerStart = async () => { 44 | await send('miner_start', []) 45 | } 46 | 47 | module.exports = { 48 | takeSnapshot, 49 | revertSnapshot, 50 | mineBlock, 51 | minerStop, 52 | minerStart, 53 | increaseTime, 54 | traceTransaction, 55 | } 56 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const HDWalletProvider = require('truffle-hdwallet-provider') 3 | const utils = require('web3-utils') 4 | const { PRIVATE_KEY, INFURA_TOKEN } = process.env 5 | 6 | module.exports = { 7 | // Uncommenting the defaults below 8 | // provides for an easier quick-start with Ganache. 9 | // You can also follow this format for other networks; 10 | // see 11 | // for more details on how to specify configuration options! 12 | // 13 | networks: { 14 | // development: { 15 | // host: '127.0.0.1', 16 | // port: 8545, 17 | // network_id: '*', 18 | // }, 19 | // test: { 20 | // host: "127.0.0.1", 21 | // port: 7545, 22 | // network_id: "*" 23 | // } 24 | mainnet: { 25 | provider: () => new HDWalletProvider(PRIVATE_KEY, `https://mainnet.infura.io/v3/${INFURA_TOKEN}`), 26 | network_id: 1, 27 | gas: 6000000, 28 | gasPrice: utils.toWei('100', 'gwei'), 29 | // confirmations: 0, 30 | // timeoutBlocks: 200, 31 | skipDryRun: true, 32 | }, 33 | kovan: { 34 | provider: () => new HDWalletProvider(PRIVATE_KEY, `https://kovan.infura.io/v3/${INFURA_TOKEN}`), 35 | network_id: 42, 36 | gas: 6000000, 37 | gasPrice: utils.toWei('1', 'gwei'), 38 | // confirmations: 0, 39 | // timeoutBlocks: 200, 40 | skipDryRun: true, 41 | }, 42 | goerli: { 43 | provider: () => new HDWalletProvider(PRIVATE_KEY, `https://goerli.infura.io/v3/${INFURA_TOKEN}`), 44 | network_id: 5, 45 | gas: 6000000, 46 | gasPrice: utils.toWei('1', 'gwei'), 47 | // confirmations: 0, 48 | // timeoutBlocks: 200, 49 | skipDryRun: true, 50 | }, 51 | coverage: { 52 | host: 'localhost', 53 | network_id: '*', 54 | port: 8554, // <-- If you change this, also set the port option in .solcover.js. 55 | gas: 0xfffffffffff, // <-- Use this high gas value 56 | gasPrice: 0x01, // <-- Use this low gas price 57 | }, 58 | }, 59 | compilers: { 60 | solc: { 61 | version: '0.6.12', 62 | settings: { 63 | optimizer: { 64 | enabled: true, 65 | runs: 200, 66 | }, 67 | }, 68 | }, 69 | }, 70 | plugins: ['truffle-plugin-verify', 'solidity-coverage'], 71 | } 72 | --------------------------------------------------------------------------------