mirror of
https://github.com/tornadocash/tornado-nova
synced 2024-02-02 14:53:56 +01:00
withdraw to L1 support and test
This commit is contained in:
parent
a28d887643
commit
5e14553dfb
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,6 +3,4 @@ node_modules
|
|||||||
build
|
build
|
||||||
cache
|
cache
|
||||||
artifacts
|
artifacts
|
||||||
artifacts-ovm
|
|
||||||
cache-ovm
|
|
||||||
src/types
|
src/types
|
||||||
|
@ -17,4 +17,34 @@ contract MockOmniBridge is IOmniBridge {
|
|||||||
function execute(address _who, bytes calldata _calldata) external returns (bool success, bytes memory result) {
|
function execute(address _who, bytes calldata _calldata) external returns (bool success, bytes memory result) {
|
||||||
(success, result) = _who.call(_calldata);
|
(success, result) = _who.call(_calldata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event OnTokenTransfer(address contr, address from, address receiver, uint256 value, bytes data);
|
||||||
|
|
||||||
|
function onTokenTransfer(
|
||||||
|
address _from,
|
||||||
|
uint256 _value,
|
||||||
|
bytes memory _data
|
||||||
|
) external returns (bool) {
|
||||||
|
bytes memory data = new bytes(0);
|
||||||
|
address receiver = _from;
|
||||||
|
if (_data.length >= 20) {
|
||||||
|
receiver = bytesToAddress(_data);
|
||||||
|
if (_data.length > 20) {
|
||||||
|
assembly {
|
||||||
|
let size := sub(mload(_data), 20)
|
||||||
|
data := add(_data, 20)
|
||||||
|
mstore(data, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit OnTokenTransfer(msg.sender, _from, receiver, _value, data);
|
||||||
|
//bridgeSpecificActionsOnTokenTransfer(msg.sender, _from, receiver, _value, data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bytesToAddress(bytes memory _bytes) internal pure returns (address addr) {
|
||||||
|
assembly {
|
||||||
|
addr := mload(add(_bytes, 20))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ pragma experimental ABIEncoderV2;
|
|||||||
import "@openzeppelin/contracts/contracts/token/ERC20/IERC20.sol";
|
import "@openzeppelin/contracts/contracts/token/ERC20/IERC20.sol";
|
||||||
import "./MerkleTreeWithHistory.sol";
|
import "./MerkleTreeWithHistory.sol";
|
||||||
|
|
||||||
|
import "hardhat/console.sol";
|
||||||
|
|
||||||
interface IERC6777 is IERC20 {
|
interface IERC6777 is IERC20 {
|
||||||
function transferAndCall(
|
function transferAndCall(
|
||||||
address,
|
address,
|
||||||
@ -47,6 +49,7 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver {
|
|||||||
IVerifier public immutable verifier16;
|
IVerifier public immutable verifier16;
|
||||||
IERC6777 public immutable token;
|
IERC6777 public immutable token;
|
||||||
address public immutable omniBridge;
|
address public immutable omniBridge;
|
||||||
|
address public immutable l1Unwrapper;
|
||||||
|
|
||||||
struct ExtData {
|
struct ExtData {
|
||||||
address recipient;
|
address recipient;
|
||||||
@ -55,7 +58,7 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver {
|
|||||||
uint256 fee;
|
uint256 fee;
|
||||||
bytes encryptedOutput1;
|
bytes encryptedOutput1;
|
||||||
bytes encryptedOutput2;
|
bytes encryptedOutput2;
|
||||||
bool isL1Withdraw;
|
bool isL1Withdrawal;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Proof {
|
struct Proof {
|
||||||
@ -88,12 +91,14 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver {
|
|||||||
uint32 _levels,
|
uint32 _levels,
|
||||||
address _hasher,
|
address _hasher,
|
||||||
IERC6777 _token,
|
IERC6777 _token,
|
||||||
address _omniBridge
|
address _omniBridge,
|
||||||
|
address _l1Unwrapper
|
||||||
) MerkleTreeWithHistory(_levels, _hasher) {
|
) MerkleTreeWithHistory(_levels, _hasher) {
|
||||||
verifier2 = _verifier2;
|
verifier2 = _verifier2;
|
||||||
verifier16 = _verifier16;
|
verifier16 = _verifier16;
|
||||||
token = _token;
|
token = _token;
|
||||||
omniBridge = _omniBridge;
|
omniBridge = _omniBridge;
|
||||||
|
l1Unwrapper = _l1Unwrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
function transact(Proof memory _args, ExtData memory _extData) public {
|
function transact(Proof memory _args, ExtData memory _extData) public {
|
||||||
@ -120,13 +125,12 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver {
|
|||||||
|
|
||||||
if (_extData.extAmount < 0) {
|
if (_extData.extAmount < 0) {
|
||||||
require(_extData.recipient != address(0), "Can't withdraw to zero address");
|
require(_extData.recipient != address(0), "Can't withdraw to zero address");
|
||||||
if (_extData.isL1Withdraw) {
|
if (_extData.isL1Withdrawal) {
|
||||||
token.transferAndCall(omniBridge, uint256(-_extData.extAmount), abi.encode(_extData.recipient));
|
token.transferAndCall(omniBridge, uint256(-_extData.extAmount), abi.encodePacked(l1Unwrapper, _extData.recipient));
|
||||||
} else {
|
} else {
|
||||||
token.transfer(_extData.recipient, uint256(-_extData.extAmount));
|
token.transfer(_extData.recipient, uint256(-_extData.extAmount));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_extData.fee > 0) {
|
if (_extData.fee > 0) {
|
||||||
token.transfer(_extData.relayer, _extData.fee);
|
token.transfer(_extData.relayer, _extData.fee);
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ async function transaction({ tornadoPool, ...rest }) {
|
|||||||
const receipt = await tornadoPool.transact(args, extData, {
|
const receipt = await tornadoPool.transact(args, extData, {
|
||||||
gasLimit: 1e6,
|
gasLimit: 1e6,
|
||||||
})
|
})
|
||||||
await receipt.wait()
|
return await receipt.wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function registerAndTransact({ tornadoPool, packedPrivateKeyData, poolAddress, ...rest }) {
|
async function registerAndTransact({ tornadoPool, packedPrivateKeyData, poolAddress, ...rest }) {
|
||||||
@ -166,4 +166,4 @@ async function registerAndTransact({ tornadoPool, packedPrivateKeyData, poolAddr
|
|||||||
await receipt.wait()
|
await receipt.wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { transaction, registerAndTransact }
|
module.exports = { transaction, registerAndTransact, prepareTransaction }
|
||||||
|
@ -5,7 +5,7 @@ const { expect } = require('chai')
|
|||||||
const { utils } = ethers
|
const { utils } = ethers
|
||||||
|
|
||||||
const Utxo = require('../src/utxo')
|
const Utxo = require('../src/utxo')
|
||||||
const { transaction, registerAndTransact } = require('../src/index')
|
const { transaction, registerAndTransact, prepareTransaction } = require('../src/index')
|
||||||
const { Keypair } = require('../src/keypair')
|
const { Keypair } = require('../src/keypair')
|
||||||
|
|
||||||
const MERKLE_TREE_HEIGHT = 5
|
const MERKLE_TREE_HEIGHT = 5
|
||||||
@ -21,7 +21,7 @@ describe('TornadoPool', function () {
|
|||||||
|
|
||||||
async function fixture() {
|
async function fixture() {
|
||||||
require('../scripts/compileHasher')
|
require('../scripts/compileHasher')
|
||||||
const [sender, gov] = await ethers.getSigners()
|
const [sender, gov, l1Unwrapper] = await ethers.getSigners()
|
||||||
const verifier2 = await deploy('Verifier2')
|
const verifier2 = await deploy('Verifier2')
|
||||||
const verifier16 = await deploy('Verifier16')
|
const verifier16 = await deploy('Verifier16')
|
||||||
const hasher = await deploy('Hasher')
|
const hasher = await deploy('Hasher')
|
||||||
@ -41,6 +41,7 @@ describe('TornadoPool', function () {
|
|||||||
hasher.address,
|
hasher.address,
|
||||||
token.address,
|
token.address,
|
||||||
omniBridge.address,
|
omniBridge.address,
|
||||||
|
l1Unwrapper.address,
|
||||||
)
|
)
|
||||||
await tornadoPool.initialize()
|
await tornadoPool.initialize()
|
||||||
|
|
||||||
@ -211,6 +212,54 @@ describe('TornadoPool', function () {
|
|||||||
expect(bobBalance).to.be.equal(bobWithdrawAmount)
|
expect(bobBalance).to.be.equal(bobWithdrawAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should deposit from L1 and withdraw to L1', async function () {
|
||||||
|
const { tornadoPool, token, omniBridge } = await loadFixture(fixture)
|
||||||
|
// console.log('tornadoPool', tornadoPool.interface)
|
||||||
|
|
||||||
|
// Alice deposits into tornado pool
|
||||||
|
const aliceDepositAmount = 1e7
|
||||||
|
const aliceDepositUtxo = new Utxo({ amount: aliceDepositAmount })
|
||||||
|
const { args, extData } = await prepareTransaction({
|
||||||
|
tornadoPool,
|
||||||
|
outputs: [aliceDepositUtxo],
|
||||||
|
})
|
||||||
|
const transactTx = await tornadoPool.populateTransaction.registerAndTransact(
|
||||||
|
{ pubKey: [], account: [] },
|
||||||
|
args,
|
||||||
|
extData,
|
||||||
|
)
|
||||||
|
const onTokenBridgedData = '0x' + transactTx.data.slice(10)
|
||||||
|
const onTokenBridgedTx = await tornadoPool.populateTransaction.onTokenBridged(
|
||||||
|
token.address,
|
||||||
|
aliceDepositUtxo.amount,
|
||||||
|
onTokenBridgedData,
|
||||||
|
)
|
||||||
|
// emulating bridge. first it sends tokens and then calls onTokenBridged method
|
||||||
|
await token.transfer(tornadoPool.address, aliceDepositAmount)
|
||||||
|
await omniBridge.execute(tornadoPool.address, onTokenBridgedTx.data)
|
||||||
|
|
||||||
|
// withdraws a part of his funds from the shielded pool
|
||||||
|
const aliceKeypair = new Keypair() // contains private and public keys
|
||||||
|
const aliceWithdrawAmount = 2e6
|
||||||
|
const recipient = '0xDeaD00000000000000000000000000000000BEEf'
|
||||||
|
const aliceChangeUtxo = new Utxo({
|
||||||
|
amount: aliceDepositAmount - aliceWithdrawAmount,
|
||||||
|
keypair: aliceKeypair,
|
||||||
|
})
|
||||||
|
await transaction({
|
||||||
|
tornadoPool,
|
||||||
|
inputs: [aliceDepositUtxo],
|
||||||
|
outputs: [aliceChangeUtxo],
|
||||||
|
recipient: recipient,
|
||||||
|
isL1Withdrawal: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const recipientBalance = await token.balanceOf(recipient)
|
||||||
|
expect(recipientBalance).to.be.equal(0)
|
||||||
|
const omniBridgeBalance = await token.balanceOf(omniBridge.address)
|
||||||
|
expect(omniBridgeBalance).to.be.equal(aliceWithdrawAmount)
|
||||||
|
})
|
||||||
|
|
||||||
it('should work with 16 inputs', async function () {
|
it('should work with 16 inputs', async function () {
|
||||||
const { tornadoPool } = await loadFixture(fixture)
|
const { tornadoPool } = await loadFixture(fixture)
|
||||||
await transaction({ tornadoPool, inputs: [new Utxo(), new Utxo(), new Utxo()] })
|
await transaction({ tornadoPool, inputs: [new Utxo(), new Utxo(), new Utxo()] })
|
||||||
|
Loading…
Reference in New Issue
Block a user