This commit is contained in:
poma 2020-12-15 18:10:11 +03:00
commit 4c561efb47
No known key found for this signature in database
GPG Key ID: BA20CB01FE165657
17 changed files with 5878 additions and 0 deletions

7
.env.example Normal file
View File

@ -0,0 +1,7 @@
DEPLOYER=0xce0042b868300000d44a59004da54a005ffdcf9f
SALT=0x00000000000000000000000000000000000000000000000000000000000325a5
PRIVATE_KEY=
RPC_URL=
NET_ID=42
AIRDROP_CHUNK_SIZE=200
GAS_PRICE_IN_WEI=123000000000

26
.eslintrc Normal file
View File

@ -0,0 +1,26 @@
{
"env": {
"node": true,
"browser": true,
"es6": true,
"mocha": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"indent": ["error", 2],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "single"],
"semi": ["error", "never"],
"object-curly-spacing": ["error", "always"],
"comma-dangle": ["error", "always-multiline"],
"require-await": "error"
}
}

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules
actions.json
airdrop.json
.env

20
.gitmodules vendored Normal file
View File

@ -0,0 +1,20 @@
[submodule "tornado-anonymity-mining"]
path = tornado-anonymity-mining
url = https://github.com/tornadocash/tornado-anonymity-mining
branch = master
[submodule "governance"]
path = governance
url = https://github.com/tornadocash/governance
branch = master
[submodule "torn-token"]
path = torn-token
url = https://github.com/tornadocash/torn-token
branch = master
[submodule "airdrop"]
path = airdrop
url = https://github.com/tornadocash/airdrop
branch = master
[submodule "deployer"]
path = deployer
url = https://github.com/tornadocash/deployer
branch = master

7
.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"semi": false,
"printWidth": 110
}

36
README.md Normal file
View File

@ -0,0 +1,36 @@
## Dependencies
1. node 12
2. yarn
3. zkutil (`brew install rust && cargo install zkutil`)
## Usage
```
git clone --recursive https://github.com/tornadocash/tornado-deploy
cd tornado-deploy
npm i
cp .env.example .env
npm run start
```
Note: build script will globally `yarn link` `torn-token` package
Note: build script will not recompile snark circuit if compilation result already exists
## How to use local npm packages:
```
# Remember that torn-token packages links to here
cd torn-token
yarn link
# Install torn-token package from local source (create symlink ./node_modules/torn-token -> <remembered torn-token path>)
cd ../governance
yarn link torn-token
```
## Verify addresses
```
cat actions.json | jq '.actions[] | {domain,expectedAddress,contract} '
```

26
abi/deployer.abi.json Normal file
View File

@ -0,0 +1,26 @@
[
{
"inputs": [
{
"internalType": "bytes",
"name": "_initCode",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "_salt",
"type": "bytes32"
}
],
"name": "deploy",
"outputs": [
{
"internalType": "address payable",
"name": "createdContract",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]

403
abi/ens.abi.json Normal file
View File

@ -0,0 +1,403 @@
[
{
"inputs": [{ "internalType": "contract ENS", "name": "_ens", "type": "address" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "indexed": true, "internalType": "uint256", "name": "contentType", "type": "uint256" }
],
"name": "ABIChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "indexed": false, "internalType": "address", "name": "a", "type": "address" }
],
"name": "AddrChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "indexed": false, "internalType": "uint256", "name": "coinType", "type": "uint256" },
{ "indexed": false, "internalType": "bytes", "name": "newAddress", "type": "bytes" }
],
"name": "AddressChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "indexed": true, "internalType": "address", "name": "owner", "type": "address" },
{ "indexed": true, "internalType": "address", "name": "target", "type": "address" },
{ "indexed": false, "internalType": "bool", "name": "isAuthorised", "type": "bool" }
],
"name": "AuthorisationChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "indexed": false, "internalType": "bytes", "name": "hash", "type": "bytes" }
],
"name": "ContenthashChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "indexed": false, "internalType": "bytes", "name": "name", "type": "bytes" },
{ "indexed": false, "internalType": "uint16", "name": "resource", "type": "uint16" },
{ "indexed": false, "internalType": "bytes", "name": "record", "type": "bytes" }
],
"name": "DNSRecordChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "indexed": false, "internalType": "bytes", "name": "name", "type": "bytes" },
{ "indexed": false, "internalType": "uint16", "name": "resource", "type": "uint16" }
],
"name": "DNSRecordDeleted",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": true, "internalType": "bytes32", "name": "node", "type": "bytes32" }],
"name": "DNSZoneCleared",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "indexed": true, "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" },
{ "indexed": false, "internalType": "address", "name": "implementer", "type": "address" }
],
"name": "InterfaceChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "indexed": false, "internalType": "string", "name": "name", "type": "string" }
],
"name": "NameChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "indexed": false, "internalType": "bytes32", "name": "x", "type": "bytes32" },
{ "indexed": false, "internalType": "bytes32", "name": "y", "type": "bytes32" }
],
"name": "PubkeyChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "indexed": true, "internalType": "string", "name": "indexedKey", "type": "string" },
{ "indexed": false, "internalType": "string", "name": "key", "type": "string" }
],
"name": "TextChanged",
"type": "event"
},
{
"constant": true,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "uint256", "name": "contentTypes", "type": "uint256" }
],
"name": "ABI",
"outputs": [
{ "internalType": "uint256", "name": "", "type": "uint256" },
{ "internalType": "bytes", "name": "", "type": "bytes" }
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [{ "internalType": "bytes32", "name": "node", "type": "bytes32" }],
"name": "addr",
"outputs": [{ "internalType": "address payable", "name": "", "type": "address" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "uint256", "name": "coinType", "type": "uint256" }
],
"name": "addr",
"outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "internalType": "bytes32", "name": "", "type": "bytes32" },
{ "internalType": "address", "name": "", "type": "address" },
{ "internalType": "address", "name": "", "type": "address" }
],
"name": "authorisations",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [{ "internalType": "bytes32", "name": "node", "type": "bytes32" }],
"name": "clearDNSZone",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [{ "internalType": "bytes32", "name": "node", "type": "bytes32" }],
"name": "contenthash",
"outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "bytes32", "name": "name", "type": "bytes32" },
{ "internalType": "uint16", "name": "resource", "type": "uint16" }
],
"name": "dnsRecord",
"outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "bytes32", "name": "name", "type": "bytes32" }
],
"name": "hasDNSRecords",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
],
"name": "interfaceImplementer",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [{ "internalType": "bytes[]", "name": "data", "type": "bytes[]" }],
"name": "multicall",
"outputs": [{ "internalType": "bytes[]", "name": "results", "type": "bytes[]" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [{ "internalType": "bytes32", "name": "node", "type": "bytes32" }],
"name": "name",
"outputs": [{ "internalType": "string", "name": "", "type": "string" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [{ "internalType": "bytes32", "name": "node", "type": "bytes32" }],
"name": "pubkey",
"outputs": [
{ "internalType": "bytes32", "name": "x", "type": "bytes32" },
{ "internalType": "bytes32", "name": "y", "type": "bytes32" }
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "uint256", "name": "contentType", "type": "uint256" },
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"name": "setABI",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "uint256", "name": "coinType", "type": "uint256" },
{ "internalType": "bytes", "name": "a", "type": "bytes" }
],
"name": "setAddr",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "address", "name": "a", "type": "address" }
],
"name": "setAddr",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "address", "name": "target", "type": "address" },
{ "internalType": "bool", "name": "isAuthorised", "type": "bool" }
],
"name": "setAuthorisation",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "bytes", "name": "hash", "type": "bytes" }
],
"name": "setContenthash",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"name": "setDNSRecords",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" },
{ "internalType": "address", "name": "implementer", "type": "address" }
],
"name": "setInterface",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "string", "name": "name", "type": "string" }
],
"name": "setName",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "bytes32", "name": "x", "type": "bytes32" },
{ "internalType": "bytes32", "name": "y", "type": "bytes32" }
],
"name": "setPubkey",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "string", "name": "key", "type": "string" },
{ "internalType": "string", "name": "value", "type": "string" }
],
"name": "setText",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [{ "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }],
"name": "supportsInterface",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "pure",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "internalType": "bytes32", "name": "node", "type": "bytes32" },
{ "internalType": "string", "name": "key", "type": "string" }
],
"name": "text",
"outputs": [{ "internalType": "string", "name": "", "type": "string" }],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]

615
abi/erc20.abi.json Normal file
View File

@ -0,0 +1,615 @@
[
{
"inputs": [
{
"internalType": "bytes32",
"name": "_governance",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "_pausePeriod",
"type": "uint256"
},
{
"components": [
{
"internalType": "bytes32",
"name": "to",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"internalType": "struct TORN.Recipient[]",
"name": "_vestings",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "target",
"type": "address"
}
],
"name": "Allowed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "target",
"type": "address"
}
],
"name": "Disallowed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "Paused",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "Unpaused",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "allowedTransferee",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "blockTimestamp",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "burn",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "burnFrom",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "canUnpauseAfter",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "chainID",
"outputs": [
{
"internalType": "uint256",
"name": "_chainID",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "subtractedValue",
"type": "uint256"
}
],
"name": "decreaseAllowance",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "governance",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "addedValue",
"type": "uint256"
}
],
"name": "increaseAllowance",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "nonces",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "paused",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"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": "permit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
}
],
"name": "resolve",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "decision",
"type": "bool"
}
],
"name": "changeTransferability",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "target",
"type": "address[]"
}
],
"name": "addToAllowedList",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "target",
"type": "address[]"
}
],
"name": "removeFromAllowedList",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract IERC20",
"name": "_token",
"type": "address"
},
{
"internalType": "address payable",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_balance",
"type": "uint256"
}
],
"name": "rescueTokens",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

28
build.sh Executable file
View File

@ -0,0 +1,28 @@
#!/bin/bash -e
# expecting node v12
cd deployer
yarn
yarn compile
cd ..
cd torn-token
yarn link
yarn
yarn compile
cd ..
cd governance
yarn link torn-token
yarn
yarn compile
cd ..
cd tornado-anonymity-mining
yarn link torn-token
yarn
if [[ ! -f "build/circuits/treeUpdateVerifier.sol" ]]; then
yarn circuit
fi
yarn compile
cd ..

36
package.json Normal file
View File

@ -0,0 +1,36 @@
{
"name": "tornado-deploy",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"clean": "rm -rf torn-token/build && rm -rf tornado-anonymity-mining/build/contracts && rm -rf governance/build",
"generate": "node src/generate.js",
"airdrop": "node src/airdrop.js",
"ens": "node src/ens.js",
"execute": "node src/execute.js",
"build": "./build.sh && yarn airdrop && yarn generate",
"deploy": "yarn build && yarn ens && yarn execute",
"eslint": "eslint --ext .js --ignore-path .gitignore src",
"prettier:check": "prettier --check src --config .prettierrc",
"prettier:fix": "prettier --write src --config .prettierrc",
"lint": "yarn eslint && yarn prettier:check"
},
"keywords": [],
"author": "Roman Semenov <semenov.roma@gmail.com>",
"license": "ISC",
"dependencies": {
"@ethersproject/address": "^5.0.4",
"@ethersproject/solidity": "^5.0.4",
"circomlib": "git+https://github.com/tornadocash/circomlib.git#3b492f9801573eebcfe1b6c584afe8a3beecf2b4",
"dotenv": "^8.2.0",
"ethers": "^5.0.14",
"fixed-merkle-tree": "^0.5.0",
"get-value": "^3.0.1"
},
"devDependencies": {
"babel-eslint": "^10.1.0",
"eslint": "^7.12.1",
"prettier": "^2.1.2"
}
}

59
src/airdrop.js Normal file
View File

@ -0,0 +1,59 @@
require('dotenv').config()
const fs = require('fs')
const ethers = require('ethers')
const { namehash, parseEther, formatEther } = ethers.utils
const config = require('../torn-token/config')
const { deploy, getContractData } = require('./utils')
const DEPLOYER = process.env.DEPLOYER
const SALT = process.env.SALT
const AIRDROP_CHUNK_SIZE = process.env.AIRDROP_CHUNK_SIZE
const airdrop = getContractData('../torn-token/build/contracts/Airdrop.json')
const actions = []
const list = fs
.readFileSync('./airdrop/airdrop.csv')
.toString()
.split('\n')
.map((a) => a.split(','))
.filter((a) => a.length === 2)
.map((a) => ({ to: a[0], amount: ethers.BigNumber.from(a[1]) }))
const total = list.reduce((acc, a) => acc.add(a.amount), ethers.BigNumber.from(0))
const expectedAirdrop = ethers.BigNumber.from(config.torn.distribution.airdrop.amount)
if (total.gt(expectedAirdrop)) {
console.log(
`Total airdrop amount ${formatEther(total)} is greater than expected ${formatEther(expectedAirdrop)}`,
)
process.exit(1)
}
console.log('Airdrop amount:', formatEther(total))
console.log('Airdrop expected:', formatEther(expectedAirdrop))
let i = 0
while (list.length) {
i++
const chunk = list.splice(0, AIRDROP_CHUNK_SIZE)
const total = chunk.reduce((acc, a) => acc.add(a.amount), ethers.BigNumber.from(0))
actions.push(
deploy({
amount: total.toString(),
contract: airdrop,
args: [namehash(config.voucher.address), chunk],
dependsOn: [config.deployer.address, config.voucher.address],
title: `Airdrop Voucher ${i}`,
description: 'Early adopters voucher coupons',
}),
)
}
// Write output
const result = {
deployer: DEPLOYER,
salt: SALT,
actions,
}
fs.writeFileSync('airdrop.json', JSON.stringify(result, null, ' '))
console.log('Created airdrop.json')

44
src/ens.js Normal file
View File

@ -0,0 +1,44 @@
require('dotenv').config()
const ethers = require('ethers')
const { namehash } = ethers.utils
const { actions } = require('../actions.json')
const abi = require('../abi/ens.abi.json')
const prefix = {
1: '',
42: 'kovan.',
5: 'goerli.',
}
const gasPrice = process.env.GAS_PRICE_IN_WEI || '123000000000' // 123 gwei
const explorer = `https://${prefix[process.env.NET_ID]}etherscan.io`
async function main() {
const privateKey = process.env.PRIVATE_KEY
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL)
const wallet = new ethers.Wallet(privateKey, provider)
const resolver =
process.env.NET_ID === '1'
? new ethers.Contract('0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41', abi, wallet) // public resolver from mainnet with multicall support
: new ethers.Contract('0x8595bFb0D940DfEDC98943FA8a907091203f25EE', abi, wallet) // our kovan mock
const data = []
for (let { domain, expectedAddress, contract } of actions) {
if (!domain && contract === 'Airdrop.sol') {
continue
}
const hash = namehash(domain)
console.log(`Setting ${expectedAddress} address for ${hash} - ${domain}`)
const calldata = resolver.interface.encodeFunctionData('setAddr(bytes32,address)', [
hash,
expectedAddress,
])
data.push(calldata)
}
const tx = await resolver.multicall(data, { gasPrice })
console.log(`\n${explorer}/tx/${tx.hash}`)
await tx.wait()
console.log('Complete')
}
main()

69
src/execute.js Normal file
View File

@ -0,0 +1,69 @@
require('dotenv').config()
const ethers = require('ethers')
const actions = require('../actions.json')
const abi = require('../abi/deployer.abi.json')
const erc20 = require('../abi/erc20.abi.json')
const { formatEther } = ethers.utils
const prefix = {
1: '',
42: 'kovan.',
5: 'goerli.',
}
const explorer = `https://${prefix[process.env.NET_ID]}etherscan.io`
async function main() {
const privateKey = process.env.PRIVATE_KEY
const provider = new ethers.providers.JsonRpcProvider(process.env.RPC_URL)
const wallet = new ethers.Wallet(privateKey, provider)
const deployer = new ethers.Contract(actions.deployer, abi, wallet)
const deployerProxy = new ethers.Contract(actions.actions[0].expectedAddress, abi, wallet)
for (const action of actions.actions.filter((a) => a.contract !== 'Airdrop.sol')) {
let code = await provider.getCode(action.expectedAddress)
if (code && code !== '0x') {
console.log(`${action.contract} is already deployed`)
continue
}
console.log(`Deploying ${action.contract} to ${action.domain} (${action.expectedAddress})`)
const dep = action === actions.actions[0] ? deployer : deployerProxy
const tx = await dep.deploy(action.bytecode, actions.salt, { gasLimit: 7e6, gasPrice: 20000000000 })
console.log(`TX hash ${explorer}/tx/${tx.hash}`)
try {
await tx.wait()
console.log(`Deployed ${action.contract} to ${explorer}/address/${action.expectedAddress}\n`)
} catch (e) {
console.error(`Failed to deploy ${action.contract}, sending debug tx`)
const tx = await wallet.sendTransaction({ gasLimit: 8e6, data: action.bytecode })
console.log(`TX hash ${explorer}/tx/${tx.hash}`)
await tx.wait()
console.log('Mined, check revert reason on etherscan')
return
// throw new Error(`Failed to deploy ${action.contract}`)
}
}
const voucher = new ethers.Contract(
actions.actions.find((a) => a.contract === 'Voucher.sol').expectedAddress,
erc20,
wallet,
)
for (const action of actions.actions.filter((a) => a.contract === 'Airdrop.sol')) {
let bal = await voucher.balanceOf(action.expectedAddress)
if (bal.eq(0)) {
console.log('This airdrop was already processed, skipping')
continue
}
console.log(`Airdropping ${formatEther(action.amount)} vouchers`)
const tx = await deployerProxy.deploy(action.bytecode, actions.salt, {
gasLimit: 7e6,
gasPrice: 20000000000,
})
console.log(`TX hash ${explorer}/tx/${tx.hash}`)
await tx.wait()
console.log('Airdropped successfully')
}
}
main()

246
src/generate.js Normal file
View File

@ -0,0 +1,246 @@
require('dotenv').config()
const fs = require('fs')
const ethers = require('ethers')
const { namehash } = ethers.utils
const config = require('../torn-token/config')
const get = require('get-value')
const { deploy, getContractData, zeroMerkleRoot } = require('./utils')
const { DEPLOYER, SALT } = process.env
const deployer = getContractData('../deployer/build/contracts/Deployer.json')
const torn = getContractData('../torn-token/build/contracts/TORN.json')
const vesting = getContractData('../torn-token/build/contracts/Vesting.json')
const voucher = getContractData('../torn-token/build/contracts/Voucher.json')
const governance = getContractData('../governance/build/contracts/Governance.json')
const governanceProxy = getContractData('../governance/build/contracts/LoopbackProxy.json')
const miner = getContractData('../tornado-anonymity-mining/build/contracts/Miner.json')
const rewardSwap = getContractData('../tornado-anonymity-mining/build/contracts/RewardSwap.json')
const tornadoTrees = getContractData('../tornado-anonymity-mining/build/contracts/TornadoTrees.json')
const tornadoProxy = getContractData('../tornado-anonymity-mining/build/contracts/TornadoProxy.json')
const poseidonHasher2 = getContractData('../tornado-anonymity-mining/build/contracts/Hasher2.json')
const poseidonHasher3 = getContractData('../tornado-anonymity-mining/build/contracts/Hasher3.json')
const rewardVerifier = getContractData('../tornado-anonymity-mining/build/contracts/RewardVerifier.json')
const withdrawVerifier = getContractData('../tornado-anonymity-mining/build/contracts/WithdrawVerifier.json')
const treeUpdateVerifier = getContractData(
'../tornado-anonymity-mining/build/contracts/TreeUpdateVerifier.json',
)
const airdrop = require('../airdrop.json')
const { domain } = require('process')
const actions = []
actions.push(
deploy({
domain: config.deployer.address,
contract: deployer,
args: ['0x0000000000000000000000000000000000000000'],
dependsOn: [],
title: 'Deployment proxy',
description:
'This a required contract to initialize all other contracts. It is simple wrapper around EIP-2470 Singleton Factory that emits an event of contract deployment. The wrapper also validates if the deployment was successful.',
}),
)
// Deploy TORN
const distribution = Object.values(config.torn.distribution).map(({ to, amount }) => ({
to: namehash(get(config, to).address),
amount,
}))
console.log(distribution)
actions.push(
deploy({
domain: config.torn.address,
contract: torn,
args: [namehash(config.governance.address), config.torn.pausePeriod, distribution],
title: 'TORN token',
description: 'Tornado.cash governance token',
}),
)
// Deploy Governance implementation
actions.push(
deploy({
domain: config.governanceImpl.address,
contract: governance,
title: 'Governance implementation',
description: 'Initial implementation of upgradable governance contract',
}),
)
// Deploy Governance proxy
const governanceContract = new ethers.utils.Interface(governance.abi)
const initData = governanceContract.encodeFunctionData('initialize', [namehash(config.torn.address)])
actions.push(
deploy({
domain: config.governance.address,
contract: governanceProxy,
args: [namehash(config.governanceImpl.address), initData],
dependsOn: [config.deployer.address, config.governanceImpl.address],
title: 'Governance Upgradable Proxy',
description:
'EIP-1167 Upgradable Proxy for Governance. It can only be upgraded through a proposal by TORN holders',
}),
)
// Deploy Verifiers
actions.push(
deploy({
domain: config.rewardVerifier.address,
contract: rewardVerifier,
title: 'Reward Verifier',
description: 'ZkSnark verifier smart contract for mining rewards',
}),
)
actions.push(
deploy({
domain: config.withdrawVerifier.address,
contract: withdrawVerifier,
title: 'Withdraw Verifier',
description: 'ZkSnark verifier smart contract for reward withdrawals',
}),
)
actions.push(
deploy({
domain: config.treeUpdateVerifier.address,
contract: treeUpdateVerifier,
title: 'Tree Update Verifier',
description: 'ZkSnark verifier smart contract for validation for account merkle tree updates',
}),
)
// Deploy RewardSwap
actions.push(
deploy({
domain: config.rewardSwap.address,
contract: rewardSwap,
args: [
namehash(config.torn.address),
namehash(config.miningV2.address),
config.torn.distribution.miningV2.amount,
config.miningV2.initialBalance,
config.rewardSwap.poolWeight,
],
title: 'Reward Swap',
description: 'AMM that allows to swap Anonymity Points to TORN',
}),
)
// Deploy PoseidonHasher2
actions.push(
deploy({
domain: config.poseidonHasher2.address,
contract: poseidonHasher2,
title: 'Poseidon hasher 2',
description: 'Poseidon hash function for 2 arguments',
}),
)
// Deploy PoseidonHasher3
actions.push(
deploy({
domain: config.poseidonHasher3.address,
contract: poseidonHasher3,
title: 'Poseidon hasher 3',
description: 'Poseidon hash function for 3 arguments',
}),
)
// Deploy TornadoProxy
const instances = config.miningV2.rates.map((rate) => namehash(rate.instance))
actions.push(
deploy({
domain: config.tornadoProxy.address,
contract: tornadoProxy,
args: [namehash(config.tornadoTrees.address), namehash(config.governance.address), instances],
title: 'TornadoCash Proxy',
description:
'Proxy contract for tornado.cash deposits and withdrawals that records block numbers for mining',
}),
)
// Deploy TornadoTrees
actions.push(
deploy({
domain: config.tornadoTrees.address,
contract: tornadoTrees,
args: [
namehash(config.tornadoProxy.address),
namehash(config.poseidonHasher2.address),
namehash(config.poseidonHasher3.address),
config.tornadoTrees.levels,
],
title: 'TornadoTrees',
description: 'Merkle tree with information about tornado cash deposits and withdrawals',
}),
)
// Deploy Miner
const rates = config.miningV2.rates.map((rate) => ({
instance: namehash(rate.instance),
value: rate.value,
}))
actions.push(
deploy({
domain: config.miningV2.address,
contract: miner,
args: [
namehash(config.rewardSwap.address),
namehash(config.governance.address),
namehash(config.tornadoTrees.address),
[
namehash(config.rewardVerifier.address),
namehash(config.withdrawVerifier.address),
namehash(config.treeUpdateVerifier.address),
],
zeroMerkleRoot,
rates,
],
title: 'Miner',
description: 'Mining contract for Anonymity Points',
}),
)
// Deploy Voucher
const airdrops = airdrop.actions.map((a) => ({ to: a.expectedAddress, amount: a.amount }))
actions.push(
deploy({
domain: config.voucher.address,
contract: voucher,
args: [
namehash(config.torn.address),
namehash(config.governance.address),
config.voucher.duration * 2592000, // 60 * 60 * 24 * 30
airdrops,
],
title: 'Voucher',
description: 'TornadoCash voucher contract for early adopters',
}),
)
// Deploy Vestings
config.vesting.governance.beneficiary = actions.find(
(a) => a.domain === 'governance.contract.tornadocash.eth',
).expectedAddress
const vestings = Object.values(config.vesting)
for (const [i, vest] of vestings.entries()) {
actions.push(
deploy({
domain: vest.address,
contract: vesting,
args: [namehash(config.torn.address), vest.beneficiary, 0, vest.cliff, vest.duration],
title: `Vesting ${i + 1} / ${vestings.length}`,
description: `Vesting contract for ${vest.address}`,
}),
)
}
// Write output
const result = {
deployer: DEPLOYER,
salt: SALT,
actions: actions.concat(airdrop.actions),
}
fs.writeFileSync('actions.json', JSON.stringify(result, null, ' '))
console.log('Created actions.json')

68
src/utils.js Normal file
View File

@ -0,0 +1,68 @@
require('dotenv').config()
const config = require('../torn-token/config')
const path = require('path')
const { getCreate2Address } = require('@ethersproject/address')
const { keccak256 } = require('@ethersproject/solidity')
const ethers = require('ethers')
const MerkleTree = require('fixed-merkle-tree')
const { poseidon } = require('circomlib')
const DEPLOYER = process.env.DEPLOYER
const SALT = process.env.SALT
const poseidonHash = (items) => poseidon(items)
const poseidonHash2 = (a, b) => poseidonHash([a, b])
const merkleTree = new MerkleTree(20, [], { hashFunction: poseidonHash2 })
const zeroMerkleRoot =
'0x' +
merkleTree
.root()
.toString(16)
.padStart(32 * 2, '0')
function getContractData(contractPath) {
const json = require(contractPath)
return {
bytecode: json.bytecode,
abi: json.abi,
name: path.basename(contractPath, '.json'),
}
}
function getAddress(bytecode) {
const initHash = keccak256(['bytes'], [bytecode])
return getCreate2Address(DEPLOYER, SALT, initHash)
}
function deploy({
domain,
amount,
contract,
args,
title = '',
description = '',
dependsOn = [config.deployer.address],
}) {
console.log('Generating deploy for', contract.name)
let bytecode = contract.bytecode
if (args) {
const c = new ethers.ContractFactory(contract.abi, contract.bytecode)
bytecode = c.getDeployTransaction(...args).data
}
return {
domain,
amount,
contract: contract.name + '.sol',
bytecode,
expectedAddress: getAddress(bytecode),
title,
description,
dependsOn,
}
}
module.exports = {
deploy,
getContractData,
zeroMerkleRoot,
}

4184
yarn.lock Normal file

File diff suppressed because it is too large Load Diff