TornadoProxy.sol 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity >=0.6.0 <0.8.0;
  3. pragma experimental ABIEncoderV2;
  4. import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
  5. import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
  6. import "@openzeppelin/contracts/math/Math.sol";
  7. import "./interfaces/ITornadoInstance.sol";
  8. import "./interfaces/ITornadoTrees.sol";
  9. contract TornadoProxy {
  10. using SafeERC20 for IERC20;
  11. event EncryptedNote(address indexed sender, bytes encryptedNote);
  12. event InstanceStateUpdated(ITornadoInstance indexed instance, InstanceState state);
  13. event TornadoTreesUpdated(ITornadoTrees addr);
  14. enum InstanceState { DISABLED, ENABLED, MINEABLE }
  15. struct Instance {
  16. bool isERC20;
  17. IERC20 token;
  18. InstanceState state;
  19. }
  20. struct Tornado {
  21. ITornadoInstance addr;
  22. Instance instance;
  23. }
  24. ITornadoTrees public tornadoTrees;
  25. address public immutable governance;
  26. mapping(ITornadoInstance => Instance) public instances;
  27. modifier onlyGovernance() {
  28. require(msg.sender == governance, "Not authorized");
  29. _;
  30. }
  31. constructor(
  32. address _tornadoTrees,
  33. address _governance,
  34. Tornado[] memory _instances
  35. ) public {
  36. tornadoTrees = ITornadoTrees(_tornadoTrees);
  37. governance = _governance;
  38. for (uint256 i = 0; i < _instances.length; i++) {
  39. _updateInstance(_instances[i]);
  40. }
  41. }
  42. function deposit(
  43. ITornadoInstance _tornado,
  44. bytes32 _commitment,
  45. bytes calldata _encryptedNote
  46. ) public payable virtual {
  47. Instance memory instance = instances[_tornado];
  48. require(instance.state != InstanceState.DISABLED, "The instance is not supported");
  49. if (instance.isERC20) {
  50. instance.token.safeTransferFrom(msg.sender, address(this), _tornado.denomination());
  51. }
  52. _tornado.deposit{ value: msg.value }(_commitment);
  53. if (instance.state == InstanceState.MINEABLE) {
  54. tornadoTrees.registerDeposit(address(_tornado), _commitment);
  55. }
  56. emit EncryptedNote(msg.sender, _encryptedNote);
  57. }
  58. function withdraw(
  59. ITornadoInstance _tornado,
  60. bytes calldata _proof,
  61. bytes32 _root,
  62. bytes32 _nullifierHash,
  63. address payable _recipient,
  64. address payable _relayer,
  65. uint256 _fee,
  66. uint256 _refund
  67. ) public payable virtual {
  68. Instance memory instance = instances[_tornado];
  69. require(instance.state != InstanceState.DISABLED, "The instance is not supported");
  70. _tornado.withdraw{ value: msg.value }(_proof, _root, _nullifierHash, _recipient, _relayer, _fee, _refund);
  71. if (instance.state == InstanceState.MINEABLE) {
  72. tornadoTrees.registerWithdrawal(address(_tornado), _nullifierHash);
  73. }
  74. }
  75. function backupNotes(bytes[] calldata _encryptedNotes) external virtual {
  76. for (uint256 i = 0; i < _encryptedNotes.length; i++) {
  77. emit EncryptedNote(msg.sender, _encryptedNotes[i]);
  78. }
  79. }
  80. function updateInstance(Tornado calldata _tornado) external virtual onlyGovernance {
  81. _updateInstance(_tornado);
  82. }
  83. function setTornadoTreesContract(ITornadoTrees _tornadoTrees) external virtual onlyGovernance {
  84. tornadoTrees = _tornadoTrees;
  85. emit TornadoTreesUpdated(_tornadoTrees);
  86. }
  87. /// @dev Method to claim junk and accidentally sent tokens
  88. function rescueTokens(
  89. IERC20 _token,
  90. address payable _to,
  91. uint256 _amount
  92. ) external virtual onlyGovernance {
  93. require(_to != address(0), "TORN: can not send to zero address");
  94. if (_token == IERC20(0)) {
  95. // for Ether
  96. uint256 totalBalance = address(this).balance;
  97. uint256 balance = Math.min(totalBalance, _amount);
  98. _to.transfer(balance);
  99. } else {
  100. // any other erc20
  101. uint256 totalBalance = _token.balanceOf(address(this));
  102. uint256 balance = Math.min(totalBalance, _amount);
  103. require(balance > 0, "TORN: trying to send 0 balance");
  104. _token.safeTransfer(_to, balance);
  105. }
  106. }
  107. function _updateInstance(Tornado memory _tornado) internal {
  108. instances[_tornado.addr] = _tornado.instance;
  109. if (_tornado.instance.isERC20) {
  110. IERC20 token = IERC20(_tornado.addr.token());
  111. require(token == _tornado.instance.token, "Incorrect token");
  112. uint256 allowance = token.allowance(address(this), address(_tornado.addr));
  113. if (_tornado.instance.state != InstanceState.DISABLED && allowance == 0) {
  114. token.safeApprove(address(_tornado.addr), uint256(-1));
  115. } else if (_tornado.instance.state == InstanceState.DISABLED && allowance != 0) {
  116. token.safeApprove(address(_tornado.addr), 0);
  117. }
  118. }
  119. emit InstanceStateUpdated(_tornado.addr, _tornado.instance.state);
  120. }
  121. }