Force router calls for new instances
Signed-off-by: T-Hax <>
This commit is contained in:
parent
851d4ef487
commit
728eb71388
@ -8,8 +8,7 @@
|
||||
{
|
||||
"files": "*.sol",
|
||||
"options": {
|
||||
"singleQuote": false,
|
||||
"printWidth": 130
|
||||
"singleQuote": false
|
||||
}
|
||||
}
|
||||
]
|
||||
|
BIN
.yarn/install-state.gz
Normal file
BIN
.yarn/install-state.gz
Normal file
Binary file not shown.
1
.yarnrc.yml
Normal file
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 {
|
||||
|
59
contracts/core/ERC20Tornado.sol
Normal file
59
contracts/core/ERC20Tornado.sol
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
46
contracts/core/ETHTornado.sol
Normal file
46
contracts/core/ETHTornado.sol
Normal file
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
158
contracts/core/MerkleTreeWithHistory.sol
Normal file
158
contracts/core/MerkleTreeWithHistory.sol
Normal file
@ -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");
|
||||
}
|
||||
}
|
8
contracts/core/Mocks/BadRecipient.sol
Normal file
8
contracts/core/Mocks/BadRecipient.sol
Normal file
@ -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");
|
||||
}
|
||||
}
|
10
contracts/core/Mocks/ERC20Mock.sol
Normal file
10
contracts/core/Mocks/ERC20Mock.sol
Normal file
@ -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);
|
||||
}
|
||||
}
|
6
contracts/core/Mocks/IDeployer.sol
Normal file
6
contracts/core/Mocks/IDeployer.sol
Normal file
@ -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);
|
||||
}
|
29
contracts/core/Mocks/IUSDT.sol
Normal file
29
contracts/core/Mocks/IUSDT.sol
Normal file
@ -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);
|
||||
}
|
12
contracts/core/Mocks/MerkleTreeWithHistoryMock.sol
Normal file
12
contracts/core/Mocks/MerkleTreeWithHistoryMock.sol
Normal file
@ -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
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
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
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
|
||||
);
|
||||
}
|
||||
}
|
39
contracts/core/cTornado.sol
Normal file
39
contracts/core/cTornado.sol
Normal file
@ -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
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
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
0
scripts/verify.js
Normal file → Executable file
Loading…
Reference in New Issue
Block a user