initialise
This commit is contained in:
commit
ca9c848f6a
22
LICENSE
Normal file
22
LICENSE
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 Truffle
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
43
README.md
Normal file
43
README.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Tornado.cash token (TORN) [![Build Status](https://github.com/tornadocash/torn-token/workflows/build/badge.svg)](https://github.com/tornadocash/torn-token/actions)
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
1. node 12
|
||||||
|
2. yarn
|
||||||
|
|
||||||
|
## Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ yarn
|
||||||
|
$ cp .env.example .env
|
||||||
|
$ yarn test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deploying
|
||||||
|
|
||||||
|
Deploy to Kovan:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ yarn deploy:kovan
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mainnet deploy instructions
|
||||||
|
|
||||||
|
1. in torn-token repo `cp .env.example .env`
|
||||||
|
2. Specify deployment `PRIVATE_KEY` in the `.env`. It will be the owner during deployment.
|
||||||
|
3. Specify gas price in `truffle.js`
|
||||||
|
4. `yarn deploy:mainnet`
|
||||||
|
|
||||||
|
5. go to [mining](https://github.com/tornadocash/tornado-anonymity-mining) repo
|
||||||
|
6. `cp .env.example .env`
|
||||||
|
7. Specify private key and TORN address
|
||||||
|
8. yarn `yarn deploy:mainnet`
|
||||||
|
|
||||||
|
9. go to [governance](https://github.com/tornadocash/governance) repo
|
||||||
|
10. ...
|
||||||
|
11. ...
|
||||||
|
12. ...
|
||||||
|
|
||||||
|
13. in this repo modify the `config.js` file. Put all addresses that you got during deployment
|
||||||
|
14. set `ONLY_INITIALIZE` to `true` and `TORN_TO_INITIALIZE` to the token address
|
||||||
|
15. `yarn deploy:mainnet`
|
226
config.js
Normal file
226
config.js
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
const { toWei } = require('web3-utils')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
torn: {
|
||||||
|
address: 'torn.contract.tornadocash.eth',
|
||||||
|
cap: toWei('10000000'),
|
||||||
|
pausePeriod: 45 * 24 * 3600, // 45 days
|
||||||
|
distribution: {
|
||||||
|
airdrop: { to: 'voucher', amount: toWei('500000') },
|
||||||
|
miningV2: { to: 'rewardSwap', amount: toWei('1000000') },
|
||||||
|
governance: { to: 'vesting.governance', amount: toWei('5500000') },
|
||||||
|
team1: { to: 'vesting.team1', amount: toWei('822407') },
|
||||||
|
team2: { to: 'vesting.team2', amount: toWei('822407') },
|
||||||
|
team3: { to: 'vesting.team3', amount: toWei('822407') },
|
||||||
|
team4: { to: 'vesting.team4', amount: toWei('500000') },
|
||||||
|
team5: { to: 'vesting.team5', amount: toWei('32779') },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
governance: { address: 'governance.contract.tornadocash.eth' },
|
||||||
|
governanceImpl: { address: 'governance-impl.contract.tornadocash.eth' },
|
||||||
|
voucher: { address: 'voucher.contract.tornadocash.eth', duration: 12 },
|
||||||
|
miningV2: {
|
||||||
|
address: 'mining-v2.contract.tornadocash.eth',
|
||||||
|
initialBalance: toWei('25000'),
|
||||||
|
rates: [
|
||||||
|
{ instance: 'eth-01.tornadocash.eth', value: '10' },
|
||||||
|
{ instance: 'eth-1.tornadocash.eth', value: '20' },
|
||||||
|
{ instance: 'eth-10.tornadocash.eth', value: '50' },
|
||||||
|
{ instance: 'eth-100.tornadocash.eth', value: '400' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
rewardSwap: { address: 'reward-swap.contract.tornadocash.eth', poolWeight: 1e11 },
|
||||||
|
tornadoTrees: { address: 'tornado-trees.contract.tornadocash.eth', levels: 20 },
|
||||||
|
tornadoProxy: { address: 'tornado-proxy.contract.tornadocash.eth' },
|
||||||
|
rewardVerifier: { address: 'reward-verifier.contract.tornadocash.eth' },
|
||||||
|
treeUpdateVerifier: { address: 'tree-update-verifier.contract.tornadocash.eth' },
|
||||||
|
withdrawVerifier: { address: 'withdraw-verifier.contract.tornadocash.eth' },
|
||||||
|
poseidonHasher2: { address: 'poseidon2.contract.tornadocash.eth' },
|
||||||
|
poseidonHasher3: { address: 'poseidon3.contract.tornadocash.eth' },
|
||||||
|
deployer: { address: 'deployer.contract.tornadocash.eth' },
|
||||||
|
vesting: {
|
||||||
|
team1: {
|
||||||
|
address: 'team1.vesting.contract.tornadocash.eth',
|
||||||
|
beneficiary: '0x5A7a51bFb49F190e5A6060a5bc6052Ac14a3b59f',
|
||||||
|
cliff: 12,
|
||||||
|
duration: 36,
|
||||||
|
},
|
||||||
|
team2: {
|
||||||
|
address: 'team2.vesting.contract.tornadocash.eth',
|
||||||
|
beneficiary: '0xF50D442e48E11F16e105431a2664141f44F9feD8',
|
||||||
|
cliff: 12,
|
||||||
|
duration: 36,
|
||||||
|
},
|
||||||
|
team3: {
|
||||||
|
address: 'team3.vesting.contract.tornadocash.eth',
|
||||||
|
beneficiary: '0x6D2C515Ff6A40554869C3Da05494b8D6910D075E',
|
||||||
|
cliff: 12,
|
||||||
|
duration: 36,
|
||||||
|
},
|
||||||
|
team4: {
|
||||||
|
address: 'team4.vesting.contract.tornadocash.eth',
|
||||||
|
beneficiary: '0x504a9c37794a2341F4861bF0A44E8d4016DF8cF2',
|
||||||
|
cliff: 12,
|
||||||
|
duration: 36,
|
||||||
|
},
|
||||||
|
team5: {
|
||||||
|
address: 'team5.vesting.contract.tornadocash.eth',
|
||||||
|
beneficiary: '0x2D81713c58452c92C19b2917e1C770eEcF53Fe41',
|
||||||
|
cliff: 12,
|
||||||
|
duration: 36,
|
||||||
|
},
|
||||||
|
governance: {
|
||||||
|
address: 'governance.vesting.contract.tornadocash.eth',
|
||||||
|
cliff: 3,
|
||||||
|
duration: 60,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
instances: {
|
||||||
|
netId1: {
|
||||||
|
eth: {
|
||||||
|
instanceAddress: {
|
||||||
|
0.1: '0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc',
|
||||||
|
1: '0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936',
|
||||||
|
10: '0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF',
|
||||||
|
100: '0xA160cdAB225685dA1d56aa342Ad8841c3b53f291',
|
||||||
|
},
|
||||||
|
symbol: 'ETH',
|
||||||
|
decimals: 18,
|
||||||
|
},
|
||||||
|
dai: {
|
||||||
|
instanceAddress: {
|
||||||
|
100: '0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3',
|
||||||
|
1000: '0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144',
|
||||||
|
10000: '0xF60dD140cFf0706bAE9Cd734Ac3ae76AD9eBC32A',
|
||||||
|
100000: undefined,
|
||||||
|
},
|
||||||
|
tokenAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
|
||||||
|
symbol: 'DAI',
|
||||||
|
decimals: 18,
|
||||||
|
},
|
||||||
|
cdai: {
|
||||||
|
instanceAddress: {
|
||||||
|
5000: '0x22aaA7720ddd5388A3c0A3333430953C68f1849b',
|
||||||
|
50000: '0xBA214C1c1928a32Bffe790263E38B4Af9bFCD659',
|
||||||
|
500000: '0xb1C8094B234DcE6e03f10a5b673c1d8C69739A00',
|
||||||
|
5000000: undefined,
|
||||||
|
},
|
||||||
|
tokenAddress: '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643',
|
||||||
|
symbol: 'cDAI',
|
||||||
|
decimals: 8,
|
||||||
|
},
|
||||||
|
usdc: {
|
||||||
|
instanceAddress: {
|
||||||
|
100: '0xd96f2B1c14Db8458374d9Aca76E26c3D18364307',
|
||||||
|
1000: '0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D',
|
||||||
|
10000: '0xD691F27f38B395864Ea86CfC7253969B409c362d',
|
||||||
|
100000: undefined,
|
||||||
|
},
|
||||||
|
tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
||||||
|
symbol: 'USDC',
|
||||||
|
decimals: 6,
|
||||||
|
},
|
||||||
|
cusdc: {
|
||||||
|
instanceAddress: {
|
||||||
|
5000: '0xaEaaC358560e11f52454D997AAFF2c5731B6f8a6',
|
||||||
|
50000: '0x1356c899D8C9467C7f71C195612F8A395aBf2f0a',
|
||||||
|
500000: '0xA60C772958a3eD56c1F15dD055bA37AC8e523a0D',
|
||||||
|
5000000: undefined,
|
||||||
|
},
|
||||||
|
tokenAddress: '0x39AA39c021dfbaE8faC545936693aC917d5E7563',
|
||||||
|
symbol: 'cUSDC',
|
||||||
|
decimals: 8,
|
||||||
|
},
|
||||||
|
usdt: {
|
||||||
|
instanceAddress: {
|
||||||
|
100: '0x169AD27A470D064DEDE56a2D3ff727986b15D52B',
|
||||||
|
1000: '0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f',
|
||||||
|
10000: '0xF67721A2D8F736E75a49FdD7FAd2e31D8676542a',
|
||||||
|
100000: '0x9AD122c22B14202B4490eDAf288FDb3C7cb3ff5E',
|
||||||
|
},
|
||||||
|
tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
|
||||||
|
symbol: 'USDT',
|
||||||
|
decimals: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
netId42: {
|
||||||
|
eth: {
|
||||||
|
instanceAddress: {
|
||||||
|
0.1: '0x8b3f5393bA08c24cc7ff5A66a832562aAB7bC95f',
|
||||||
|
1: '0xD6a6AC46d02253c938B96D12BE439F570227aE8E',
|
||||||
|
10: '0xe1BE96331391E519471100c3c1528B66B8F4e5a7',
|
||||||
|
100: '0xd037E0Ac98Dab2fCb7E296c69C6e52767Ae5414D',
|
||||||
|
},
|
||||||
|
symbol: 'ETH',
|
||||||
|
decimals: 18,
|
||||||
|
},
|
||||||
|
dai: {
|
||||||
|
instanceAddress: {
|
||||||
|
100: '0xdf2d3cC5F361CF95b3f62c4bB66deFe3FDE47e3D',
|
||||||
|
1000: '0xD96291dFa35d180a71964D0894a1Ae54247C4ccD',
|
||||||
|
10000: '0xb192794f72EA45e33C3DF6fe212B9c18f6F45AE3',
|
||||||
|
100000: undefined,
|
||||||
|
},
|
||||||
|
tokenAddress: '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa',
|
||||||
|
symbol: 'DAI',
|
||||||
|
decimals: 18,
|
||||||
|
},
|
||||||
|
cdai: {
|
||||||
|
instanceAddress: {
|
||||||
|
5000: '0x6Fc9386ABAf83147b3a89C36D422c625F44121C8',
|
||||||
|
50000: '0x7182EA067e0f050997444FCb065985Fd677C16b6',
|
||||||
|
500000: '0xC22ceFd90fbd1FdEeE554AE6Cc671179BC3b10Ae',
|
||||||
|
5000000: undefined,
|
||||||
|
},
|
||||||
|
tokenAddress: '0xe7bc397DBd069fC7d0109C0636d06888bb50668c',
|
||||||
|
symbol: 'cDAI',
|
||||||
|
decimals: 8,
|
||||||
|
},
|
||||||
|
usdc: {
|
||||||
|
instanceAddress: {
|
||||||
|
100: '0x137E2B6d185018e7f09f6cf175a970e7fC73826C',
|
||||||
|
1000: '0xcC7f1633A5068E86E3830e692e3e3f8f520525Af',
|
||||||
|
10000: '0x28C8f149a0ab8A9bdB006B8F984fFFCCE52ef5EF',
|
||||||
|
100000: undefined,
|
||||||
|
},
|
||||||
|
tokenAddress: '0x75B0622Cec14130172EaE9Cf166B92E5C112FaFF',
|
||||||
|
symbol: 'USDC',
|
||||||
|
decimals: 6,
|
||||||
|
},
|
||||||
|
cusdc: {
|
||||||
|
instanceAddress: {
|
||||||
|
5000: '0xc0648F28ABA385c8a1421Bbf1B59e3c474F89cB0',
|
||||||
|
50000: '0x0C53853379c6b1A7B74E0A324AcbDD5Eabd4981D',
|
||||||
|
500000: '0xf84016A0E03917cBe700D318EB1b7a53e6e3dEe1',
|
||||||
|
5000000: undefined,
|
||||||
|
},
|
||||||
|
tokenAddress: '0xcfC9bB230F00bFFDB560fCe2428b4E05F3442E35',
|
||||||
|
symbol: 'cUSDC',
|
||||||
|
decimals: 8,
|
||||||
|
},
|
||||||
|
usdt: {
|
||||||
|
instanceAddress: {
|
||||||
|
100: '0x327853Da7916a6A0935563FB1919A48843036b42',
|
||||||
|
1000: '0x531AA4DF5858EA1d0031Dad16e3274609DE5AcC0',
|
||||||
|
10000: '0x0958275F0362cf6f07D21373aEE0cf37dFe415dD',
|
||||||
|
100000: '0x14aEd24B67EaF3FF28503eB92aeb217C47514364',
|
||||||
|
},
|
||||||
|
tokenAddress: '0x03c5F29e9296006876d8DF210BCFfD7EA5Db1Cf1',
|
||||||
|
symbol: 'USDT',
|
||||||
|
decimals: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
netId5: {
|
||||||
|
eth: {
|
||||||
|
instanceAddress: {
|
||||||
|
0.1: '0x6Bf694a291DF3FeC1f7e69701E3ab6c592435Ae7',
|
||||||
|
1: '0x3aac1cC67c2ec5Db4eA850957b967Ba153aD6279',
|
||||||
|
10: '0x723B78e67497E85279CB204544566F4dC5d2acA0',
|
||||||
|
100: '0x0E3A09dDA6B20aFbB34aC7cD4A6881493f3E7bf7',
|
||||||
|
},
|
||||||
|
symbol: 'ETH',
|
||||||
|
decimals: 18,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
23
contracts/Airdrop.sol
Normal file
23
contracts/Airdrop.sol
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
import "./ENS.sol";
|
||||||
|
|
||||||
|
contract Airdrop is EnsResolve {
|
||||||
|
struct Recipient {
|
||||||
|
address to;
|
||||||
|
uint256 amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(bytes32 tokenAddress, Recipient[] memory targets) public {
|
||||||
|
IERC20 token = IERC20(resolve(tokenAddress));
|
||||||
|
require(token.balanceOf(address(this)) > 0, "Balance is 0, airdrop already done");
|
||||||
|
for (uint256 i = 0; i < targets.length; i++) {
|
||||||
|
token.transfer(targets[i].to, targets[i].amount);
|
||||||
|
}
|
||||||
|
selfdestruct(address(0));
|
||||||
|
}
|
||||||
|
}
|
93
contracts/ECDSA.sol
Normal file
93
contracts/ECDSA.sol
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
|
// A copy from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237/files
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
|
||||||
|
*
|
||||||
|
* These functions can be used to verify that a message was signed by the holder
|
||||||
|
* of the private keys of a given address.
|
||||||
|
*/
|
||||||
|
library ECDSA {
|
||||||
|
/**
|
||||||
|
* @dev Returns the address that signed a hashed message (`hash`) with
|
||||||
|
* `signature`. This address can then be used for verification purposes.
|
||||||
|
*
|
||||||
|
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
|
||||||
|
* this function rejects them by requiring the `s` value to be in the lower
|
||||||
|
* half order, and the `v` value to be either 27 or 28.
|
||||||
|
*
|
||||||
|
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
|
||||||
|
* verification to be secure: it is possible to craft signatures that
|
||||||
|
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
|
||||||
|
* this is by receiving a hash of the original message (which may otherwise
|
||||||
|
* be too long), and then calling {toEthSignedMessageHash} on it.
|
||||||
|
*/
|
||||||
|
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
|
||||||
|
// Check the signature length
|
||||||
|
if (signature.length != 65) {
|
||||||
|
revert("ECDSA: invalid signature length");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divide the signature in r, s and v variables
|
||||||
|
bytes32 r;
|
||||||
|
bytes32 s;
|
||||||
|
uint8 v;
|
||||||
|
|
||||||
|
// ecrecover takes the signature parameters, and the only way to get them
|
||||||
|
// currently is to use assembly.
|
||||||
|
// solhint-disable-next-line no-inline-assembly
|
||||||
|
assembly {
|
||||||
|
r := mload(add(signature, 0x20))
|
||||||
|
s := mload(add(signature, 0x40))
|
||||||
|
v := mload(add(signature, 0x41))
|
||||||
|
}
|
||||||
|
|
||||||
|
return recover(hash, v, r, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`,
|
||||||
|
* `r` and `s` signature fields separately.
|
||||||
|
*/
|
||||||
|
function recover(
|
||||||
|
bytes32 hash,
|
||||||
|
uint8 v,
|
||||||
|
bytes32 r,
|
||||||
|
bytes32 s
|
||||||
|
) internal pure returns (address) {
|
||||||
|
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
|
||||||
|
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
|
||||||
|
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
|
||||||
|
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
|
||||||
|
//
|
||||||
|
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
|
||||||
|
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
|
||||||
|
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
|
||||||
|
// these malleable signatures as well.
|
||||||
|
require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
|
||||||
|
require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
|
||||||
|
|
||||||
|
// If the signature is valid (and not malleable), return the signer address
|
||||||
|
address signer = ecrecover(hash, v, r, s);
|
||||||
|
require(signer != address(0), "ECDSA: invalid signature");
|
||||||
|
|
||||||
|
return signer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
|
||||||
|
* replicates the behavior of the
|
||||||
|
* https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
|
||||||
|
* JSON-RPC method.
|
||||||
|
*
|
||||||
|
* See {recover}.
|
||||||
|
*/
|
||||||
|
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
|
||||||
|
// 32 is the length in bytes of hash,
|
||||||
|
// enforced by the type signature above
|
||||||
|
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
|
||||||
|
}
|
||||||
|
}
|
35
contracts/ENS.sol
Normal file
35
contracts/ENS.sol
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
|
interface ENS {
|
||||||
|
function resolver(bytes32 node) external view returns (Resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Resolver {
|
||||||
|
function addr(bytes32 node) external view returns (address);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract EnsResolve {
|
||||||
|
function resolve(bytes32 node) public view virtual returns (address) {
|
||||||
|
ENS Registry = ENS(
|
||||||
|
getChainId() == 1 ? 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e : 0x8595bFb0D940DfEDC98943FA8a907091203f25EE
|
||||||
|
);
|
||||||
|
return Registry.resolver(node).addr(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bulkResolve(bytes32[] memory domains) public view returns (address[] memory result) {
|
||||||
|
result = new address[](domains.length);
|
||||||
|
for (uint256 i = 0; i < domains.length; i++) {
|
||||||
|
result[i] = resolve(domains[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChainId() internal pure returns (uint256) {
|
||||||
|
uint256 chainId;
|
||||||
|
assembly {
|
||||||
|
chainId := chainid()
|
||||||
|
}
|
||||||
|
return chainId;
|
||||||
|
}
|
||||||
|
}
|
106
contracts/ERC20Permit.sol
Normal file
106
contracts/ERC20Permit.sol
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
|
// Adapted copy from https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2237/files
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||||
|
import "./ECDSA.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Extension of {ERC20} that allows token holders to use their tokens
|
||||||
|
* without sending any transactions by setting {IERC20-allowance} with a
|
||||||
|
* signature using the {permit} method, and then spend them via
|
||||||
|
* {IERC20-transferFrom}.
|
||||||
|
*
|
||||||
|
* The {permit} signature mechanism conforms to the {IERC2612Permit} interface.
|
||||||
|
*/
|
||||||
|
abstract contract ERC20Permit is ERC20 {
|
||||||
|
mapping(address => uint256) private _nonces;
|
||||||
|
|
||||||
|
bytes32 private constant _PERMIT_TYPEHASH = keccak256(
|
||||||
|
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mapping of ChainID to domain separators. This is a very gas efficient way
|
||||||
|
// to not recalculate the domain separator on every call, while still
|
||||||
|
// automatically detecting ChainID changes.
|
||||||
|
mapping(uint256 => bytes32) private _domainSeparators;
|
||||||
|
|
||||||
|
constructor() internal {
|
||||||
|
_updateDomainSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev See {IERC2612Permit-permit}.
|
||||||
|
*
|
||||||
|
* If https://eips.ethereum.org/EIPS/eip-1344[ChainID] ever changes, the
|
||||||
|
* EIP712 Domain Separator is automatically recalculated.
|
||||||
|
*/
|
||||||
|
function permit(
|
||||||
|
address owner,
|
||||||
|
address spender,
|
||||||
|
uint256 amount,
|
||||||
|
uint256 deadline,
|
||||||
|
uint8 v,
|
||||||
|
bytes32 r,
|
||||||
|
bytes32 s
|
||||||
|
) public {
|
||||||
|
require(blockTimestamp() <= deadline, "ERC20Permit: expired deadline");
|
||||||
|
|
||||||
|
bytes32 hashStruct = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, amount, _nonces[owner], deadline));
|
||||||
|
|
||||||
|
bytes32 hash = keccak256(abi.encodePacked(uint16(0x1901), _domainSeparator(), hashStruct));
|
||||||
|
|
||||||
|
address signer = ECDSA.recover(hash, v, r, s);
|
||||||
|
require(signer == owner, "ERC20Permit: invalid signature");
|
||||||
|
|
||||||
|
_nonces[owner]++;
|
||||||
|
_approve(owner, spender, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev See {IERC2612Permit-nonces}.
|
||||||
|
*/
|
||||||
|
function nonces(address owner) public view returns (uint256) {
|
||||||
|
return _nonces[owner];
|
||||||
|
}
|
||||||
|
|
||||||
|
function _updateDomainSeparator() private returns (bytes32) {
|
||||||
|
uint256 _chainID = chainID();
|
||||||
|
|
||||||
|
bytes32 newDomainSeparator = keccak256(
|
||||||
|
abi.encode(
|
||||||
|
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
|
||||||
|
keccak256(bytes(name())),
|
||||||
|
keccak256(bytes("1")), // Version
|
||||||
|
_chainID,
|
||||||
|
address(this)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
_domainSeparators[_chainID] = newDomainSeparator;
|
||||||
|
|
||||||
|
return newDomainSeparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the domain separator, updating it if chainID changes
|
||||||
|
function _domainSeparator() private returns (bytes32) {
|
||||||
|
bytes32 domainSeparator = _domainSeparators[chainID()];
|
||||||
|
if (domainSeparator != 0x00) {
|
||||||
|
return domainSeparator;
|
||||||
|
} else {
|
||||||
|
return _updateDomainSeparator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function chainID() public view virtual returns (uint256 _chainID) {
|
||||||
|
assembly {
|
||||||
|
_chainID := chainid()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function blockTimestamp() public view virtual returns (uint256) {
|
||||||
|
return block.timestamp;
|
||||||
|
}
|
||||||
|
}
|
110
contracts/TORN.sol
Normal file
110
contracts/TORN.sol
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||||
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||||
|
import "@openzeppelin/contracts/utils/Pausable.sol";
|
||||||
|
import "@openzeppelin/contracts/math/Math.sol";
|
||||||
|
import "./ERC20Permit.sol";
|
||||||
|
import "./ENS.sol";
|
||||||
|
|
||||||
|
contract TORN is ERC20("TornadoCash", "TORN"), ERC20Burnable, ERC20Permit, Pausable, EnsResolve {
|
||||||
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
|
uint256 public immutable canUnpauseAfter;
|
||||||
|
address public immutable governance;
|
||||||
|
mapping(address => bool) public allowedTransferee;
|
||||||
|
|
||||||
|
event Allowed(address target);
|
||||||
|
event Disallowed(address target);
|
||||||
|
|
||||||
|
struct Recipient {
|
||||||
|
bytes32 to;
|
||||||
|
uint256 amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
bytes32 _governance,
|
||||||
|
uint256 _pausePeriod,
|
||||||
|
Recipient[] memory _vestings
|
||||||
|
) public {
|
||||||
|
address _resolvedGovernance = resolve(_governance);
|
||||||
|
governance = _resolvedGovernance;
|
||||||
|
allowedTransferee[_resolvedGovernance] = true;
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < _vestings.length; i++) {
|
||||||
|
address to = resolve(_vestings[i].to);
|
||||||
|
_mint(to, _vestings[i].amount);
|
||||||
|
allowedTransferee[to] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
canUnpauseAfter = blockTimestamp().add(_pausePeriod);
|
||||||
|
_pause();
|
||||||
|
require(totalSupply() == 10000000 ether, "TORN: incorrect distribution");
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier onlyGovernance() {
|
||||||
|
require(_msgSender() == governance, "TORN: only governance can perform this action");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeTransferability(bool decision) public onlyGovernance {
|
||||||
|
require(blockTimestamp() > canUnpauseAfter, "TORN: cannot change transferability yet");
|
||||||
|
if (decision) {
|
||||||
|
_unpause();
|
||||||
|
} else {
|
||||||
|
_pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addToAllowedList(address[] memory target) public onlyGovernance {
|
||||||
|
for (uint256 i = 0; i < target.length; i++) {
|
||||||
|
allowedTransferee[target[i]] = true;
|
||||||
|
emit Allowed(target[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeFromAllowedList(address[] memory target) public onlyGovernance {
|
||||||
|
for (uint256 i = 0; i < target.length; i++) {
|
||||||
|
allowedTransferee[target[i]] = false;
|
||||||
|
emit Disallowed(target[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _beforeTokenTransfer(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
) internal override {
|
||||||
|
super._beforeTokenTransfer(from, to, amount);
|
||||||
|
require(!paused() || allowedTransferee[from] || allowedTransferee[to], "TORN: paused");
|
||||||
|
require(to != address(this), "TORN: invalid recipient");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Method to claim junk and accidentally sent tokens
|
||||||
|
function rescueTokens(
|
||||||
|
IERC20 _token,
|
||||||
|
address payable _to,
|
||||||
|
uint256 _balance
|
||||||
|
) external onlyGovernance {
|
||||||
|
require(_to != address(0), "TORN: can not send to zero address");
|
||||||
|
|
||||||
|
if (_token == IERC20(0)) {
|
||||||
|
// for Ether
|
||||||
|
uint256 totalBalance = address(this).balance;
|
||||||
|
uint256 balance = _balance == 0 ? totalBalance : Math.min(totalBalance, _balance);
|
||||||
|
_to.transfer(balance);
|
||||||
|
} else {
|
||||||
|
// any other erc20
|
||||||
|
uint256 totalBalance = _token.balanceOf(address(this));
|
||||||
|
uint256 balance = _balance == 0 ? totalBalance : Math.min(totalBalance, _balance);
|
||||||
|
require(balance > 0, "TORN: trying to send 0 balance");
|
||||||
|
_token.safeTransfer(_to, balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
105
contracts/Vesting.sol
Normal file
105
contracts/Vesting.sol
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||||
|
import "@openzeppelin/contracts/math/Math.sol";
|
||||||
|
import "@openzeppelin/contracts/math/SafeMath.sol";
|
||||||
|
import "./ENS.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title Vesting
|
||||||
|
* @dev A token holder contract that can release its token balance gradually like a
|
||||||
|
* typical vesting scheme, with a cliff and vesting period. Optionally revocable by the
|
||||||
|
* owner.
|
||||||
|
*/
|
||||||
|
contract Vesting is EnsResolve {
|
||||||
|
using SafeERC20 for IERC20;
|
||||||
|
using SafeMath for uint256;
|
||||||
|
|
||||||
|
uint256 public constant SECONDS_PER_MONTH = 30 days;
|
||||||
|
|
||||||
|
event Released(uint256 amount);
|
||||||
|
|
||||||
|
// beneficiary of tokens after they are released
|
||||||
|
address public immutable beneficiary;
|
||||||
|
IERC20 public immutable token;
|
||||||
|
|
||||||
|
uint256 public immutable cliffInMonths;
|
||||||
|
uint256 public immutable startTimestamp;
|
||||||
|
uint256 public immutable durationInMonths;
|
||||||
|
uint256 public released;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Creates a vesting contract that vests its balance of any ERC20 token to the
|
||||||
|
* _beneficiary, monthly in a linear fashion until duration has passed. By then all
|
||||||
|
* of the balance will have vested.
|
||||||
|
* @param _beneficiary address of the beneficiary to whom vested tokens are transferred
|
||||||
|
* @param _cliffInMonths duration in months of the cliff in which tokens will begin to vest
|
||||||
|
* @param _durationInMonths duration in months of the period in which the tokens will vest
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
bytes32 _token,
|
||||||
|
address _beneficiary,
|
||||||
|
uint256 _startTimestamp,
|
||||||
|
uint256 _cliffInMonths,
|
||||||
|
uint256 _durationInMonths
|
||||||
|
) public {
|
||||||
|
require(_beneficiary != address(0), "Beneficiary cannot be empty");
|
||||||
|
require(_cliffInMonths <= _durationInMonths, "Cliff is greater than duration");
|
||||||
|
|
||||||
|
token = IERC20(resolve(_token));
|
||||||
|
beneficiary = _beneficiary;
|
||||||
|
durationInMonths = _durationInMonths;
|
||||||
|
cliffInMonths = _cliffInMonths;
|
||||||
|
startTimestamp = _startTimestamp == 0 ? blockTimestamp() : _startTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Transfers vested tokens to beneficiary.
|
||||||
|
*/
|
||||||
|
function release() external {
|
||||||
|
uint256 vested = vestedAmount();
|
||||||
|
require(vested > 0, "No tokens to release");
|
||||||
|
|
||||||
|
released = released.add(vested);
|
||||||
|
token.safeTransfer(beneficiary, vested);
|
||||||
|
|
||||||
|
emit Released(vested);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Calculates the amount that has already vested but hasn't been released yet.
|
||||||
|
*/
|
||||||
|
function vestedAmount() public view returns (uint256) {
|
||||||
|
if (blockTimestamp() < startTimestamp) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 elapsedTime = blockTimestamp().sub(startTimestamp);
|
||||||
|
uint256 elapsedMonths = elapsedTime.div(SECONDS_PER_MONTH);
|
||||||
|
|
||||||
|
if (elapsedMonths < cliffInMonths) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If over vesting duration, all tokens vested
|
||||||
|
if (elapsedMonths >= durationInMonths) {
|
||||||
|
return token.balanceOf(address(this));
|
||||||
|
} else {
|
||||||
|
uint256 currentBalance = token.balanceOf(address(this));
|
||||||
|
uint256 totalBalance = currentBalance.add(released);
|
||||||
|
|
||||||
|
uint256 vested = totalBalance.mul(elapsedMonths).div(durationInMonths);
|
||||||
|
uint256 unreleased = vested.sub(released);
|
||||||
|
|
||||||
|
// currentBalance can be 0 in case of vesting being revoked earlier.
|
||||||
|
return Math.min(currentBalance, unreleased);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function blockTimestamp() public view virtual returns (uint256) {
|
||||||
|
return block.timestamp;
|
||||||
|
}
|
||||||
|
}
|
66
contracts/Voucher.sol
Normal file
66
contracts/Voucher.sol
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* This is tornado.cash airdrop for early adopters. In order to claim your TORN token please follow https://tornado.cash/airdrop
|
||||||
|
*/
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
|
||||||
|
import "./ENS.sol";
|
||||||
|
|
||||||
|
contract Voucher is ERC20("TornadoCash voucher for early adopters", "vTORN"), EnsResolve {
|
||||||
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
|
IERC20 public immutable torn;
|
||||||
|
uint256 public immutable expiresAt;
|
||||||
|
address public immutable governance;
|
||||||
|
mapping(address => bool) public allowedTransferee;
|
||||||
|
|
||||||
|
struct Recipient {
|
||||||
|
address to;
|
||||||
|
uint256 amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
bytes32 _torn,
|
||||||
|
bytes32 _governance,
|
||||||
|
uint256 _duration,
|
||||||
|
Recipient[] memory _airdrops
|
||||||
|
) public {
|
||||||
|
torn = IERC20(resolve(_torn));
|
||||||
|
governance = resolve(_governance);
|
||||||
|
expiresAt = blockTimestamp().add(_duration);
|
||||||
|
for (uint256 i = 0; i < _airdrops.length; i++) {
|
||||||
|
_mint(_airdrops[i].to, _airdrops[i].amount);
|
||||||
|
allowedTransferee[_airdrops[i].to] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function redeem() external {
|
||||||
|
require(blockTimestamp() < expiresAt, "Airdrop redeem period has ended");
|
||||||
|
uint256 amount = balanceOf(msg.sender);
|
||||||
|
_burn(msg.sender, amount);
|
||||||
|
torn.safeTransfer(msg.sender, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rescueExpiredTokens() external {
|
||||||
|
require(blockTimestamp() >= expiresAt, "Airdrop redeem period has not ended yet");
|
||||||
|
torn.safeTransfer(governance, torn.balanceOf(address(this)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _beforeTokenTransfer(
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount
|
||||||
|
) internal override {
|
||||||
|
super._beforeTokenTransfer(from, to, amount);
|
||||||
|
require(to == address(0) || from == address(0) || allowedTransferee[from], "ERC20: transfer is not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
function blockTimestamp() public view virtual returns (uint256) {
|
||||||
|
return block.timestamp;
|
||||||
|
}
|
||||||
|
}
|
13
contracts/mocks/AirdropMock.sol
Normal file
13
contracts/mocks/AirdropMock.sol
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../Airdrop.sol";
|
||||||
|
|
||||||
|
contract AirdropMock is Airdrop {
|
||||||
|
constructor(bytes32 tokenAddress, Recipient[] memory targets) public Airdrop(tokenAddress, targets) {}
|
||||||
|
|
||||||
|
function resolve(bytes32 addr) public view override returns (address) {
|
||||||
|
return address(uint160(uint256(addr) >> (12 * 8)));
|
||||||
|
}
|
||||||
|
}
|
32
contracts/mocks/ENSMock.sol
Normal file
32
contracts/mocks/ENSMock.sol
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract ENSMock {
|
||||||
|
mapping(bytes32 => address) public registry;
|
||||||
|
|
||||||
|
function resolver(
|
||||||
|
bytes32 /* _node */
|
||||||
|
) external view returns (address) {
|
||||||
|
return address(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addr(bytes32 _node) external view returns (address) {
|
||||||
|
return registry[_node];
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAddr(bytes32 _node, address _addr) external {
|
||||||
|
registry[_node] = _addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
|
||||||
|
results = new bytes[](data.length);
|
||||||
|
for (uint256 i = 0; i < data.length; i++) {
|
||||||
|
(bool success, bytes memory result) = address(this).delegatecall(data[i]);
|
||||||
|
require(success);
|
||||||
|
results[i] = result;
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
32
contracts/mocks/TORNMock.sol
Normal file
32
contracts/mocks/TORNMock.sol
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../TORN.sol";
|
||||||
|
import "./Timestamp.sol";
|
||||||
|
|
||||||
|
contract TORNMock is TORN, Timestamp {
|
||||||
|
uint256 public chainId;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
bytes32 _governance,
|
||||||
|
uint256 _pausePeriod,
|
||||||
|
Recipient[] memory _vesting
|
||||||
|
) public TORN(_governance, _pausePeriod, _vesting) {}
|
||||||
|
|
||||||
|
function resolve(bytes32 addr) public view override returns (address) {
|
||||||
|
return address(uint160(uint256(addr) >> (12 * 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setChainId(uint256 _chainId) public {
|
||||||
|
chainId = _chainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function chainID() public view override returns (uint256) {
|
||||||
|
return chainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function blockTimestamp() public view override(Timestamp, ERC20Permit) returns (uint256) {
|
||||||
|
return Timestamp.blockTimestamp();
|
||||||
|
}
|
||||||
|
}
|
14
contracts/mocks/Timestamp.sol
Normal file
14
contracts/mocks/Timestamp.sol
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
|
contract Timestamp {
|
||||||
|
uint256 public fakeTimestamp;
|
||||||
|
|
||||||
|
function setFakeTimestamp(uint256 _fakeTimestamp) public {
|
||||||
|
fakeTimestamp = _fakeTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function blockTimestamp() public view virtual returns (uint256) {
|
||||||
|
return fakeTimestamp == 0 ? block.timestamp : fakeTimestamp;
|
||||||
|
}
|
||||||
|
}
|
25
contracts/mocks/VestingMock.sol
Normal file
25
contracts/mocks/VestingMock.sol
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
|
||||||
|
import "../Vesting.sol";
|
||||||
|
import "./Timestamp.sol";
|
||||||
|
|
||||||
|
contract VestingMock is Vesting, Timestamp {
|
||||||
|
constructor(
|
||||||
|
bytes32 _token,
|
||||||
|
address _beneficiary,
|
||||||
|
uint256 _startTimestamp,
|
||||||
|
uint256 _cliffInMonths,
|
||||||
|
uint256 _durationInMonths
|
||||||
|
) public Vesting(_token, _beneficiary, _startTimestamp, _cliffInMonths, _durationInMonths) {}
|
||||||
|
|
||||||
|
function resolve(bytes32 addr) public view override returns (address) {
|
||||||
|
return address(uint160(uint256(addr) >> (12 * 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function blockTimestamp() public view override(Timestamp, Vesting) returns (uint256) {
|
||||||
|
return Timestamp.blockTimestamp();
|
||||||
|
}
|
||||||
|
}
|
25
contracts/mocks/VoucherMock.sol
Normal file
25
contracts/mocks/VoucherMock.sol
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.6.0;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
|
||||||
|
import "../Voucher.sol";
|
||||||
|
import "./Timestamp.sol";
|
||||||
|
|
||||||
|
contract VoucherMock is Voucher, Timestamp {
|
||||||
|
constructor(
|
||||||
|
bytes32 _torn,
|
||||||
|
bytes32 _governance,
|
||||||
|
uint256 _duration,
|
||||||
|
Recipient[] memory _airdrops
|
||||||
|
) public Voucher(_torn, _governance, _duration, _airdrops) {}
|
||||||
|
|
||||||
|
function resolve(bytes32 addr) public view override returns (address) {
|
||||||
|
return address(uint160(uint256(addr) >> (12 * 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function blockTimestamp() public view override(Timestamp, Voucher) returns (uint256) {
|
||||||
|
return Timestamp.blockTimestamp();
|
||||||
|
}
|
||||||
|
}
|
56
package.json
Normal file
56
package.json
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"name": "torn-token",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "config.js",
|
||||||
|
"repository": "https://github.com/tornadocash/torn-token.git",
|
||||||
|
"author": "Tornadocash team <hello@tornado.cash>",
|
||||||
|
"license": "MIT",
|
||||||
|
"files": [
|
||||||
|
"config.js",
|
||||||
|
"contracts/*"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"compile": "truffle compile",
|
||||||
|
"coverage": "yarn compile && truffle run coverage",
|
||||||
|
"test": "truffle test",
|
||||||
|
"test:stacktrace": "yarn test --stacktrace",
|
||||||
|
"eslint": "eslint --ext .js --ignore-path .gitignore .",
|
||||||
|
"prettier:check": "prettier --check . --config .prettierrc",
|
||||||
|
"prettier:fix": "prettier --write . --config .prettierrc",
|
||||||
|
"lint": "yarn eslint && yarn prettier:check",
|
||||||
|
"deploy:mainnet": "truffle migrate --network mainnet",
|
||||||
|
"deploy:kovan": "truffle migrate --network kovan",
|
||||||
|
"deploy:dev": "truffle migrate --skip-dry-run --network test",
|
||||||
|
"init:mainnet": "truffle migrate -f 2 --to 2 --network mainnet",
|
||||||
|
"init:kovan": "truffle migrate -f 2 --to 2 --network kovan",
|
||||||
|
"init:test": "truffle migrate -f 2 --to 2 --network test",
|
||||||
|
"voucher:mainnet": "truffle migrate -f 3 --network mainnet",
|
||||||
|
"voucher:kovan": "truffle migrate -f 3 --network kovan",
|
||||||
|
"voucher:dev": "truffle migrate -f 3 --skip-dry-run --network test",
|
||||||
|
"verify": "truffle run verify --network $NETWORK"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@ticket721/e712": "^0.4.1",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"bn-chai": "^1.0.1",
|
||||||
|
"chai": "^4.2.0",
|
||||||
|
"chai-as-promised": "^7.1.1",
|
||||||
|
"eslint": "^7.5.0",
|
||||||
|
"prettier": "^2.1.2",
|
||||||
|
"prettier-plugin-solidity": "^1.0.0-alpha.59",
|
||||||
|
"rlp": "^2.2.6",
|
||||||
|
"solhint-plugin-prettier": "^0.0.4",
|
||||||
|
"solidity-coverage": "^0.7.7",
|
||||||
|
"truffle": "^5.1.29",
|
||||||
|
"truffle-flattener": "^1.4.4",
|
||||||
|
"truffle-hdwallet-provider": "^1.0.17",
|
||||||
|
"truffle-plugin-verify": "^0.3.11"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@openzeppelin/contracts": "^3.1.0",
|
||||||
|
"dotenv": "^8.2.0",
|
||||||
|
"eth-sig-util": "^2.5.3",
|
||||||
|
"ethereumjs-util": "^7.0.3",
|
||||||
|
"web3": "^1.2.11"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user