mirror of
https://github.com/tornadocash/tornado-core.git
synced 2024-11-22 01:37:07 +01:00
test with real DAI and USDT
This commit is contained in:
parent
5006006a20
commit
b8c0c1898f
@ -5,3 +5,12 @@ TOKEN_AMOUNT=100000000000000000
|
|||||||
EMPTY_ELEMENT=1337
|
EMPTY_ELEMENT=1337
|
||||||
PRIVATE_KEY=
|
PRIVATE_KEY=
|
||||||
ERC20_TOKEN=
|
ERC20_TOKEN=
|
||||||
|
|
||||||
|
# DAI mirror in Kovan
|
||||||
|
#ERC20_TOKEN=0xd2b1a6b34f4a68425e7c28b4db5a37be3b7a4947
|
||||||
|
# block when 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1 has some DAI is 13146218
|
||||||
|
|
||||||
|
# USDT mirror in Kovan
|
||||||
|
#ERC20_TOKEN=0xf3e0d7bf58c5d455d31ef1c2d5375904df525105
|
||||||
|
#TOKEN_AMOUNT=1000000
|
||||||
|
# block when 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1 has some USDT is 13147586
|
||||||
|
@ -12,10 +12,9 @@
|
|||||||
pragma solidity ^0.5.8;
|
pragma solidity ^0.5.8;
|
||||||
|
|
||||||
import "./Mixer.sol";
|
import "./Mixer.sol";
|
||||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
||||||
|
|
||||||
contract ERC20Mixer is Mixer {
|
contract ERC20Mixer is Mixer {
|
||||||
IERC20 public token;
|
address public token;
|
||||||
// mixed token amount
|
// mixed token amount
|
||||||
uint256 public tokenDenomination;
|
uint256 public tokenDenomination;
|
||||||
// ether value to cover network fee (for relayer) and to have some ETH on a brand new address
|
// ether value to cover network fee (for relayer) and to have some ETH on a brand new address
|
||||||
@ -27,7 +26,7 @@ contract ERC20Mixer is Mixer {
|
|||||||
uint8 _merkleTreeHeight,
|
uint8 _merkleTreeHeight,
|
||||||
uint256 _emptyElement,
|
uint256 _emptyElement,
|
||||||
address payable _operator,
|
address payable _operator,
|
||||||
IERC20 _token,
|
address _token,
|
||||||
uint256 _tokenDenomination
|
uint256 _tokenDenomination
|
||||||
) Mixer(_verifier, _merkleTreeHeight, _emptyElement, _operator) public {
|
) Mixer(_verifier, _merkleTreeHeight, _emptyElement, _operator) public {
|
||||||
token = _token;
|
token = _token;
|
||||||
@ -42,7 +41,7 @@ contract ERC20Mixer is Mixer {
|
|||||||
*/
|
*/
|
||||||
function deposit(uint256 commitment) public payable {
|
function deposit(uint256 commitment) public payable {
|
||||||
require(msg.value == etherFeeDenomination, "Please send `etherFeeDenomination` ETH along with transaction");
|
require(msg.value == etherFeeDenomination, "Please send `etherFeeDenomination` ETH along with transaction");
|
||||||
require(token.transferFrom(msg.sender, address(this), tokenDenomination), "Approve before using");
|
transferFrom(msg.sender, address(this), tokenDenomination);
|
||||||
_deposit(commitment);
|
_deposit(commitment);
|
||||||
|
|
||||||
emit Deposit(commitment, next_index - 1, block.timestamp);
|
emit Deposit(commitment, next_index - 1, block.timestamp);
|
||||||
@ -69,8 +68,46 @@ contract ERC20Mixer is Mixer {
|
|||||||
operator.transfer(fee);
|
operator.transfer(fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
token.transfer(receiver, tokenDenomination);
|
transfer(receiver, tokenDenomination);
|
||||||
|
|
||||||
emit Withdraw(receiver, nullifierHash, fee);
|
emit Withdraw(receiver, nullifierHash, fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function transferFrom(address from, address to, uint256 amount) internal {
|
||||||
|
bool success;
|
||||||
|
bytes memory data;
|
||||||
|
bytes4 transferFromSelector = 0x23b872dd;
|
||||||
|
(success, data) = token.call(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
transferFromSelector,
|
||||||
|
from, to, amount
|
||||||
|
)
|
||||||
|
);
|
||||||
|
require(success, "not enough allowed tokens");
|
||||||
|
if (data.length > 0) {
|
||||||
|
assembly {
|
||||||
|
success := mload(add(data, 0x20))
|
||||||
|
}
|
||||||
|
require(success, "not enough allowed tokens");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function transfer(address to, uint256 amount) internal {
|
||||||
|
bool success;
|
||||||
|
bytes memory data;
|
||||||
|
bytes4 transferSelector = 0xa9059cbb;
|
||||||
|
(success, data) = token.call(
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
transferSelector,
|
||||||
|
to, amount
|
||||||
|
)
|
||||||
|
);
|
||||||
|
require(success, "not enough tokens");
|
||||||
|
if (data.length > 0) {
|
||||||
|
assembly {
|
||||||
|
success := mload(add(data, 0x20))
|
||||||
|
}
|
||||||
|
require(success, "not enough tokens");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
18
contracts/Mocks/IUSDT.sol
Normal file
18
contracts/Mocks/IUSDT.sol
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
contract ERC20Basic {
|
||||||
|
uint public _totalSupply;
|
||||||
|
function totalSupply() public view returns (uint);
|
||||||
|
function balanceOf(address who) public view returns (uint);
|
||||||
|
function transfer(address to, uint value) public;
|
||||||
|
event Transfer(address indexed from, address indexed to, uint value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title ERC20 interface
|
||||||
|
* @dev see https://github.com/ethereum/EIPs/issues/20
|
||||||
|
*/
|
||||||
|
contract IUSDT is ERC20Basic {
|
||||||
|
function allowance(address owner, address spender) public view returns (uint);
|
||||||
|
function transferFrom(address from, address to, uint value) public;
|
||||||
|
function approve(address spender, uint value) public;
|
||||||
|
event Approval(address indexed owner, address indexed spender, uint value);
|
||||||
|
}
|
@ -13,7 +13,7 @@ module.exports = function(deployer, network, accounts) {
|
|||||||
const miMC = await MiMC.deployed()
|
const miMC = await MiMC.deployed()
|
||||||
await ERC20Mixer.link(MiMC, miMC.address)
|
await ERC20Mixer.link(MiMC, miMC.address)
|
||||||
let token = ERC20_TOKEN
|
let token = ERC20_TOKEN
|
||||||
if(deployer.network !== 'mainnet') {
|
if(token === '') {
|
||||||
const tokenInstance = await deployer.deploy(ERC20Mock)
|
const tokenInstance = await deployer.deploy(ERC20Mock)
|
||||||
token = tokenInstance.address
|
token = tokenInstance.address
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,8 @@ const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper')
|
|||||||
|
|
||||||
const Mixer = artifacts.require('./ERC20Mixer.sol')
|
const Mixer = artifacts.require('./ERC20Mixer.sol')
|
||||||
const Token = artifacts.require('./ERC20Mock.sol')
|
const Token = artifacts.require('./ERC20Mock.sol')
|
||||||
const { ETH_AMOUNT, TOKEN_AMOUNT, MERKLE_TREE_HEIGHT, EMPTY_ELEMENT } = process.env
|
const USDTToken = artifacts.require('./IUSDT.sol')
|
||||||
|
const { ETH_AMOUNT, TOKEN_AMOUNT, MERKLE_TREE_HEIGHT, EMPTY_ELEMENT, ERC20_TOKEN } = process.env
|
||||||
|
|
||||||
const websnarkUtils = require('websnark/src/utils')
|
const websnarkUtils = require('websnark/src/utils')
|
||||||
const buildGroth16 = require('websnark/src/groth16')
|
const buildGroth16 = require('websnark/src/groth16')
|
||||||
@ -45,11 +46,12 @@ function getRandomReceiver() {
|
|||||||
contract('ERC20Mixer', accounts => {
|
contract('ERC20Mixer', accounts => {
|
||||||
let mixer
|
let mixer
|
||||||
let token
|
let token
|
||||||
|
let usdtToken
|
||||||
const sender = accounts[0]
|
const sender = accounts[0]
|
||||||
const operator = accounts[0]
|
const operator = accounts[0]
|
||||||
const levels = MERKLE_TREE_HEIGHT || 16
|
const levels = MERKLE_TREE_HEIGHT || 16
|
||||||
const zeroValue = EMPTY_ELEMENT || 1337
|
const zeroValue = EMPTY_ELEMENT || 1337
|
||||||
const tokenDenomination = TOKEN_AMOUNT || '1000000000000000000' // 1 ether
|
let tokenDenomination = TOKEN_AMOUNT || '1000000000000000000' // 1 ether
|
||||||
const value = ETH_AMOUNT || '1000000000000000000' // 1 ether
|
const value = ETH_AMOUNT || '1000000000000000000' // 1 ether
|
||||||
let snapshotId
|
let snapshotId
|
||||||
let prefix = 'test'
|
let prefix = 'test'
|
||||||
@ -69,8 +71,13 @@ contract('ERC20Mixer', accounts => {
|
|||||||
prefix,
|
prefix,
|
||||||
)
|
)
|
||||||
mixer = await Mixer.deployed()
|
mixer = await Mixer.deployed()
|
||||||
token = await Token.deployed()
|
if (ERC20_TOKEN) {
|
||||||
await token.mint(sender, tokenDenomination)
|
token = await Token.at(ERC20_TOKEN)
|
||||||
|
usdtToken = await USDTToken.at(ERC20_TOKEN)
|
||||||
|
} else {
|
||||||
|
token = await Token.deployed()
|
||||||
|
await token.mint(sender, tokenDenomination)
|
||||||
|
}
|
||||||
snapshotId = await takeSnapshot()
|
snapshotId = await takeSnapshot()
|
||||||
groth16 = await buildGroth16()
|
groth16 = await buildGroth16()
|
||||||
circuit = require('../build/circuits/withdraw.json')
|
circuit = require('../build/circuits/withdraw.json')
|
||||||
@ -161,6 +168,167 @@ contract('ERC20Mixer', accounts => {
|
|||||||
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(value)).sub(feeBN))
|
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(value)).sub(feeBN))
|
||||||
|
|
||||||
|
|
||||||
|
logs[0].event.should.be.equal('Withdraw')
|
||||||
|
logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
|
||||||
|
logs[0].args.fee.should.be.eq.BN(feeBN)
|
||||||
|
isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
|
||||||
|
isSpent.should.be.equal(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it.skip('should work with REAL USDT', async () => {
|
||||||
|
// dont forget to specify your token in .env
|
||||||
|
// USDT decimals is 6, so TOKEN_AMOUNT=1000000
|
||||||
|
// and sent `tokenDenomination` to accounts[0] (0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1)
|
||||||
|
// run ganache as
|
||||||
|
// ganache-cli --fork https://kovan.infura.io/v3/27a9649f826b4e31a83e07ae09a87448@13147586 -d --keepAliveTimeout 20
|
||||||
|
const deposit = generateDeposit()
|
||||||
|
const user = accounts[4]
|
||||||
|
const userBal = await usdtToken.balanceOf(user)
|
||||||
|
console.log('userBal', userBal.toString())
|
||||||
|
const senderBal = await usdtToken.balanceOf(sender)
|
||||||
|
console.log('senderBal', senderBal.toString())
|
||||||
|
await tree.insert(deposit.commitment)
|
||||||
|
await usdtToken.transfer(user, tokenDenomination, { from: sender })
|
||||||
|
console.log('transfer done')
|
||||||
|
|
||||||
|
const balanceUserBefore = await usdtToken.balanceOf(user)
|
||||||
|
console.log('balanceUserBefore', balanceUserBefore.toString())
|
||||||
|
await usdtToken.approve(mixer.address, tokenDenomination, { from: user })
|
||||||
|
console.log('approve done')
|
||||||
|
const allowanceUser = await usdtToken.allowance(user, mixer.address)
|
||||||
|
console.log('allowanceUser', allowanceUser.toString())
|
||||||
|
await mixer.deposit(toBN(deposit.commitment.toString()), { value, from: user, gasPrice: '0' })
|
||||||
|
console.log('deposit done')
|
||||||
|
|
||||||
|
const balanceUserAfter = await usdtToken.balanceOf(user)
|
||||||
|
balanceUserAfter.should.be.eq.BN(toBN(balanceUserBefore).sub(toBN(tokenDenomination)))
|
||||||
|
|
||||||
|
const { root, path_elements, path_index } = await tree.path(0)
|
||||||
|
|
||||||
|
// Circuit input
|
||||||
|
const input = stringifyBigInts({
|
||||||
|
// public
|
||||||
|
root,
|
||||||
|
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
|
||||||
|
receiver,
|
||||||
|
fee,
|
||||||
|
|
||||||
|
// private
|
||||||
|
nullifier: deposit.nullifier,
|
||||||
|
secret: deposit.secret,
|
||||||
|
pathElements: path_elements,
|
||||||
|
pathIndex: path_index,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const proof = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||||
|
const { pi_a, pi_b, pi_c, publicSignals } = websnarkUtils.toSolidityInput(proof)
|
||||||
|
|
||||||
|
const balanceMixerBefore = await usdtToken.balanceOf(mixer.address)
|
||||||
|
const balanceRelayerBefore = await usdtToken.balanceOf(relayer)
|
||||||
|
const ethBalanceOperatorBefore = await web3.eth.getBalance(operator)
|
||||||
|
const balanceRecieverBefore = await usdtToken.balanceOf(toHex(receiver.toString()))
|
||||||
|
const ethBalanceRecieverBefore = await web3.eth.getBalance(toHex(receiver.toString()))
|
||||||
|
let isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
|
||||||
|
isSpent.should.be.equal(false)
|
||||||
|
|
||||||
|
// Uncomment to measure gas usage
|
||||||
|
// gas = await mixer.withdraw.estimateGas(pi_a, pi_b, pi_c, publicSignals, { from: relayer, gasPrice: '0' })
|
||||||
|
// console.log('withdraw gas:', gas)
|
||||||
|
const { logs } = await mixer.withdraw(pi_a, pi_b, pi_c, publicSignals, { from: relayer, gasPrice: '0' })
|
||||||
|
|
||||||
|
const balanceMixerAfter = await usdtToken.balanceOf(mixer.address)
|
||||||
|
const balanceRelayerAfter = await usdtToken.balanceOf(relayer)
|
||||||
|
const ethBalanceOperatorAfter = await web3.eth.getBalance(operator)
|
||||||
|
const balanceRecieverAfter = await usdtToken.balanceOf(toHex(receiver.toString()))
|
||||||
|
const ethBalanceRecieverAfter = await web3.eth.getBalance(toHex(receiver.toString()))
|
||||||
|
const feeBN = toBN(fee.toString())
|
||||||
|
balanceMixerAfter.should.be.eq.BN(toBN(balanceMixerBefore).sub(toBN(tokenDenomination)))
|
||||||
|
balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore))
|
||||||
|
ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore).add(feeBN))
|
||||||
|
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination)))
|
||||||
|
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(value)).sub(feeBN))
|
||||||
|
|
||||||
|
|
||||||
|
logs[0].event.should.be.equal('Withdraw')
|
||||||
|
logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
|
||||||
|
logs[0].args.fee.should.be.eq.BN(feeBN)
|
||||||
|
isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
|
||||||
|
isSpent.should.be.equal(true)
|
||||||
|
})
|
||||||
|
it.skip('should work with REAL DAI', async () => {
|
||||||
|
// dont forget to specify your token in .env
|
||||||
|
// and sent `tokenDenomination` to accounts[0] (0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1)
|
||||||
|
// run ganache as
|
||||||
|
// ganache-cli --fork https://kovan.infura.io/v3/27a9649f826b4e31a83e07ae09a87448@13146218 -d --keepAliveTimeout 20
|
||||||
|
const deposit = generateDeposit()
|
||||||
|
const user = accounts[4]
|
||||||
|
const userBal = await token.balanceOf(user)
|
||||||
|
console.log('userBal', userBal.toString())
|
||||||
|
const senderBal = await token.balanceOf(sender)
|
||||||
|
console.log('senderBal', senderBal.toString())
|
||||||
|
await tree.insert(deposit.commitment)
|
||||||
|
await token.transfer(user, tokenDenomination, { from: sender })
|
||||||
|
console.log('transfer done')
|
||||||
|
|
||||||
|
const balanceUserBefore = await token.balanceOf(user)
|
||||||
|
console.log('balanceUserBefore', balanceUserBefore.toString())
|
||||||
|
await token.approve(mixer.address, tokenDenomination, { from: user })
|
||||||
|
console.log('approve done')
|
||||||
|
await mixer.deposit(toBN(deposit.commitment.toString()), { value, from: user, gasPrice: '0' })
|
||||||
|
console.log('deposit done')
|
||||||
|
|
||||||
|
const balanceUserAfter = await token.balanceOf(user)
|
||||||
|
balanceUserAfter.should.be.eq.BN(toBN(balanceUserBefore).sub(toBN(tokenDenomination)))
|
||||||
|
|
||||||
|
const { root, path_elements, path_index } = await tree.path(0)
|
||||||
|
|
||||||
|
// Circuit input
|
||||||
|
const input = stringifyBigInts({
|
||||||
|
// public
|
||||||
|
root,
|
||||||
|
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
|
||||||
|
receiver,
|
||||||
|
fee,
|
||||||
|
|
||||||
|
// private
|
||||||
|
nullifier: deposit.nullifier,
|
||||||
|
secret: deposit.secret,
|
||||||
|
pathElements: path_elements,
|
||||||
|
pathIndex: path_index,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const proof = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||||
|
const { pi_a, pi_b, pi_c, publicSignals } = websnarkUtils.toSolidityInput(proof)
|
||||||
|
|
||||||
|
const balanceMixerBefore = await token.balanceOf(mixer.address)
|
||||||
|
const balanceRelayerBefore = await token.balanceOf(relayer)
|
||||||
|
const ethBalanceOperatorBefore = await web3.eth.getBalance(operator)
|
||||||
|
const balanceRecieverBefore = await token.balanceOf(toHex(receiver.toString()))
|
||||||
|
const ethBalanceRecieverBefore = await web3.eth.getBalance(toHex(receiver.toString()))
|
||||||
|
let isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
|
||||||
|
isSpent.should.be.equal(false)
|
||||||
|
|
||||||
|
// Uncomment to measure gas usage
|
||||||
|
// gas = await mixer.withdraw.estimateGas(pi_a, pi_b, pi_c, publicSignals, { from: relayer, gasPrice: '0' })
|
||||||
|
// console.log('withdraw gas:', gas)
|
||||||
|
const { logs } = await mixer.withdraw(pi_a, pi_b, pi_c, publicSignals, { from: relayer, gasPrice: '0' })
|
||||||
|
console.log('withdraw done')
|
||||||
|
|
||||||
|
const balanceMixerAfter = await token.balanceOf(mixer.address)
|
||||||
|
const balanceRelayerAfter = await token.balanceOf(relayer)
|
||||||
|
const ethBalanceOperatorAfter = await web3.eth.getBalance(operator)
|
||||||
|
const balanceRecieverAfter = await token.balanceOf(toHex(receiver.toString()))
|
||||||
|
const ethBalanceRecieverAfter = await web3.eth.getBalance(toHex(receiver.toString()))
|
||||||
|
const feeBN = toBN(fee.toString())
|
||||||
|
balanceMixerAfter.should.be.eq.BN(toBN(balanceMixerBefore).sub(toBN(tokenDenomination)))
|
||||||
|
balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore))
|
||||||
|
ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore).add(feeBN))
|
||||||
|
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination)))
|
||||||
|
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(value)).sub(feeBN))
|
||||||
|
|
||||||
|
|
||||||
logs[0].event.should.be.equal('Withdraw')
|
logs[0].event.should.be.equal('Withdraw')
|
||||||
logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
|
logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
|
||||||
logs[0].args.fee.should.be.eq.BN(feeBN)
|
logs[0].args.fee.should.be.eq.BN(feeBN)
|
||||||
|
Loading…
Reference in New Issue
Block a user