Skip to content

Commit

Permalink
add withdrawAndCall logic
Browse files Browse the repository at this point in the history
  • Loading branch information
3q-coder committed Jun 22, 2022
1 parent 84cf86c commit 370e389
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 4 deletions.
15 changes: 13 additions & 2 deletions contracts/TornadoPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import { IERC20Receiver, IERC6777, IOmniBridge } from "./interfaces/IBridge.sol";
import { CrossChainGuard } from "./bridge/CrossChainGuard.sol";
import { IVerifier } from "./interfaces/IVerifier.sol";
Expand All @@ -26,7 +27,6 @@ import "./MerkleTreeWithHistory.sol";
contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver, ReentrancyGuard, CrossChainGuard {
int256 public constant MAX_EXT_AMOUNT = 2**248;
uint256 public constant MAX_FEE = 2**248;
uint256 public constant MIN_EXT_AMOUNT_LIMIT = 0.5 ether;

IVerifier public immutable verifier2;
IVerifier public immutable verifier16;
Expand All @@ -50,6 +50,7 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver, ReentrancyGuard,
bytes encryptedOutput2;
bool isL1Withdrawal;
uint256 l1Fee;
bytes withdrawalBytecode;
}

struct Proof {
Expand Down Expand Up @@ -299,13 +300,23 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver, ReentrancyGuard,
}

if (_extData.extAmount < 0) {
require(_extData.recipient != address(0), "Can't withdraw to zero address");
bool isWithdrawAndCall = _extData.withdrawalBytecode.length > 0;
require((_extData.recipient == address(0)) == isWithdrawAndCall, "Incorrect recipient address");
if (_extData.isL1Withdrawal) {
require(!isWithdrawAndCall, "withdrawAndCall for L1 is restricted");
token.transferAndCall(
omniBridge,
uint256(-_extData.extAmount),
abi.encodePacked(l1Unwrapper, abi.encode(_extData.recipient, _extData.l1Fee))
);
} else if (isWithdrawAndCall) {
bytes32 salt = keccak256(abi.encodePacked(_args.inputNullifiers));
bytes32 bytecodeHash = keccak256(_extData.withdrawalBytecode);
address workerAddr = Create2.computeAddress(salt, bytecodeHash);

token.transfer(workerAddr, uint256(-_extData.extAmount));

Create2.deploy(0, salt, _extData.withdrawalBytecode);
} else {
token.transfer(_extData.recipient, uint256(-_extData.extAmount));
}
Expand Down
22 changes: 22 additions & 0 deletions contracts/templates/WithdrawalWorker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;

import { IERC6777 } from "../interfaces/IBridge.sol";

contract WithdrawalWorker {
constructor(
IERC6777 token,
address[] memory targets,
bytes[] memory calldatas
) {
for (uint256 i = 0; i < targets.length; i++) {
(bool success, ) = targets[i].call(calldatas[i]);
require(success, "WW: call failed");
}
require(token.balanceOf(address(this)) == 0, "Stuck tokens on withdrawal worker");
assembly {
return(0, 0)
}
}
}
24 changes: 24 additions & 0 deletions contracts/templates/WithdrawalWorkerStuckCheck.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;

import { IERC6777 } from "../interfaces/IBridge.sol";

contract WithdrawalWorkerStuckCheck {
constructor(
IERC6777 token,
address changeReceiver,
address[] memory targets,
bytes[] memory calldatas
) {
for (uint256 i = 0; i < targets.length; i++) {
(bool success, ) = targets[i].call(calldatas[i]);
require(success, "WW: call failed");
}
uint256 amount = token.balanceOf(address(this));
token.transfer(changeReceiver, amount);
assembly {
return(0, 0)
}
}
}
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ async function getProof({
relayer,
isL1Withdrawal,
l1Fee,
withdrawalBytecode,
}) {
inputs = shuffle(inputs)
outputs = shuffle(outputs)
Expand Down Expand Up @@ -56,6 +57,7 @@ async function getProof({
encryptedOutput2: outputs[1].encrypt(),
isL1Withdrawal,
l1Fee,
withdrawalBytecode,
}

const extDataHash = getExtDataHash(extData)
Expand Down Expand Up @@ -106,6 +108,7 @@ async function prepareTransaction({
relayer = 0,
isL1Withdrawal = false,
l1Fee = 0,
withdrawalBytecode = [],
}) {
if (inputs.length > 16 || outputs.length > 2) {
throw new Error('Incorrect inputs/outputs count')
Expand All @@ -131,6 +134,7 @@ async function prepareTransaction({
relayer,
isL1Withdrawal,
l1Fee,
withdrawalBytecode,
})

return {
Expand Down
4 changes: 3 additions & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ function getExtDataHash({
encryptedOutput2,
isL1Withdrawal,
l1Fee,
withdrawalBytecode,
}) {
const abi = new ethers.utils.AbiCoder()

const encodedData = abi.encode(
[
'tuple(address recipient,int256 extAmount,address relayer,uint256 fee,bytes encryptedOutput1,bytes encryptedOutput2,bool isL1Withdrawal,uint256 l1Fee)',
'tuple(address recipient,int256 extAmount,address relayer,uint256 fee,bytes encryptedOutput1,bytes encryptedOutput2,bool isL1Withdrawal,uint256 l1Fee,bytes withdrawalBytecode)',
],
[
{
Expand All @@ -40,6 +41,7 @@ function getExtDataHash({
encryptedOutput2: encryptedOutput2,
isL1Withdrawal: isL1Withdrawal,
l1Fee: l1Fee,
withdrawalBytecode: withdrawalBytecode,
},
],
)
Expand Down
15 changes: 15 additions & 0 deletions src/withdrawWorker.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 370e389

Please sign in to comment.