mirror of
https://github.com/tornadocash/tornado-deploy.git
synced 2025-01-15 07:27:55 +01:00
initial
This commit is contained in:
commit
4c561efb47
7
.env.example
Normal file
7
.env.example
Normal 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
26
.eslintrc
Normal 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
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
actions.json
|
||||
airdrop.json
|
||||
.env
|
20
.gitmodules
vendored
Normal file
20
.gitmodules
vendored
Normal 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
7
.prettierrc
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"semi": false,
|
||||
"printWidth": 110
|
||||
}
|
36
README.md
Normal file
36
README.md
Normal 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
26
abi/deployer.abi.json
Normal 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
403
abi/ens.abi.json
Normal 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
615
abi/erc20.abi.json
Normal 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
28
build.sh
Executable 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
36
package.json
Normal 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
59
src/airdrop.js
Normal 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
44
src/ens.js
Normal 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
69
src/execute.js
Normal 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
246
src/generate.js
Normal 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
68
src/utils.js
Normal 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,
|
||||
}
|
Loading…
Reference in New Issue
Block a user