Force router calls for new instances

Signed-off-by: T-Hax <>
This commit is contained in:
T-Hax 2023-06-12 15:50:25 +00:00
parent 851d4ef487
commit 728eb71388
42 changed files with 16354 additions and 14036 deletions

@ -8,8 +8,7 @@
{
"files": "*.sol",
"options": {
"singleQuote": false,
"printWidth": 130
"singleQuote": false
}
}
]

BIN
.yarn/install-state.gz Normal file

Binary file not shown.

1
.yarnrc.yml Normal file

@ -0,0 +1 @@
nodeLinker: node-modules

@ -11,7 +11,11 @@ contract AdminUpgradeableProxy is TransparentUpgradeableProxy {
/**
* @dev Initializes an upgradeable proxy backed by the implementation at `_logic`.
*/
constructor(address _logic, address _admin, bytes memory _data) payable TransparentUpgradeableProxy(_logic, _admin, _data) {}
constructor(
address _logic,
address _admin,
bytes memory _data
) payable TransparentUpgradeableProxy(_logic, _admin, _data) {}
/**
* @dev Override to allow admin access the fallback function.

@ -3,72 +3,138 @@
pragma solidity 0.7.6;
pragma abicoder v2;
// OZ Imports
import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
// Local imports
import "./instances/ERC20TornadoCloneable.sol";
import "./instances/ETHTornadoCloneable.sol";
/**
* @title MinimalInstanceFactory
* @author AlienTornadosaurusHex
* @notice A factory to deploy new Tornado Instances.
*/
contract MinimalInstanceFactory {
using Clones for address;
using Address for address;
address public immutable ERC20Impl;
address public immutable nativeCurImpl;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/** @notice The address of the verifier contract, no actual use in the contract but just to show which
address was set in the instances */
address public immutable verifier;
/** @notice The address of the hasher contract, no actual use in the contract but just to show which address
was set in the instances */
address public immutable hasher;
/** @notice The address of the router which will have relayers run their txs through */
address public immutable router;
/** @notice The standard merkle tree height used for the instances */
uint32 public immutable merkleTreeHeight;
/** @notice The implementation which will be used for the cloned ETH (!) instances */
address public immutable ethImpl;
/** @notice The implementation which will be used for the cloned ERC20 (!) instances */
address public immutable ERC20Impl;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
event NewTreeHeightSet(uint32 indexed newTreeHeight);
event NewImplementationSet(address indexed ERC20Impl, address indexed nativeCurImpl, address verifier, address hasher);
event NewInstanceCloneCreated(address indexed clone);
event NewImplementationSet(
address indexed ERC20Impl,
address indexed ethImpl,
address verifier,
address hasher
);
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/**
* @notice This instance factory is NOT permissioned. It is designed to always be able to print
* new Tornado Instances if necessary.
* @dev Creates new Tornado instance.
* @notice This instance factory is designed to be always able to print new
tornado instances.
*/
constructor(address _verifier, address _hasher, uint32 _merkleTreeHeight) public {
verifier = _verifier;
hasher = _hasher;
constructor(
address _verifierAddress,
address _hasherAddress,
address _routerAddresss,
uint32 _merkleTreeHeight
) {
// Store instance parameters
merkleTreeHeight = _merkleTreeHeight;
verifier = _verifierAddress;
hasher = _hasherAddress;
ERC20TornadoCloneable ERC20ImplContract = new ERC20TornadoCloneable(_verifier, _hasher);
// Store router addresses
router = _routerAddresss;
// Deploy implementations
ETHTornadoCloneable ethImplContract = new ETHTornadoCloneable(_verifierAddress, _hasherAddress);
ERC20TornadoCloneable ERC20ImplContract = new ERC20TornadoCloneable(_verifierAddress, _hasherAddress);
// Store implementations
ethImpl = address(ethImplContract);
ERC20Impl = address(ERC20ImplContract);
ETHTornadoCloneable nativeCurImplContract = new ETHTornadoCloneable(_verifier, _hasher);
nativeCurImpl = address(nativeCurImplContract);
}
/**
* @notice Creates new Tornado instance.
* @param _denomination denomination of new Tornado instance
* @param _token address of ERC20 token for a new instance, if zero address, then it will be ETH
* @notice Creates a new Tornado instance. If the registry is dead, you can
immediately call `relayerRegistryIsDead()` on the cloned instance.
* @param _denomination The denomination of the cloned instance. Must be in E18.
* @param _token The token address of the new instance.
then it will be ETH
*/
function createInstanceClone(uint256 _denomination, address _token) public virtual returns (address clone) {
// Calculate salt for the deterministic deployment
bytes32 salt = keccak256(abi.encodePacked(_denomination, _token));
// Check whether token is ETH
if (_token == address(0)) {
clone = nativeCurImpl.predictDeterministicAddress(salt);
// Calc deployment address
clone = ethImpl.predictDeterministicAddress(salt);
// Check whether already exists, if so, don't deploy, just give address
if (!clone.isContract()) {
nativeCurImpl.cloneDeterministic(salt);
ethImpl.cloneDeterministic(salt);
emit NewInstanceCloneCreated(clone);
ETHTornadoCloneable(clone).init(_denomination, merkleTreeHeight);
ETHTornadoCloneable(clone).initialize(_denomination, merkleTreeHeight);
}
} else {
// Calc deployment address
clone = ERC20Impl.predictDeterministicAddress(salt);
// Check whether already exists, if so, don't deploy, just give address
if (!clone.isContract()) {
ERC20Impl.cloneDeterministic(salt);
emit NewInstanceCloneCreated(clone);
ERC20TornadoCloneable(clone).init(_denomination, merkleTreeHeight, _token);
ERC20TornadoCloneable(clone).initialize(router, _token, _denomination, merkleTreeHeight);
}
}
// Return address of the clone
return clone;
}
/**
* @notice Since addresses are deterministic, this function fetches the
deterministic address to which an instance would be or is deployed, for a
specific denomination and token.
* @param _denomination The denomination to calculate the address for. Needs
to be E18.
* @param _token The token address to calculate the address for.
*/
function getInstanceAddress(uint256 _denomination, address _token) public view returns (address) {
bytes32 salt = keccak256(abi.encodePacked(_denomination, _token));
if (_token == address(0)) {
return nativeCurImpl.predictDeterministicAddress(salt);
return ethImpl.predictDeterministicAddress(salt);
} else {
return ERC20Impl.predictDeterministicAddress(salt);
}

@ -158,7 +158,9 @@ contract Miner is EnsResolve {
accountNullifiers[_args.account.inputNullifierHash] = true;
rewardNullifiers[_args.rewardNullifier] = true;
insertAccountRoot(_args.account.inputRoot == getLastAccountRoot() ? _args.account.outputRoot : _treeUpdateArgs.newRoot);
insertAccountRoot(
_args.account.inputRoot == getLastAccountRoot() ? _args.account.outputRoot : _treeUpdateArgs.newRoot
);
if (_args.fee > 0) {
rewardSwap.swap(_args.extData.relayer, _args.fee);
}
@ -200,7 +202,9 @@ contract Miner is EnsResolve {
"Invalid withdrawal proof"
);
insertAccountRoot(_args.account.inputRoot == getLastAccountRoot() ? _args.account.outputRoot : _treeUpdateArgs.newRoot);
insertAccountRoot(
_args.account.inputRoot == getLastAccountRoot() ? _args.account.outputRoot : _treeUpdateArgs.newRoot
);
accountNullifiers[_args.account.inputNullifierHash] = true;
// allow submitting noop withdrawals (amount == 0)
uint256 amount = _args.amount.sub(_args.extData.fee, "Amount should be greater than fee");
@ -258,7 +262,11 @@ contract Miner is EnsResolve {
return keccak256(_data) & 0x00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
}
function validateTreeUpdate(bytes memory _proof, TreeUpdateArgs memory _args, bytes32 _commitment) internal view {
function validateTreeUpdate(
bytes memory _proof,
TreeUpdateArgs memory _args,
bytes32 _commitment
) internal view {
require(_proof.length > 0, "Outdated account merkle root");
require(_args.oldRoot == getLastAccountRoot(), "Outdated tree update merkle root");
require(_args.leaf == _commitment, "Incorrect commitment inserted");

@ -40,7 +40,13 @@ contract RewardSwap is EnsResolve {
_;
}
constructor(bytes32 _torn, bytes32 _miner, uint256 _miningCap, uint256 _initialLiquidity, uint256 _poolWeight) public {
constructor(
bytes32 _torn,
bytes32 _miner,
uint256 _miningCap,
uint256 _initialLiquidity,
uint256 _poolWeight
) public {
require(_initialLiquidity <= _miningCap, "Initial liquidity should be lower than mining cap");
torn = IERC20(resolve(_torn));
miner = resolve(_miner);

@ -51,7 +51,11 @@ contract TornadoProxy {
}
}
function deposit(ITornadoInstance _tornado, bytes32 _commitment, bytes calldata _encryptedNote) public payable virtual {
function deposit(
ITornadoInstance _tornado,
bytes32 _commitment,
bytes calldata _encryptedNote
) public payable virtual {
Instance memory instance = instances[_tornado];
require(instance.state != InstanceState.DISABLED, "The instance is not supported");

@ -6,7 +6,11 @@ import "./interfaces/ITornadoInstance.sol";
contract TornadoProxyLight {
event EncryptedNote(address indexed sender, bytes encryptedNote);
function deposit(ITornadoInstance _tornado, bytes32 _commitment, bytes calldata _encryptedNote) external payable {
function deposit(
ITornadoInstance _tornado,
bytes32 _commitment,
bytes calldata _encryptedNote
) external payable {
_tornado.deposit{ value: msg.value }(_commitment);
emit EncryptedNote(msg.sender, _encryptedNote);
}

@ -145,7 +145,8 @@ library FloatMath {
function muli(int128 x, int256 y) internal pure returns (int256) {
if (x == MIN_64x64) {
require(
y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF && y <= 0x1000000000000000000000000000000000000000000000000
y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&
y <= 0x1000000000000000000000000000000000000000000000000
);
return -y << 63;
} else {

@ -0,0 +1,59 @@
// https://tornado.cash
/*
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./Tornado.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
contract ERC20Tornado is Tornado {
using SafeERC20 for IERC20;
IERC20 public token;
constructor(
IVerifier _verifier,
IHasher _hasher,
uint256 _denomination,
uint32 _merkleTreeHeight,
IERC20 _token
) Tornado(_verifier, _hasher, _denomination, _merkleTreeHeight) {
token = _token;
}
function _processDeposit() internal override {
require(msg.value == 0, "ETH value is supposed to be 0 for ERC20 instance");
token.safeTransferFrom(msg.sender, address(this), denomination);
}
function _processWithdraw(
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) internal virtual override {
require(msg.value == _refund, "Incorrect refund amount received by the contract");
token.safeTransfer(_recipient, denomination - _fee);
if (_fee > 0) {
token.safeTransfer(_relayer, _fee);
}
if (_refund > 0) {
(bool success, ) = _recipient.call{ value: _refund }("");
if (!success) {
// let's return _refund back to the relayer
_relayer.transfer(_refund);
}
}
}
}

@ -0,0 +1,46 @@
// https://tornado.cash
/*
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./Tornado.sol";
contract ETHTornado is Tornado {
constructor(
IVerifier _verifier,
IHasher _hasher,
uint256 _denomination,
uint32 _merkleTreeHeight
) Tornado(_verifier, _hasher, _denomination, _merkleTreeHeight) {}
function _processDeposit() internal override {
require(msg.value == denomination, "Please send `mixDenomination` ETH along with transaction");
}
function _processWithdraw(
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) internal override {
// sanity checks
require(msg.value == 0, "Message value is supposed to be zero for ETH instance");
require(_refund == 0, "Refund value is supposed to be zero for ETH instance");
(bool success, ) = _recipient.call{ value: denomination - _fee }("");
require(success, "payment to _recipient did not go thru");
if (_fee > 0) {
(success, ) = _relayer.call{ value: _fee }("");
require(success, "payment to _relayer did not go thru");
}
}
}

@ -0,0 +1,158 @@
// https://tornado.cash
/*
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
interface IHasher {
function MiMCSponge(uint256 in_xL, uint256 in_xR) external pure returns (uint256 xL, uint256 xR);
}
contract MerkleTreeWithHistory {
uint256 public constant FIELD_SIZE =
21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 public constant ZERO_VALUE =
21663839004416932945382355908790599225266501822907911457504978515578255421292; // = keccak256("tornado") % FIELD_SIZE
IHasher public immutable hasher;
uint32 public levels;
// the following variables are made public for easier testing and debugging and
// are not supposed to be accessed in regular code
// filledSubtrees and roots could be bytes32[size], but using mappings makes it cheaper because
// it removes index range check on every interaction
mapping(uint256 => bytes32) public filledSubtrees;
mapping(uint256 => bytes32) public roots;
uint32 public constant ROOT_HISTORY_SIZE = 30;
uint32 public currentRootIndex = 0;
uint32 public nextIndex = 0;
constructor(uint32 _levels, IHasher _hasher) {
require(_levels > 0, "_levels should be greater than zero");
require(_levels < 32, "_levels should be less than 32");
levels = _levels;
hasher = _hasher;
for (uint32 i = 0; i < _levels; i++) {
filledSubtrees[i] = zeros(i);
}
roots[0] = zeros(_levels - 1);
}
/**
@dev Hash 2 tree leaves, returns MiMC(_left, _right)
*/
function hashLeftRight(IHasher _hasher, bytes32 _left, bytes32 _right) public pure returns (bytes32) {
require(uint256(_left) < FIELD_SIZE, "_left should be inside the field");
require(uint256(_right) < FIELD_SIZE, "_right should be inside the field");
uint256 R = uint256(_left);
uint256 C = 0;
(R, C) = _hasher.MiMCSponge(R, C);
R = addmod(R, uint256(_right), FIELD_SIZE);
(R, C) = _hasher.MiMCSponge(R, C);
return bytes32(R);
}
function _insert(bytes32 _leaf) internal returns (uint32 index) {
uint32 _nextIndex = nextIndex;
require(_nextIndex != uint32(2) ** levels, "Merkle tree is full. No more leaves can be added");
uint32 currentIndex = _nextIndex;
bytes32 currentLevelHash = _leaf;
bytes32 left;
bytes32 right;
for (uint32 i = 0; i < levels; i++) {
if (currentIndex % 2 == 0) {
left = currentLevelHash;
right = zeros(i);
filledSubtrees[i] = currentLevelHash;
} else {
left = filledSubtrees[i];
right = currentLevelHash;
}
currentLevelHash = hashLeftRight(hasher, left, right);
currentIndex /= 2;
}
uint32 newRootIndex = (currentRootIndex + 1) % ROOT_HISTORY_SIZE;
currentRootIndex = newRootIndex;
roots[newRootIndex] = currentLevelHash;
nextIndex = _nextIndex + 1;
return _nextIndex;
}
/**
@dev Whether the root is present in the root history
*/
function isKnownRoot(bytes32 _root) public view returns (bool) {
if (_root == 0) {
return false;
}
uint32 _currentRootIndex = currentRootIndex;
uint32 i = _currentRootIndex;
do {
if (_root == roots[i]) {
return true;
}
if (i == 0) {
i = ROOT_HISTORY_SIZE;
}
i--;
} while (i != _currentRootIndex);
return false;
}
/**
@dev Returns the last root
*/
function getLastRoot() public view returns (bytes32) {
return roots[currentRootIndex];
}
/// @dev provides Zero (Empty) elements for a MiMC MerkleTree. Up to 32 levels
function zeros(uint256 i) public pure returns (bytes32) {
if (i == 0) return bytes32(0x2fe54c60d3acabf3343a35b6eba15db4821b340f76e741e2249685ed4899af6c);
else if (i == 1) return bytes32(0x256a6135777eee2fd26f54b8b7037a25439d5235caee224154186d2b8a52e31d);
else if (i == 2) return bytes32(0x1151949895e82ab19924de92c40a3d6f7bcb60d92b00504b8199613683f0c200);
else if (i == 3) return bytes32(0x20121ee811489ff8d61f09fb89e313f14959a0f28bb428a20dba6b0b068b3bdb);
else if (i == 4) return bytes32(0x0a89ca6ffa14cc462cfedb842c30ed221a50a3d6bf022a6a57dc82ab24c157c9);
else if (i == 5) return bytes32(0x24ca05c2b5cd42e890d6be94c68d0689f4f21c9cec9c0f13fe41d566dfb54959);
else if (i == 6) return bytes32(0x1ccb97c932565a92c60156bdba2d08f3bf1377464e025cee765679e604a7315c);
else if (i == 7) return bytes32(0x19156fbd7d1a8bf5cba8909367de1b624534ebab4f0f79e003bccdd1b182bdb4);
else if (i == 8) return bytes32(0x261af8c1f0912e465744641409f622d466c3920ac6e5ff37e36604cb11dfff80);
else if (i == 9) return bytes32(0x0058459724ff6ca5a1652fcbc3e82b93895cf08e975b19beab3f54c217d1c007);
else if (i == 10) return bytes32(0x1f04ef20dee48d39984d8eabe768a70eafa6310ad20849d4573c3c40c2ad1e30);
else if (i == 11) return bytes32(0x1bea3dec5dab51567ce7e200a30f7ba6d4276aeaa53e2686f962a46c66d511e5);
else if (i == 12) return bytes32(0x0ee0f941e2da4b9e31c3ca97a40d8fa9ce68d97c084177071b3cb46cd3372f0f);
else if (i == 13) return bytes32(0x1ca9503e8935884501bbaf20be14eb4c46b89772c97b96e3b2ebf3a36a948bbd);
else if (i == 14) return bytes32(0x133a80e30697cd55d8f7d4b0965b7be24057ba5dc3da898ee2187232446cb108);
else if (i == 15) return bytes32(0x13e6d8fc88839ed76e182c2a779af5b2c0da9dd18c90427a644f7e148a6253b6);
else if (i == 16) return bytes32(0x1eb16b057a477f4bc8f572ea6bee39561098f78f15bfb3699dcbb7bd8db61854);
else if (i == 17) return bytes32(0x0da2cb16a1ceaabf1c16b838f7a9e3f2a3a3088d9e0a6debaa748114620696ea);
else if (i == 18) return bytes32(0x24a3b3d822420b14b5d8cb6c28a574f01e98ea9e940551d2ebd75cee12649f9d);
else if (i == 19) return bytes32(0x198622acbd783d1b0d9064105b1fc8e4d8889de95c4c519b3f635809fe6afc05);
else if (i == 20) return bytes32(0x29d7ed391256ccc3ea596c86e933b89ff339d25ea8ddced975ae2fe30b5296d4);
else if (i == 21) return bytes32(0x19be59f2f0413ce78c0c3703a3a5451b1d7f39629fa33abd11548a76065b2967);
else if (i == 22) return bytes32(0x1ff3f61797e538b70e619310d33f2a063e7eb59104e112e95738da1254dc3453);
else if (i == 23) return bytes32(0x10c16ae9959cf8358980d9dd9616e48228737310a10e2b6b731c1a548f036c48);
else if (i == 24) return bytes32(0x0ba433a63174a90ac20992e75e3095496812b652685b5e1a2eae0b1bf4e8fcd1);
else if (i == 25) return bytes32(0x019ddb9df2bc98d987d0dfeca9d2b643deafab8f7036562e627c3667266a044c);
else if (i == 26) return bytes32(0x2d3c88b23175c5a5565db928414c66d1912b11acf974b2e644caaac04739ce99);
else if (i == 27) return bytes32(0x2eab55f6ae4e66e32c5189eed5c470840863445760f5ed7e7b69b2a62600f354);
else if (i == 28) return bytes32(0x002df37a2642621802383cf952bf4dd1f32e05433beeb1fd41031fb7eace979d);
else if (i == 29) return bytes32(0x104aeb41435db66c3e62feccc1d6f5d98d0a0ed75d1374db457cf462e3a1f427);
else if (i == 30) return bytes32(0x1f3c6fd858e9a7d4b0d1f38e256a09d81d5a5e3c963987e2d4b814cfab7c6ebb);
else if (i == 31) return bytes32(0x2c7a07d20dff79d01fecedc1134284a8d08436606c93693b67e333f671bf69cc);
else revert("Index out of bounds");
}
}

@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
contract BadRecipient {
fallback() external {
require(false, "this contract does not accept ETH");
}
}

@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract ERC20Mock is ERC20("DAIMock", "DAIM") {
function mint(address account, uint256 amount) public {
_mint(account, amount);
}
}

@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
interface IDeployer {
function deploy(bytes memory _initCode, bytes32 _salt) external returns (address payable createdContract);
}

@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
interface ERC20Basic {
function _totalSupply() external returns (uint256);
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function transfer(address to, uint256 value) external;
event Transfer(address indexed from, address indexed to, uint256 value);
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
interface IUSDT is ERC20Basic {
function allowance(address owner, address spender) external view returns (uint256);
function transferFrom(address from, address to, uint256 value) external;
function approve(address spender, uint256 value) external;
event Approval(address indexed owner, address indexed spender, uint256 value);
}

@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "../MerkleTreeWithHistory.sol";
contract MerkleTreeWithHistoryMock is MerkleTreeWithHistory {
constructor(uint32 _treeLevels, IHasher _hasher) MerkleTreeWithHistory(_treeLevels, _hasher) {}
function insert(bytes32 _leaf) public {
_insert(_leaf);
}
}

285
contracts/core/Other.sol Normal file

@ -0,0 +1,285 @@
/**
*Submitted for verification at Etherscan.io on 2020-05-12
*/
// https://tornado.cash Verifier.sol generated by trusted setup ceremony.
/*
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
// Copyright 2017 Christian Reitwiessner
// 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.
// 2019 OKIMS
pragma solidity 0.5.17;
library Pairing {
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
struct G1Point {
uint256 X;
uint256 Y;
}
// Encoding of field elements is: X[0] * z + X[1]
struct G2Point {
uint256[2] X;
uint256[2] Y;
}
/*
* @return The negation of p, i.e. p.plus(p.negate()) should be zero.
*/
function negate(G1Point memory p) internal pure returns (G1Point memory) {
// The prime q in the base field F_q for G1
if (p.X == 0 && p.Y == 0) {
return G1Point(0, 0);
} else {
return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q));
}
}
/*
* @return r the sum of two points of G1
*/
function plus(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
uint256[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = p2.Y;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "pairing-add-failed");
}
/*
* @return r the product of a point on G1 and a scalar, i.e.
* p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all
* points p.
*/
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
uint256[3] memory input;
input[0] = p.X;
input[1] = p.Y;
input[2] = s;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "pairing-mul-failed");
}
/* @return The result of computing the pairing check
* e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
* For example,
* pairing([P1(), P1().negate()], [P2(), P2()]) should return true.
*/
function pairing(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2,
G1Point memory c1,
G2Point memory c2,
G1Point memory d1,
G2Point memory d2
) internal view returns (bool) {
G1Point[4] memory p1 = [a1, b1, c1, d1];
G2Point[4] memory p2 = [a2, b2, c2, d2];
uint256 inputSize = 24;
uint256[] memory input = new uint256[](inputSize);
for (uint256 i = 0; i < 4; i++) {
uint256 j = i * 6;
input[j + 0] = p1[i].X;
input[j + 1] = p1[i].Y;
input[j + 2] = p2[i].X[0];
input[j + 3] = p2[i].X[1];
input[j + 4] = p2[i].Y[0];
input[j + 5] = p2[i].Y[1];
}
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "pairing-opcode-failed");
return out[0] != 0;
}
}
contract Verifier {
uint256 constant SNARK_SCALAR_FIELD =
21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
using Pairing for *;
struct VerifyingKey {
Pairing.G1Point alfa1;
Pairing.G2Point beta2;
Pairing.G2Point gamma2;
Pairing.G2Point delta2;
Pairing.G1Point[7] IC;
}
struct Proof {
Pairing.G1Point A;
Pairing.G2Point B;
Pairing.G1Point C;
}
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
vk.alfa1 = Pairing.G1Point(
uint256(20692898189092739278193869274495556617788530808486270118371701516666252877969),
uint256(11713062878292653967971378194351968039596396853904572879488166084231740557279)
);
vk.beta2 = Pairing.G2Point(
[
uint256(12168528810181263706895252315640534818222943348193302139358377162645029937006),
uint256(281120578337195720357474965979947690431622127986816839208576358024608803542)
],
[
uint256(16129176515713072042442734839012966563817890688785805090011011570989315559913),
uint256(9011703453772030375124466642203641636825223906145908770308724549646909480510)
]
);
vk.gamma2 = Pairing.G2Point(
[
uint256(11559732032986387107991004021392285783925812861821192530917403151452391805634),
uint256(10857046999023057135944570762232829481370756359578518086990519993285655852781)
],
[
uint256(4082367875863433681332203403145435568316851327593401208105741076214120093531),
uint256(8495653923123431417604973247489272438418190587263600148770280649306958101930)
]
);
vk.delta2 = Pairing.G2Point(
[
uint256(21280594949518992153305586783242820682644996932183186320680800072133486887432),
uint256(150879136433974552800030963899771162647715069685890547489132178314736470662)
],
[
uint256(1081836006956609894549771334721413187913047383331561601606260283167615953295),
uint256(11434086686358152335540554643130007307617078324975981257823476472104616196090)
]
);
vk.IC[0] = Pairing.G1Point(
uint256(16225148364316337376768119297456868908427925829817748684139175309620217098814),
uint256(5167268689450204162046084442581051565997733233062478317813755636162413164690)
);
vk.IC[1] = Pairing.G1Point(
uint256(12882377842072682264979317445365303375159828272423495088911985689463022094260),
uint256(19488215856665173565526758360510125932214252767275816329232454875804474844786)
);
vk.IC[2] = Pairing.G1Point(
uint256(13083492661683431044045992285476184182144099829507350352128615182516530014777),
uint256(602051281796153692392523702676782023472744522032670801091617246498551238913)
);
vk.IC[3] = Pairing.G1Point(
uint256(9732465972180335629969421513785602934706096902316483580882842789662669212890),
uint256(2776526698606888434074200384264824461688198384989521091253289776235602495678)
);
vk.IC[4] = Pairing.G1Point(
uint256(8586364274534577154894611080234048648883781955345622578531233113180532234842),
uint256(21276134929883121123323359450658320820075698490666870487450985603988214349407)
);
vk.IC[5] = Pairing.G1Point(
uint256(4910628533171597675018724709631788948355422829499855033965018665300386637884),
uint256(20532468890024084510431799098097081600480376127870299142189696620752500664302)
);
vk.IC[6] = Pairing.G1Point(
uint256(15335858102289947642505450692012116222827233918185150176888641903531542034017),
uint256(5311597067667671581646709998171703828965875677637292315055030353779531404812)
);
}
/*
* @returns Whether the proof is valid given the hardcoded verifying key
* above and the public inputs
*/
function verifyProof(bytes memory proof, uint256[6] memory input) public view returns (bool) {
uint256[8] memory p = abi.decode(proof, (uint256[8]));
// Make sure that each element in the proof is less than the prime q
for (uint8 i = 0; i < p.length; i++) {
require(p[i] < PRIME_Q, "verifier-proof-element-gte-prime-q");
}
Proof memory _proof;
_proof.A = Pairing.G1Point(p[0], p[1]);
_proof.B = Pairing.G2Point([p[2], p[3]], [p[4], p[5]]);
_proof.C = Pairing.G1Point(p[6], p[7]);
VerifyingKey memory vk = verifyingKey();
// Compute the linear combination vk_x
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
vk_x = Pairing.plus(vk_x, vk.IC[0]);
// Make sure that every input is less than the snark scalar field
for (uint256 i = 0; i < input.length; i++) {
require(input[i] < SNARK_SCALAR_FIELD, "verifier-gte-snark-scalar-field");
vk_x = Pairing.plus(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
}
return
Pairing.pairing(
Pairing.negate(_proof.A),
_proof.B,
vk.alfa1,
vk.beta2,
vk_x,
vk.gamma2,
_proof.C,
vk.delta2
);
}
}

123
contracts/core/Tornado.sol Normal file

@ -0,0 +1,123 @@
// https://tornado.cash
/*
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./MerkleTreeWithHistory.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
interface IVerifier {
function verifyProof(bytes memory _proof, uint256[6] memory _input) external returns (bool);
}
abstract contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
IVerifier public immutable verifier;
uint256 public denomination;
mapping(bytes32 => bool) public nullifierHashes;
// we store all commitments just to prevent accidental deposits with the same commitment
mapping(bytes32 => bool) public commitments;
event Deposit(bytes32 indexed commitment, uint32 leafIndex, uint256 timestamp);
event Withdrawal(address to, bytes32 nullifierHash, address indexed relayer, uint256 fee);
/**
@dev The constructor
@param _verifier the address of SNARK verifier for this contract
@param _hasher the address of MiMC hash contract
@param _denomination transfer amount for each deposit
@param _merkleTreeHeight the height of deposits' Merkle Tree
*/
constructor(
IVerifier _verifier,
IHasher _hasher,
uint256 _denomination,
uint32 _merkleTreeHeight
) MerkleTreeWithHistory(_merkleTreeHeight, _hasher) {
require(_denomination > 0, "denomination should be greater than 0");
verifier = _verifier;
denomination = _denomination;
}
/**
@dev Deposit funds into the contract. The caller must send (for ETH) or approve (for ERC20) value equal to or `denomination` of this instance.
@param _commitment the note commitment, which is PedersenHash(nullifier + secret)
*/
function deposit(bytes32 _commitment) external payable nonReentrant {
require(!commitments[_commitment], "The commitment has been submitted");
uint32 insertedIndex = _insert(_commitment);
commitments[_commitment] = true;
_processDeposit();
emit Deposit(_commitment, insertedIndex, block.timestamp);
}
/** @dev this function is defined in a child contract */
function _processDeposit() internal virtual;
/**
@dev Withdraw a deposit from the contract. `proof` is a zkSNARK proof data, and input is an array of circuit public inputs
`input` array consists of:
- merkle root of all deposits in the contract
- hash of unique deposit nullifier to prevent double spends
- the recipient of funds
- optional fee that goes to the transaction sender (usually a relay)
*/
function withdraw(
bytes calldata _proof,
bytes32 _root,
bytes32 _nullifierHash,
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) external payable nonReentrant {
require(_fee <= denomination, "Fee exceeds transfer value");
require(!nullifierHashes[_nullifierHash], "The note has been already spent");
require(isKnownRoot(_root), "Cannot find your merkle root"); // Make sure to use a recent one
require(
verifier.verifyProof(
_proof,
[uint256(_root), uint256(_nullifierHash), uint256(_recipient), uint256(_relayer), _fee, _refund]
),
"Invalid withdraw proof"
);
nullifierHashes[_nullifierHash] = true;
_processWithdraw(_recipient, _relayer, _fee, _refund);
emit Withdrawal(_recipient, _nullifierHash, _relayer, _fee);
}
/** @dev this function is defined in a child contract */
function _processWithdraw(
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) internal virtual;
/** @dev whether a note is already spent */
function isSpent(bytes32 _nullifierHash) public view returns (bool) {
return nullifierHashes[_nullifierHash];
}
/** @dev whether an array of notes is already spent */
function isSpentArray(bytes32[] calldata _nullifierHashes) external view returns (bool[] memory spent) {
spent = new bool[](_nullifierHashes.length);
for (uint256 i = 0; i < _nullifierHashes.length; i++) {
if (isSpent(_nullifierHashes[i])) {
spent[i] = true;
}
}
}
}

286
contracts/core/Verifier.sol Normal file

@ -0,0 +1,286 @@
/**
*Submitted for verification at Etherscan.io on 2020-05-12
*/
// https://tornado.cash Verifier.sol generated by trusted setup ceremony.
/*
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
// SPDX-License-Identifier: MIT
// Copyright 2017 Christian Reitwiessner
// 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.
// 2019 OKIMS
pragma solidity ^0.7.0;
library Pairing {
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
struct G1Point {
uint256 X;
uint256 Y;
}
// Encoding of field elements is: X[0] * z + X[1]
struct G2Point {
uint256[2] X;
uint256[2] Y;
}
/*
* @return The negation of p, i.e. p.plus(p.negate()) should be zero.
*/
function negate(G1Point memory p) internal pure returns (G1Point memory) {
// The prime q in the base field F_q for G1
if (p.X == 0 && p.Y == 0) {
return G1Point(0, 0);
} else {
return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q));
}
}
/*
* @return r the sum of two points of G1
*/
function plus(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
uint256[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = p2.Y;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "pairing-add-failed");
}
/*
* @return r the product of a point on G1 and a scalar, i.e.
* p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all
* points p.
*/
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
uint256[3] memory input;
input[0] = p.X;
input[1] = p.Y;
input[2] = s;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "pairing-mul-failed");
}
/* @return The result of computing the pairing check
* e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
* For example,
* pairing([P1(), P1().negate()], [P2(), P2()]) should return true.
*/
function pairing(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2,
G1Point memory c1,
G2Point memory c2,
G1Point memory d1,
G2Point memory d2
) internal view returns (bool) {
G1Point[4] memory p1 = [a1, b1, c1, d1];
G2Point[4] memory p2 = [a2, b2, c2, d2];
uint256 inputSize = 24;
uint256[] memory input = new uint256[](inputSize);
for (uint256 i = 0; i < 4; i++) {
uint256 j = i * 6;
input[j + 0] = p1[i].X;
input[j + 1] = p1[i].Y;
input[j + 2] = p2[i].X[0];
input[j + 3] = p2[i].X[1];
input[j + 4] = p2[i].Y[0];
input[j + 5] = p2[i].Y[1];
}
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
// Use "invalid" to make gas estimation work
switch success
case 0 {
invalid()
}
}
require(success, "pairing-opcode-failed");
return out[0] != 0;
}
}
contract Verifier {
uint256 constant SNARK_SCALAR_FIELD =
21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
using Pairing for *;
struct VerifyingKey {
Pairing.G1Point alfa1;
Pairing.G2Point beta2;
Pairing.G2Point gamma2;
Pairing.G2Point delta2;
Pairing.G1Point[7] IC;
}
struct Proof {
Pairing.G1Point A;
Pairing.G2Point B;
Pairing.G1Point C;
}
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
vk.alfa1 = Pairing.G1Point(
uint256(20692898189092739278193869274495556617788530808486270118371701516666252877969),
uint256(11713062878292653967971378194351968039596396853904572879488166084231740557279)
);
vk.beta2 = Pairing.G2Point(
[
uint256(12168528810181263706895252315640534818222943348193302139358377162645029937006),
uint256(281120578337195720357474965979947690431622127986816839208576358024608803542)
],
[
uint256(16129176515713072042442734839012966563817890688785805090011011570989315559913),
uint256(9011703453772030375124466642203641636825223906145908770308724549646909480510)
]
);
vk.gamma2 = Pairing.G2Point(
[
uint256(11559732032986387107991004021392285783925812861821192530917403151452391805634),
uint256(10857046999023057135944570762232829481370756359578518086990519993285655852781)
],
[
uint256(4082367875863433681332203403145435568316851327593401208105741076214120093531),
uint256(8495653923123431417604973247489272438418190587263600148770280649306958101930)
]
);
vk.delta2 = Pairing.G2Point(
[
uint256(21280594949518992153305586783242820682644996932183186320680800072133486887432),
uint256(150879136433974552800030963899771162647715069685890547489132178314736470662)
],
[
uint256(1081836006956609894549771334721413187913047383331561601606260283167615953295),
uint256(11434086686358152335540554643130007307617078324975981257823476472104616196090)
]
);
vk.IC[0] = Pairing.G1Point(
uint256(16225148364316337376768119297456868908427925829817748684139175309620217098814),
uint256(5167268689450204162046084442581051565997733233062478317813755636162413164690)
);
vk.IC[1] = Pairing.G1Point(
uint256(12882377842072682264979317445365303375159828272423495088911985689463022094260),
uint256(19488215856665173565526758360510125932214252767275816329232454875804474844786)
);
vk.IC[2] = Pairing.G1Point(
uint256(13083492661683431044045992285476184182144099829507350352128615182516530014777),
uint256(602051281796153692392523702676782023472744522032670801091617246498551238913)
);
vk.IC[3] = Pairing.G1Point(
uint256(9732465972180335629969421513785602934706096902316483580882842789662669212890),
uint256(2776526698606888434074200384264824461688198384989521091253289776235602495678)
);
vk.IC[4] = Pairing.G1Point(
uint256(8586364274534577154894611080234048648883781955345622578531233113180532234842),
uint256(21276134929883121123323359450658320820075698490666870487450985603988214349407)
);
vk.IC[5] = Pairing.G1Point(
uint256(4910628533171597675018724709631788948355422829499855033965018665300386637884),
uint256(20532468890024084510431799098097081600480376127870299142189696620752500664302)
);
vk.IC[6] = Pairing.G1Point(
uint256(15335858102289947642505450692012116222827233918185150176888641903531542034017),
uint256(5311597067667671581646709998171703828965875677637292315055030353779531404812)
);
}
/*
* @returns Whether the proof is valid given the hardcoded verifying key
* above and the public inputs
*/
function verifyProof(bytes memory proof, uint256[6] memory input) public view returns (bool) {
uint256[8] memory p = abi.decode(proof, (uint256[8]));
// Make sure that each element in the proof is less than the prime q
for (uint8 i = 0; i < p.length; i++) {
require(p[i] < PRIME_Q, "verifier-proof-element-gte-prime-q");
}
Proof memory _proof;
_proof.A = Pairing.G1Point(p[0], p[1]);
_proof.B = Pairing.G2Point([p[2], p[3]], [p[4], p[5]]);
_proof.C = Pairing.G1Point(p[6], p[7]);
VerifyingKey memory vk = verifyingKey();
// Compute the linear combination vk_x
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
vk_x = Pairing.plus(vk_x, vk.IC[0]);
// Make sure that every input is less than the snark scalar field
for (uint256 i = 0; i < input.length; i++) {
require(input[i] < SNARK_SCALAR_FIELD, "verifier-gte-snark-scalar-field");
vk_x = Pairing.plus(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
}
return
Pairing.pairing(
Pairing.negate(_proof.A),
_proof.B,
vk.alfa1,
vk.beta2,
vk_x,
vk.gamma2,
_proof.C,
vk.delta2
);
}
}

@ -0,0 +1,39 @@
// https://tornado.cash
/*
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./ERC20Tornado.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract cTornado is ERC20Tornado {
address public immutable governance = 0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce;
IERC20 public immutable comp;
constructor(
IERC20 _comp,
IVerifier _verifier,
IHasher _hasher,
uint256 _denomination,
uint32 _merkleTreeHeight,
IERC20 _token
) ERC20Tornado(_verifier, _hasher, _denomination, _merkleTreeHeight, _token) {
require(address(_comp) != address(0), "Invalid COMP token address");
comp = _comp;
}
/// @dev Moves earned yield of the COMP token to the tornado governance contract
/// To make it work you might need to call `comptroller.claimComp(cPoolAddress)` first
function claimComp() external {
comp.transfer(governance, comp.balanceOf(address(this)));
}
}

@ -70,7 +70,10 @@ contract InstanceAdditionProposal {
);
// Convert the data.
IInstanceRegistry.Tornado memory tornadoForUpdate = IInstanceRegistry.Tornado(instance, newInstanceData);
IInstanceRegistry.Tornado memory tornadoForUpdate = IInstanceRegistry.Tornado(
instance,
newInstanceData
);
// Update the instance data in the registry.
instanceRegistry.updateInstance(tornadoForUpdate);

@ -21,7 +21,12 @@ contract InstanceFactory is Initializable {
uint32 public merkleTreeHeight;
event NewTreeHeightSet(uint32 indexed newTreeHeight);
event NewImplementationSet(address indexed ERC20Impl, address indexed nativeCurImpl, address verifier, address hasher);
event NewImplementationSet(
address indexed ERC20Impl,
address indexed nativeCurImpl,
address verifier,
address hasher
);
event NewInstanceCloneCreated(address indexed clone);
modifier onlyAdmin() {
@ -34,7 +39,13 @@ contract InstanceFactory is Initializable {
* @dev this contract will be deployed behind a proxy and should not assign values at logic address,
* params left out because self explainable
* */
function initialize(address _verifier, address _hasher, uint32 _merkleTreeHeight, address _admin) public initializer {
function initialize(
address _verifier,
address _hasher,
uint32 _merkleTreeHeight,
address _admin,
address router
) public initializer {
verifier = _verifier;
hasher = _hasher;
merkleTreeHeight = _merkleTreeHeight;
@ -51,7 +62,10 @@ contract InstanceFactory is Initializable {
* @param _denomination denomination of new Tornado instance
* @param _token address of ERC20 token for a new instance, if zero address, then it will be ETH
*/
function createInstanceClone(uint256 _denomination, address _token) public virtual onlyAdmin returns (address clone) {
function createInstanceClone(
uint256 _denomination,
address _token
) public virtual onlyAdmin returns (address clone) {
return _createInstanceClone(_denomination, _token);
}
@ -90,10 +104,10 @@ contract InstanceFactory is Initializable {
emit NewTreeHeightSet(merkleTreeHeight);
}
function generateNewImplementation(address _verifier, address _hasher) external onlyAdmin {
function generateNewImplementation(address _verifier, address _hasher, address _router) external onlyAdmin {
verifier = _verifier;
hasher = _hasher;
ERC20Impl = address(new ERC20TornadoCloneable(_verifier, _hasher));
ERC20Impl = address(new ERC20TornadoCloneable(_verifier, _hasher, _router));
nativeCurImpl = address(new ETHTornadoCloneable(_verifier, _hasher));
emit NewImplementationSet(ERC20Impl, nativeCurImpl, _verifier, _hasher);
}

@ -114,7 +114,10 @@ contract InstanceProposalFactory {
(, , , , uint16 observationCardinalityNext, , ) = IUniswapV3PoolState(poolAddress).slot0();
require(minObservationCardinality <= observationCardinalityNext, "Uniswap Pool observation cardinality is low.");
require(
minObservationCardinality <= observationCardinalityNext,
"Uniswap Pool observation cardinality is low."
);
}
// 4⃣ Denominations, div exponent and protocol fees must have an element at t(oken). The exponent must be good.

@ -10,7 +10,10 @@ contract SidechainInstanceFactory is InstanceFactory {
* @param _denomination denomination of new Tornado instance
* @param _token address of ERC20 token for a new instance, if zero address, then it will be ETH
*/
function createInstanceClone(uint256 _denomination, address _token) public override returns (address clone) {
function createInstanceClone(
uint256 _denomination,
address _token
) public override returns (address clone) {
return _createInstanceClone(_denomination, _token);
}
@ -19,7 +22,10 @@ contract SidechainInstanceFactory is InstanceFactory {
* @param _token address of ERC20 token for a new instance
* @param _denominations list of denominations for each new instance
*/
function createInstanceClones(address _token, uint256[] memory _denominations) external returns (address[] memory) {
function createInstanceClones(
address _token,
uint256[] memory _denominations
) external returns (address[] memory) {
address[] memory newClones = new address[](_denominations.length);
for (uint256 i = 0; i < _denominations.length; i++) {
newClones[i] = _createInstanceClone(_denominations[i], _token);

@ -3,17 +3,82 @@
pragma solidity 0.7.6;
pragma abicoder v2;
import "tornado-core/contracts/ERC20Tornado.sol";
// Local imports
import "../core/ERC20Tornado.sol";
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTERFACES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
interface ITornadoRouter {
function relayerRegistry() external view returns (address);
}
interface IRelayerRegistry {
function burn(address, address, address) external;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CONTRACT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/**
* @title ERC20TornadoCloneable
* @author AlienTornadosaurusHex
* @notice A cloneable ERC20 instances version which must be called (only by relayers) through the router (as
long as registry not dead or we don't disable, what the intention is).
*/
contract ERC20TornadoCloneable is ERC20Tornado {
constructor(address verifier, address hasher) ERC20Tornado(IVerifier(verifier), IHasher(hasher), 1, 1, IERC20(address(0))) {}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
function init(uint256 _denomination, uint32 _merkleTreeHeight, address _token) external {
/** @notice The address of the router which may call this contract's withdraw function (unless registry
dead) */
address public router;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
constructor(
address verifier,
address hasher
) ERC20Tornado(IVerifier(verifier), IHasher(hasher), 1, 1, IERC20(address(0))) {}
/**
* @notice This initialization function follows the logic of the original
instance contructors, meaning ERC20Tornado => Tornado =>
MerkleTreeWithHistory. It includes the appropriate checks which ensure that
initialization may only be done once. It also initializes the address of
the router such that we can, until the relayer registry is not dead, force
transactions through the router (more in the function below on this).
* @param _router The address of the TornadoRouter.
* @param _token The token of the instance.
* @param _denomination The denomination of the instance.
* @param _merkleTreeHeight The merkle tree height of the instance. Usually
20.
*/
function initialize(
address _router,
address _token,
uint256 _denomination,
uint32 _merkleTreeHeight
) external {
// Part of ERC20TornadoCloneable.sol, denomination and levels is obviously
// in all older code set once and never changed again, as such, we can use
// these for `initialize`. The question comes, why is it set to 1 above?
// This only sets in the local implementation address the state of the
// variables to 1, why the original devs chose 1 instead of 0, is because
// internal requires need it to be above 0 for the constructor not to
// revert.
require(denomination == 0 && levels == 0, "already initialized");
// Part of ERC20Tornado.sol constructor
token = IERC20(_token);
// Part of the Tornado.sol constructor,
// Note that the verifier has already been set as an immutable
require(_denomination > 0, "denomination should be greater than 0");
denomination = _denomination;
// Part of the MerkleTreeWithHistory.sol constructor The merkle tree height
// is the same thing as levels, meaning the levels of the merkle... binary
// tree. The variable is properly assigned. Node that just like the
// verifier, the hasher is properly assigned as an immutable.
require(_merkleTreeHeight > 0, "_levels should be greater than zero");
require(_merkleTreeHeight < 32, "_levels should be less than 32");
levels = _merkleTreeHeight;
@ -23,5 +88,54 @@ contract ERC20TornadoCloneable is ERC20Tornado {
}
roots[0] = zeros(_merkleTreeHeight - 1);
// Part of ERC20TornadoCloneable.sol
router = _router;
}
/**
* @notice This function is a permissionless function which, if the registry is dead, immediately downgrades
the address of the registry to address(0), which makes the instance function as any of the older
instances.
* @dev We will disable the current registry and call this function once we deploy a new registry
infrastructure system which will require the relayers to have a wallet-like smart contract, because then
frontends will be able to build proofs for it, and as such there will be no need for this bullshit.
*/
function checkRelayerRegistryIsDead() external {
require(router != address(0), "relayer registry already dead");
try
IRelayerRegistry(ITornadoRouter(router).relayerRegistry()).burn(address(this), address(0), address(0))
{
require(false, "registry not dead yet");
} catch {
router = address(0);
}
}
/**
* @notice Alright so first of all, the contract is still permissionless with
this function. If the `_relayer` field is set to address(0), transactions
will still work (because of the router). This means, that what this blocks
is relayers which are not registered from processing transactions, while
manual user deposits and transactions still work. This is done because, the
entire intention of the system was to make it economically (similarly to
proof of stake) sybil resistant, but the system has the issue that it can
be avoided. So, this is a temporary fix for this until we don't make a full
system upgrade after which will we disable this trash by disabling the registry
and calling `checkRelayerRegistryIsDead()`.
* @param _recipient The recipient address of the withdraw.
* @param _relayer The relayer address of the withdraw. Must be a registered
relayer otherwise router reverts.
* @param _fee The fee in bips.
* @param _refund The refund in amount * decimals.
*/
function _processWithdraw(
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) internal override {
if (router != address(0)) require(msg.sender == router, "if registry not dead, router must be caller");
super._processWithdraw(_recipient, _relayer, _fee, _refund);
}
}

@ -3,16 +3,50 @@
pragma solidity 0.7.6;
pragma abicoder v2;
import "tornado-core/contracts/ETHTornado.sol";
// Local imports
import "../core/ETHTornado.sol";
/**
* @title ETHTornadoCloneable
* @author AlienTornadosaurusHex
* @notice A cloneable ETH instances version.
*/
contract ETHTornadoCloneable is ETHTornado {
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
constructor(address verifier, address hasher) ETHTornado(IVerifier(verifier), IHasher(hasher), 1, 1) {}
function init(uint256 _denomination, uint32 _merkleTreeHeight) external {
/**
* @notice This initialization function follows the logic of the original
instance contructors, meaning ETHTornado => Tornado =>
MerkleTreeWithHistory. It includes the appropriate checks which ensure that
initialization may only be done once. It also initializes the address of
the router such that we can, until the relayer registry is not dead, force
transactions through the router (more in the function below on this).
* @param _denomination The denomination of the instance.
* @param _merkleTreeHeight The merkle tree height of the instance. Usually
20.
*/
function initialize(uint256 _denomination, uint32 _merkleTreeHeight) external {
// Part of ERC20TornadoCloneable.sol, denomination and levels is obviously
// in all older code set once and never changed again, as such, we can use
// these for `initialize`. The question comes, why is it set to 1 above?
// This only sets in the local implementation address the state of the
// variables to 1, why the original devs chose 1 instead of 0, is because
// internal requires need it to be above 0 for the constructor not to
// revert.
require(denomination == 0 && levels == 0, "already initialized");
// Part of the Tornado.sol constructor, ETHTornado.sol doesn't do anything
// Note that the verifier has already been set as an immutable
require(_denomination > 0, "denomination should be greater than 0");
denomination = _denomination;
// Part of the MerkleTreeWithHistory.sol constructor The merkle tree height
// is the same thing as levels, meaning the levels of the merkle... binary
// tree. The variable is properly assigned. Node that just like the
// verifier, the hasher is properly assigned as an immutable.
require(_merkleTreeHeight > 0, "_levels should be greater than zero");
require(_merkleTreeHeight < 32, "_levels should be less than 32");
levels = _merkleTreeHeight;

@ -75,7 +75,13 @@ contract RelayerRegistry is Initializable, EnsResolve {
_;
}
constructor(address _torn, address _governance, address _ens, bytes32 _staking, bytes32 _feeManager) public {
constructor(
address _torn,
address _governance,
address _ens,
bytes32 _staking,
bytes32 _feeManager
) public {
torn = TORN(_torn);
governance = _governance;
ens = IENS(_ens);
@ -119,7 +125,12 @@ contract RelayerRegistry is Initializable, EnsResolve {
_register(relayer, ensName, stake, workersToRegister);
}
function _register(address relayer, string calldata ensName, uint256 stake, address[] calldata workersToRegister) internal {
function _register(
address relayer,
string calldata ensName,
uint256 stake,
address[] calldata workersToRegister
) internal {
bytes32 ensHash = bytes(ensName).namehash();
require(relayer == ens.owner(ensHash), "only ens owner");
require(workers[relayer] == address(0), "cant register again");

@ -13,7 +13,8 @@ interface IERC20Decimals {
library UniswapV3OracleHelper {
using LowGasSafeMath for uint256;
IUniswapV3Factory internal constant UniswapV3Factory = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984);
IUniswapV3Factory internal constant UniswapV3Factory =
IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984);
address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
uint256 internal constant RATIO_DIVIDER = 1e18;
@ -77,8 +78,13 @@ library UniswapV3OracleHelper {
* @param period the amount of seconds we are going to look into the past for the new token price
* @return returns the price of token[0] in token[1]
* */
function getPriceRatioOfTokens(address[2] memory tokens, uint24[2] memory fees, uint32 period) internal view returns (uint256) {
function getPriceRatioOfTokens(
address[2] memory tokens,
uint24[2] memory fees,
uint32 period
) internal view returns (uint256) {
return
getPriceOfTokenInWETH(tokens[0], fees[0], period).mul(RATIO_DIVIDER) / getPriceOfTokenInWETH(tokens[1], fees[1], period);
getPriceOfTokenInWETH(tokens[0], fees[0], period).mul(RATIO_DIVIDER) /
getPriceOfTokenInWETH(tokens[1], fees[1], period);
}
}

@ -84,7 +84,10 @@ contract TornadoStakingRewards is Initializable, EnsResolve {
* @param account address of account to update data for
* @param amountLockedBeforehand the balance locked beforehand in the governance contract
* */
function updateRewardsOnLockedBalanceChange(address account, uint256 amountLockedBeforehand) external onlyGovernance {
function updateRewardsOnLockedBalanceChange(
address account,
uint256 amountLockedBeforehand
) external onlyGovernance {
uint256 claimed = _updateReward(account, amountLockedBeforehand);
accumulatedRewards[account] = accumulatedRewards[account].add(claimed);
}
@ -109,9 +112,9 @@ contract TornadoStakingRewards is Initializable, EnsResolve {
*/
function _updateReward(address account, uint256 amountLockedBeforehand) private returns (uint256 claimed) {
if (amountLockedBeforehand != 0)
claimed = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account])).mul(amountLockedBeforehand).div(
ratioConstant
);
claimed = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account]))
.mul(amountLockedBeforehand)
.div(ratioConstant);
accumulatedRewardRateOnLastUpdate[account] = accumulatedRewardPerTorn;
emit RewardsUpdated(account, claimed);
}
@ -123,7 +126,9 @@ contract TornadoStakingRewards is Initializable, EnsResolve {
function checkReward(address account) external view returns (uint256 rewards) {
uint256 amountLocked = Governance.lockedBalance(account);
if (amountLocked != 0)
rewards = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account])).mul(amountLocked).div(ratioConstant);
rewards = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account]))
.mul(amountLocked)
.div(ratioConstant);
rewards = rewards.add(accumulatedRewards[account]);
}
}

@ -93,7 +93,8 @@ contract FeeManager is EnsResolve {
* @return newFee the new fee pool
*/
function calculatePoolFee(ITornadoInstance _instance) public view returns (uint160) {
(bool isERC20, IERC20 token, , uint24 uniswapPoolSwappingFee, uint32 protocolFeePercentage) = registry.instances(_instance);
(bool isERC20, IERC20 token, , uint24 uniswapPoolSwappingFee, uint32 protocolFeePercentage) = registry
.instances(_instance);
if (protocolFeePercentage == 0) {
return 0;
}

@ -36,8 +36,14 @@ contract TornadoRouter is EnsResolve {
relayerRegistry = RelayerRegistry(resolve(_relayerRegistry));
}
function deposit(ITornadoInstance _tornado, bytes32 _commitment, bytes calldata _encryptedNote) public payable virtual {
(bool isERC20, IERC20 token, InstanceRegistry.InstanceState state, , ) = instanceRegistry.instances(_tornado);
function deposit(
ITornadoInstance _tornado,
bytes32 _commitment,
bytes calldata _encryptedNote
) public payable virtual {
(bool isERC20, IERC20 token, InstanceRegistry.InstanceState state, , ) = instanceRegistry.instances(
_tornado
);
require(state != InstanceRegistry.InstanceState.DISABLED, "The instance is not supported");
if (isERC20) {

@ -10,7 +10,10 @@ contract AdminUpgradeableProxy is TransparentUpgradeableProxy {
/**
* @dev Initializes an upgradeable proxy backed by the implementation at `_logic`.
*/
constructor(address _logic, bytes memory _data) public payable TransparentUpgradeableProxy(_logic, msg.sender, _data) {}
constructor(
address _logic,
bytes memory _data
) public payable TransparentUpgradeableProxy(_logic, msg.sender, _data) {}
/**
* @dev Override to allow admin access the fallback function.

@ -22,7 +22,8 @@ contract TornadoTrees is Initializable {
uint256 public constant CHUNK_SIZE = 2 ** CHUNK_TREE_HEIGHT;
uint256 public constant ITEM_SIZE = 32 + 20 + 4;
uint256 public constant BYTES_SIZE = 32 + 32 + 4 + CHUNK_SIZE * ITEM_SIZE;
uint256 public constant SNARK_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 public constant SNARK_FIELD =
21888242871839275222246405745257275088548364400416034343698204186575808495617;
mapping(uint256 => bytes32) public deposits;
uint256 public depositsLength;
@ -60,7 +61,11 @@ contract TornadoTrees is Initializable {
uint256 withdrawalsStep;
}
constructor(address _governance, ITornadoTreesV1 _tornadoTreesV1, SearchParams memory _searchParams) public {
constructor(
address _governance,
ITornadoTreesV1 _tornadoTreesV1,
SearchParams memory _searchParams
) public {
governance = _governance;
tornadoTreesV1 = _tornadoTreesV1;
@ -79,7 +84,10 @@ contract TornadoTrees is Initializable {
);
}
function initialize(address _tornadoProxy, IBatchTreeUpdateVerifier _treeUpdateVerifier) public initializer onlyGovernance {
function initialize(
address _tornadoProxy,
IBatchTreeUpdateVerifier _treeUpdateVerifier
) public initializer onlyGovernance {
tornadoProxy = _tornadoProxy;
treeUpdateVerifier = _treeUpdateVerifier;
@ -130,9 +138,15 @@ contract TornadoTrees is Initializable {
mstore(add(data, 0x20), _currentRoot)
}
for (uint256 i = 0; i < CHUNK_SIZE; i++) {
(bytes32 hash, address instance, uint32 blockNumber) = (_events[i].hash, _events[i].instance, _events[i].block);
(bytes32 hash, address instance, uint32 blockNumber) = (
_events[i].hash,
_events[i].instance,
_events[i].block
);
bytes32 leafHash = keccak256(abi.encode(instance, hash, blockNumber));
bytes32 deposit = offset + i >= depositsV1Length ? deposits[offset + i] : tornadoTreesV1.deposits(offset + i);
bytes32 deposit = offset + i >= depositsV1Length
? deposits[offset + i]
: tornadoTreesV1.deposits(offset + i);
require(leafHash == deposit, "Incorrect deposit");
assembly {
mstore(add(add(data, mul(ITEM_SIZE, i)), 0x7c), blockNumber)
@ -175,9 +189,15 @@ contract TornadoTrees is Initializable {
mstore(add(data, 0x20), _currentRoot)
}
for (uint256 i = 0; i < CHUNK_SIZE; i++) {
(bytes32 hash, address instance, uint32 blockNumber) = (_events[i].hash, _events[i].instance, _events[i].block);
(bytes32 hash, address instance, uint32 blockNumber) = (
_events[i].hash,
_events[i].instance,
_events[i].block
);
bytes32 leafHash = keccak256(abi.encode(instance, hash, blockNumber));
bytes32 withdrawal = offset + i >= withdrawalsV1Length ? withdrawals[offset + i] : tornadoTreesV1.withdrawals(offset + i);
bytes32 withdrawal = offset + i >= withdrawalsV1Length
? withdrawals[offset + i]
: tornadoTreesV1.withdrawals(offset + i);
require(leafHash == withdrawal, "Incorrect withdrawal");
assembly {
mstore(add(add(data, mul(ITEM_SIZE, i)), 0x7c), blockNumber)
@ -201,8 +221,14 @@ contract TornadoTrees is Initializable {
}
function validateRoots(bytes32 _depositRoot, bytes32 _withdrawalRoot) public view {
require(_depositRoot == depositRoot || _depositRoot == previousDepositRoot, "Incorrect deposit tree root");
require(_withdrawalRoot == withdrawalRoot || _withdrawalRoot == previousWithdrawalRoot, "Incorrect withdrawal tree root");
require(
_depositRoot == depositRoot || _depositRoot == previousDepositRoot,
"Incorrect deposit tree root"
);
require(
_withdrawalRoot == withdrawalRoot || _withdrawalRoot == previousWithdrawalRoot,
"Incorrect withdrawal tree root"
);
}
/// @dev There is no array length getter for deposit and withdrawal arrays
@ -238,7 +264,11 @@ contract TornadoTrees is Initializable {
return mid + 1;
}
function elementExists(ITornadoTreesV1 _tornadoTreesV1, string memory _type, uint256 index) public view returns (bool success) {
function elementExists(
ITornadoTreesV1 _tornadoTreesV1,
string memory _type,
uint256 index
) public view returns (bool success) {
// Try to get the element. If it succeeds the array length is higher, it it reverts the length is equal or lower
(success, ) = address(_tornadoTreesV1).staticcall{ gas: 2500 }(abi.encodeWithSignature(_type, index));
}

@ -65,7 +65,11 @@ contract TornadoTreesMock is TornadoTrees {
mstore(add(data, 0x20), _oldRoot)
}
for (uint256 i = 0; i < CHUNK_SIZE; i++) {
(bytes32 hash, address instance, uint32 depositBlock) = (_events[i].hash, _events[i].instance, _events[i].block);
(bytes32 hash, address instance, uint32 depositBlock) = (
_events[i].hash,
_events[i].instance,
_events[i].block
);
assembly {
mstore(add(add(data, mul(ITEM_SIZE, i)), 0x7c), depositBlock)
mstore(add(add(data, mul(ITEM_SIZE, i)), 0x78), instance)
@ -88,7 +92,11 @@ contract TornadoTreesMock is TornadoTrees {
mstore(add(data, 0x20), _oldRoot)
}
for (uint256 i = 0; i < CHUNK_SIZE; i++) {
(bytes32 hash, address instance, uint32 depositBlock) = (_events[i].hash, _events[i].instance, _events[i].block);
(bytes32 hash, address instance, uint32 depositBlock) = (
_events[i].hash,
_events[i].instance,
_events[i].block
);
assembly {
mstore(add(add(data, mul(ITEM_SIZE, i)), 0x7c), depositBlock)
mstore(add(add(data, mul(ITEM_SIZE, i)), 0x78), instance)

@ -11,6 +11,15 @@ require('hardhat-contract-sizer')
module.exports = {
solidity: {
compilers: [
{
version: '0.5.17',
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
{
version: '0.6.2',
settings: {

@ -21,7 +21,6 @@
"@uniswap/v3-periphery": "1.1.1",
"torn-token": "^1.0.4",
"tornado-cli": "^0.0.1",
"tornado-core": "https://github.com/tornadocash/tornado-core.git",
"tornado-governance": "2.0.0"
},
"devDependencies": {

7
scripts/deploy.js Normal file → Executable file

@ -68,6 +68,11 @@ async function main() {
config.merkleTreeHeight,
)
console.log(happyDeployedMessage('MinimalInstanceFactory', chainId, minimalFactory.address))
await hre.run('verify:verify', {
address: minimalFactory.address,
constructorArguments: [config.verifier, config.hasher, config.merkleTreeHeight],
})
} else {
return '\nDecided to stop at MinimalInstanceFactory deployment.\n'
}
@ -98,7 +103,7 @@ async function main() {
if (await prompt(promptMessageBase('Continuing to contract verification.'))) {
await hre.run('verify:verify', {
address: minimalFactory.address,
constructorArguments: [config.verifier, config.hasher, config.merkleTreeHeight],
constructorArguments: [config.verifier, config.hasher, config.merkleTreeHeight.config.router],
})
console.log(happyVerifiedMessage('MinimalInstanceFactory', minimalFactory.address))

39
scripts/instances.js Normal file

@ -0,0 +1,39 @@
require('dotenv').config()
const hre = require('hardhat')
const { ethers } = hre
async function main() {
const mf = await ethers.getContractAt(
'MinimalInstanceFactory',
'0x9d00007c0f5037157b5be8bff174b194a99118d0',
)
const crvUSD = '0xf939e0a03fb07f59a73314e73794be0e57ac1b4e'
let response = await mf.createInstanceClone(ethers.utils.parseUnits('100'), crvUSD)
await response.wait()
console.log('Deployed')
response = await mf.createInstanceClone(ethers.utils.parseUnits('1000'), crvUSD)
await response.wait()
console.log('Deployed')
response = await mf.createInstanceClone(ethers.utils.parseUnits('10000'), crvUSD)
await response.wait()
console.log('Deployed')
response = await mf.createInstanceClone(ethers.utils.parseUnits('100000'), crvUSD)
await response.wait()
console.log('Deployed')
response = await mf.createInstanceClone(ethers.utils.parseUnits('1000000'), crvUSD)
await response.wait()
console.log('Deployed')
}
main()

0
scripts/verify.js Normal file → Executable file

28794
yarn.lock

File diff suppressed because it is too large Load Diff