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
|
||||
cache
|
||||
artifacts
|
||||
artifacts-ovm
|
||||
cache-ovm
|
||||
src/types
|
||||
|
@ -17,4 +17,34 @@ contract MockOmniBridge is IOmniBridge {
|
||||
function execute(address _who, bytes calldata _calldata) external returns (bool success, bytes memory result) {
|
||||
(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 "./MerkleTreeWithHistory.sol";
|
||||
|
||||
import "hardhat/console.sol";
|
||||
|
||||
interface IERC6777 is IERC20 {
|
||||
function transferAndCall(
|
||||
address,
|
||||
@ -47,6 +49,7 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver {
|
||||
IVerifier public immutable verifier16;
|
||||
IERC6777 public immutable token;
|
||||
address public immutable omniBridge;
|
||||
address public immutable l1Unwrapper;
|
||||
|
||||
struct ExtData {
|
||||
address recipient;
|
||||
@ -55,7 +58,7 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver {
|
||||
uint256 fee;
|
||||
bytes encryptedOutput1;
|
||||
bytes encryptedOutput2;
|
||||
bool isL1Withdraw;
|
||||
bool isL1Withdrawal;
|
||||
}
|
||||
|
||||
struct Proof {
|
||||
@ -88,12 +91,14 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver {
|
||||
uint32 _levels,
|
||||
address _hasher,
|
||||
IERC6777 _token,
|
||||
address _omniBridge
|
||||
address _omniBridge,
|
||||
address _l1Unwrapper
|
||||
) MerkleTreeWithHistory(_levels, _hasher) {
|
||||
verifier2 = _verifier2;
|
||||
verifier16 = _verifier16;
|
||||
token = _token;
|
||||
omniBridge = _omniBridge;
|
||||
l1Unwrapper = _l1Unwrapper;
|
||||
}
|
||||
|
||||
function transact(Proof memory _args, ExtData memory _extData) public {
|
||||
@ -120,13 +125,12 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver {
|
||||
|
||||
if (_extData.extAmount < 0) {
|
||||
require(_extData.recipient != address(0), "Can't withdraw to zero address");
|
||||
if (_extData.isL1Withdraw) {
|
||||
token.transferAndCall(omniBridge, uint256(-_extData.extAmount), abi.encode(_extData.recipient));
|
||||
if (_extData.isL1Withdrawal) {
|
||||
token.transferAndCall(omniBridge, uint256(-_extData.extAmount), abi.encodePacked(l1Unwrapper, _extData.recipient));
|
||||
} else {
|
||||
token.transfer(_extData.recipient, uint256(-_extData.extAmount));
|
||||
}
|
||||
}
|
||||
|
||||
if (_extData.fee > 0) {
|
||||
token.transfer(_extData.relayer, _extData.fee);
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ async function transaction({ tornadoPool, ...rest }) {
|
||||
const receipt = await tornadoPool.transact(args, extData, {
|
||||
gasLimit: 1e6,
|
||||
})
|
||||
await receipt.wait()
|
||||
return await receipt.wait()
|
||||
}
|
||||
|
||||
async function registerAndTransact({ tornadoPool, packedPrivateKeyData, poolAddress, ...rest }) {
|
||||
@ -166,4 +166,4 @@ async function registerAndTransact({ tornadoPool, packedPrivateKeyData, poolAddr
|
||||
await receipt.wait()
|
||||
}
|
||||
|
||||
module.exports = { transaction, registerAndTransact }
|
||||
module.exports = { transaction, registerAndTransact, prepareTransaction }
|
||||
|
@ -5,7 +5,7 @@ const { expect } = require('chai')
|
||||
const { utils } = ethers
|
||||
|
||||
const Utxo = require('../src/utxo')
|
||||
const { transaction, registerAndTransact } = require('../src/index')
|
||||
const { transaction, registerAndTransact, prepareTransaction } = require('../src/index')
|
||||
const { Keypair } = require('../src/keypair')
|
||||
|
||||
const MERKLE_TREE_HEIGHT = 5
|
||||
@ -21,7 +21,7 @@ describe('TornadoPool', function () {
|
||||
|
||||
async function fixture() {
|
||||
require('../scripts/compileHasher')
|
||||
const [sender, gov] = await ethers.getSigners()
|
||||
const [sender, gov, l1Unwrapper] = await ethers.getSigners()
|
||||
const verifier2 = await deploy('Verifier2')
|
||||
const verifier16 = await deploy('Verifier16')
|
||||
const hasher = await deploy('Hasher')
|
||||
@ -41,6 +41,7 @@ describe('TornadoPool', function () {
|
||||
hasher.address,
|
||||
token.address,
|
||||
omniBridge.address,
|
||||
l1Unwrapper.address,
|
||||
)
|
||||
await tornadoPool.initialize()
|
||||
|
||||
@ -211,6 +212,54 @@ describe('TornadoPool', function () {
|
||||
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 () {
|
||||
const { tornadoPool } = await loadFixture(fixture)
|
||||
await transaction({ tornadoPool, inputs: [new Utxo(), new Utxo(), new Utxo()] })
|
||||
|
Loading…
Reference in New Issue
Block a user