mirror of
https://github.com/tornadocash/tornado-core.git
synced 2025-04-20 23:26:43 -04:00
Add feeToSetter
This commit is contained in:
parent
66a8fe5712
commit
603dcdc426
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1508
client/contracts/FeeManager.json
Normal file
1508
client/contracts/FeeManager.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -51,6 +51,5 @@
|
||||
}
|
||||
},
|
||||
"schemaVersion": "3.3.4",
|
||||
"updatedAt": "2021-03-22T02:54:26.425Z",
|
||||
"networkType": "ethereum"
|
||||
"updatedAt": "2021-04-02T22:30:43.196Z"
|
||||
}
|
9168
client/contracts/IFeeManager.json
Normal file
9168
client/contracts/IFeeManager.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -15,15 +15,19 @@ import "./Tornado.sol";
|
||||
|
||||
contract ERC20Tornado is Tornado {
|
||||
address public token;
|
||||
uint256 public protocolFee;
|
||||
|
||||
constructor(
|
||||
IVerifier _verifier,
|
||||
IFeeManager _feeManager,
|
||||
uint256 _denomination,
|
||||
uint32 _merkleTreeHeight,
|
||||
address _operator,
|
||||
address _token
|
||||
) Tornado(_verifier, _denomination, _merkleTreeHeight, _operator) public {
|
||||
) Tornado(_verifier, _feeManager, _denomination, _merkleTreeHeight, _operator) public {
|
||||
token = _token;
|
||||
// 0.5% fee
|
||||
protocolFee = _denomination / 200;
|
||||
}
|
||||
|
||||
function _processDeposit() internal {
|
||||
@ -31,12 +35,19 @@ contract ERC20Tornado is Tornado {
|
||||
_safeErc20TransferFrom(msg.sender, address(this), denomination);
|
||||
}
|
||||
|
||||
function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal {
|
||||
function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _relayer_fee, uint256 _refund, address _feeTo) internal {
|
||||
require(msg.value == _refund, "Incorrect refund amount received by the contract");
|
||||
|
||||
_safeErc20Transfer(_recipient, denomination - _fee);
|
||||
if (_fee > 0) {
|
||||
_safeErc20Transfer(_relayer, _fee);
|
||||
bool feeOn = _feeTo != address(0);
|
||||
if (feeOn) {
|
||||
_safeErc20Transfer(_recipient, denomination - _relayer_fee - protocolFee);
|
||||
_safeErc20Transfer(_feeTo, protocolFee);
|
||||
} else {
|
||||
_safeErc20Transfer(_recipient, denomination - _relayer_fee);
|
||||
}
|
||||
|
||||
if (_relayer_fee > 0) {
|
||||
_safeErc20Transfer(_relayer, _relayer_fee);
|
||||
}
|
||||
|
||||
if (_refund > 0) {
|
||||
|
@ -1,41 +0,0 @@
|
||||
// https://tornado.cash
|
||||
/*
|
||||
* d888888P dP a88888b. dP
|
||||
* 88 88 d8' `88 88
|
||||
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
|
||||
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
|
||||
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
|
||||
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
|
||||
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
|
||||
*/
|
||||
|
||||
pragma solidity 0.5.17;
|
||||
|
||||
import "./Tornado.sol";
|
||||
|
||||
contract ETHTornado is Tornado {
|
||||
constructor(
|
||||
IVerifier _verifier,
|
||||
uint256 _denomination,
|
||||
uint32 _merkleTreeHeight,
|
||||
address _operator
|
||||
) Tornado(_verifier, _denomination, _merkleTreeHeight, _operator) public {
|
||||
}
|
||||
|
||||
function _processDeposit() internal {
|
||||
require(msg.value == denomination, "Please send `mixDenomination` ETH along with transaction");
|
||||
}
|
||||
|
||||
function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal {
|
||||
// sanity checks
|
||||
require(msg.value == 0, "Message value is supposed to be zero for ETH instance");
|
||||
require(_refund == 0, "Refund value is supposed to be zero for ETH instance");
|
||||
|
||||
(bool success, ) = _recipient.call.value(denomination - _fee)("");
|
||||
require(success, "payment to _recipient did not go thru");
|
||||
if (_fee > 0) {
|
||||
(success, ) = _relayer.call.value(_fee)("");
|
||||
require(success, "payment to _relayer did not go thru");
|
||||
}
|
||||
}
|
||||
}
|
20
contracts/FeeManager.sol
Normal file
20
contracts/FeeManager.sol
Normal file
@ -0,0 +1,20 @@
|
||||
pragma solidity 0.5.17;
|
||||
|
||||
contract FeeManager {
|
||||
address public feeTo;
|
||||
address public feeToSetter;
|
||||
|
||||
constructor(address _feeToSetter) public {
|
||||
feeToSetter = _feeToSetter;
|
||||
}
|
||||
|
||||
function setFeeTo(address _feeTo) external {
|
||||
require(msg.sender == feeToSetter, 'Poof: FORBIDDEN');
|
||||
feeTo = _feeTo;
|
||||
}
|
||||
|
||||
function setFeeToSetter(address _feeToSetter) external {
|
||||
require(msg.sender == feeToSetter, 'Poof: FORBIDDEN');
|
||||
feeToSetter = _feeToSetter;
|
||||
}
|
||||
}
|
@ -1,14 +1,3 @@
|
||||
// https://tornado.cash
|
||||
/*
|
||||
* d888888P dP a88888b. dP
|
||||
* 88 88 d8' `88 88
|
||||
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
|
||||
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
|
||||
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
|
||||
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
|
||||
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
|
||||
*/
|
||||
|
||||
pragma solidity 0.5.17;
|
||||
|
||||
import "./MerkleTreeWithHistory.sol";
|
||||
@ -18,12 +7,17 @@ contract IVerifier {
|
||||
function verifyProof(bytes memory _proof, uint256[6] memory _input) public returns(bool);
|
||||
}
|
||||
|
||||
contract IFeeManager {
|
||||
function feeTo() external view returns (address);
|
||||
}
|
||||
|
||||
contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
|
||||
uint256 public denomination;
|
||||
mapping(bytes32 => bool) public nullifierHashes;
|
||||
// we store all commitments just to prevent accidental deposits with the same commitment
|
||||
mapping(bytes32 => bool) public commitments;
|
||||
IVerifier public verifier;
|
||||
IFeeManager public feeManager;
|
||||
|
||||
// operator can update snark verification key
|
||||
// after the final trusted setup ceremony operator rights are supposed to be transferred to zero address
|
||||
@ -45,12 +39,14 @@ contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
|
||||
*/
|
||||
constructor(
|
||||
IVerifier _verifier,
|
||||
IFeeManager _feeManager,
|
||||
uint256 _denomination,
|
||||
uint32 _merkleTreeHeight,
|
||||
address _operator
|
||||
) MerkleTreeWithHistory(_merkleTreeHeight) public {
|
||||
require(_denomination > 0, "denomination should be greater than 0");
|
||||
verifier = _verifier;
|
||||
feeManager = _feeManager;
|
||||
operator = _operator;
|
||||
denomination = _denomination;
|
||||
}
|
||||
@ -87,12 +83,12 @@ contract Tornado is MerkleTreeWithHistory, ReentrancyGuard {
|
||||
require(verifier.verifyProof(_proof, [uint256(_root), uint256(_nullifierHash), uint256(_recipient), uint256(_relayer), _fee, _refund]), "Invalid withdraw proof");
|
||||
|
||||
nullifierHashes[_nullifierHash] = true;
|
||||
_processWithdraw(_recipient, _relayer, _fee, _refund);
|
||||
_processWithdraw(_recipient, _relayer, _fee, _refund, feeManager.feeTo());
|
||||
emit Withdrawal(_recipient, _nullifierHash, _relayer, _fee);
|
||||
}
|
||||
|
||||
/** @dev this function is defined in a child contract */
|
||||
function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal;
|
||||
function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _relayer_fee, uint256 _refund, address _feeTo) internal;
|
||||
|
||||
/** @dev whether a note is already spent */
|
||||
function isSpent(bytes32 _nullifierHash) public view returns(bool) {
|
||||
|
@ -1,17 +0,0 @@
|
||||
/* global artifacts */
|
||||
require('dotenv').config({ path: '../.env' })
|
||||
const ETHTornado = artifacts.require('ETHTornado')
|
||||
const Verifier = artifacts.require('Verifier')
|
||||
const hasherContract = artifacts.require('Hasher')
|
||||
|
||||
|
||||
module.exports = function(deployer, network, accounts) {
|
||||
return deployer.then(async () => {
|
||||
const { MERKLE_TREE_HEIGHT, ETH_AMOUNT } = process.env
|
||||
const verifier = await Verifier.deployed()
|
||||
const hasherInstance = await hasherContract.deployed()
|
||||
await ETHTornado.link(hasherContract, hasherInstance.address)
|
||||
const tornado = await deployer.deploy(ETHTornado, verifier.address, ETH_AMOUNT, MERKLE_TREE_HEIGHT, accounts[0])
|
||||
console.log('ETHTornado\'s address ', tornado.address)
|
||||
})
|
||||
}
|
6
migrations/4_deploy_fee_manager.js
Normal file
6
migrations/4_deploy_fee_manager.js
Normal file
@ -0,0 +1,6 @@
|
||||
/* global artifacts */
|
||||
const FeeManager = artifacts.require('FeeManager')
|
||||
|
||||
module.exports = function (deployer, network, accounts) {
|
||||
deployer.deploy(FeeManager, accounts[0])
|
||||
}
|
@ -1,25 +1,28 @@
|
||||
/* global artifacts */
|
||||
require('dotenv').config({ path: '../.env' })
|
||||
require('dotenv').config({path: '../.env'})
|
||||
const ERC20Tornado = artifacts.require('ERC20Tornado')
|
||||
const Verifier = artifacts.require('Verifier')
|
||||
const FeeManager = artifacts.require('FeeManager')
|
||||
const hasherContract = artifacts.require('Hasher')
|
||||
const ERC20Mock = artifacts.require('ERC20Mock')
|
||||
|
||||
|
||||
module.exports = function(deployer, network, accounts) {
|
||||
module.exports = function (deployer, network, accounts) {
|
||||
return deployer.then(async () => {
|
||||
const { MERKLE_TREE_HEIGHT, ERC20_TOKEN, TOKEN_AMOUNT } = process.env
|
||||
const {MERKLE_TREE_HEIGHT, ERC20_TOKEN, TOKEN_AMOUNT} = process.env
|
||||
const verifier = await Verifier.deployed()
|
||||
const feeManager = await FeeManager.deployed()
|
||||
const hasherInstance = await hasherContract.deployed()
|
||||
await ERC20Tornado.link(hasherContract, hasherInstance.address)
|
||||
let token = ERC20_TOKEN
|
||||
if(token === '') {
|
||||
if (token === '') {
|
||||
const tokenInstance = await deployer.deploy(ERC20Mock)
|
||||
token = tokenInstance.address
|
||||
}
|
||||
const tornado = await deployer.deploy(
|
||||
ERC20Tornado,
|
||||
verifier.address,
|
||||
feeManager.address,
|
||||
TOKEN_AMOUNT,
|
||||
MERKLE_TREE_HEIGHT,
|
||||
accounts[0],
|
||||
|
@ -9,10 +9,11 @@ const { toBN } = require('web3-utils')
|
||||
const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper')
|
||||
|
||||
const Tornado = artifacts.require('./ERC20Tornado.sol')
|
||||
const FeeManager = artifacts.require('./FeeManager.sol')
|
||||
const BadRecipient = artifacts.require('./BadRecipient.sol')
|
||||
const Token = artifacts.require('./ERC20Mock.sol')
|
||||
const USDTToken = artifacts.require('./IUSDT.sol')
|
||||
const { ETH_AMOUNT, TOKEN_AMOUNT, MERKLE_TREE_HEIGHT, ERC20_TOKEN } = process.env
|
||||
const { CELO_AMOUNT, TOKEN_AMOUNT, MERKLE_TREE_HEIGHT, ERC20_TOKEN } = process.env
|
||||
|
||||
const websnarkUtils = require('websnark/src/utils')
|
||||
const buildGroth16 = require('websnark/src/groth16')
|
||||
@ -40,6 +41,7 @@ function generateDeposit() {
|
||||
|
||||
contract('ERC20Tornado', accounts => {
|
||||
let tornado
|
||||
let feeManager
|
||||
let token
|
||||
let usdtToken
|
||||
let badRecipient
|
||||
@ -50,8 +52,8 @@ contract('ERC20Tornado', accounts => {
|
||||
let snapshotId
|
||||
let prefix = 'test'
|
||||
let tree
|
||||
const fee = bigInt(ETH_AMOUNT).shr(1) || bigInt(1e17)
|
||||
const refund = ETH_AMOUNT || '1000000000000000000' // 1 ether
|
||||
const fee = bigInt(CELO_AMOUNT).shr(1) || bigInt(1e17)
|
||||
const refund = CELO_AMOUNT || '1000000000000000000' // 1 ether
|
||||
let recipient = getRandomRecipient()
|
||||
const relayer = accounts[1]
|
||||
let groth16
|
||||
@ -72,6 +74,7 @@ contract('ERC20Tornado', accounts => {
|
||||
token = await Token.deployed()
|
||||
await token.mint(sender, tokenDenomination)
|
||||
}
|
||||
feeManager = await FeeManager.deployed()
|
||||
badRecipient = await BadRecipient.new()
|
||||
snapshotId = await takeSnapshot()
|
||||
groth16 = await buildGroth16()
|
||||
@ -191,6 +194,131 @@ contract('ERC20Tornado', accounts => {
|
||||
isSpent.should.be.equal(true)
|
||||
})
|
||||
|
||||
it("should give fees when feeTo is set", async () => {
|
||||
await feeManager.setFeeTo(accounts[5]);
|
||||
const balanceFeeToBefore = await token.balanceOf(accounts[5]);
|
||||
|
||||
const deposit = generateDeposit();
|
||||
const user = accounts[4];
|
||||
await tree.insert(deposit.commitment);
|
||||
await token.mint(user, tokenDenomination);
|
||||
|
||||
const balanceUserBefore = await token.balanceOf(user);
|
||||
await token.approve(tornado.address, tokenDenomination, { from: user });
|
||||
// Uncomment to measure gas usage
|
||||
// let gas = await tornado.deposit.estimateGas(toBN(deposit.commitment.toString()), { from: user, gasPrice: '0' })
|
||||
// console.log('deposit gas:', gas)
|
||||
await tornado.deposit(toFixedHex(deposit.commitment), {
|
||||
from: user,
|
||||
gasPrice: "0",
|
||||
});
|
||||
|
||||
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)),
|
||||
relayer,
|
||||
recipient,
|
||||
fee,
|
||||
refund,
|
||||
|
||||
// private
|
||||
nullifier: deposit.nullifier,
|
||||
secret: deposit.secret,
|
||||
pathElements: path_elements,
|
||||
pathIndices: path_index,
|
||||
});
|
||||
|
||||
const proofData = await websnarkUtils.genWitnessAndProve(
|
||||
groth16,
|
||||
input,
|
||||
circuit,
|
||||
proving_key
|
||||
);
|
||||
const { proof } = websnarkUtils.toSolidityInput(proofData);
|
||||
|
||||
const balanceTornadoBefore = await token.balanceOf(tornado.address);
|
||||
const balanceRelayerBefore = await token.balanceOf(relayer);
|
||||
const balanceRecieverBefore = await token.balanceOf(
|
||||
toFixedHex(recipient, 20)
|
||||
);
|
||||
|
||||
const ethBalanceOperatorBefore = await web3.eth.getBalance(operator);
|
||||
const ethBalanceRecieverBefore = await web3.eth.getBalance(
|
||||
toFixedHex(recipient, 20)
|
||||
);
|
||||
const ethBalanceRelayerBefore = await web3.eth.getBalance(relayer);
|
||||
let isSpent = await tornado.isSpent(toFixedHex(input.nullifierHash));
|
||||
isSpent.should.be.equal(false);
|
||||
// Uncomment to measure gas usage
|
||||
// gas = await tornado.withdraw.estimateGas(proof, publicSignals, { from: relayer, gasPrice: '0' })
|
||||
// console.log('withdraw gas:', gas)
|
||||
const args = [
|
||||
toFixedHex(input.root),
|
||||
toFixedHex(input.nullifierHash),
|
||||
toFixedHex(input.recipient, 20),
|
||||
toFixedHex(input.relayer, 20),
|
||||
toFixedHex(input.fee),
|
||||
toFixedHex(input.refund),
|
||||
];
|
||||
const { logs } = await tornado.withdraw(proof, ...args, {
|
||||
value: refund,
|
||||
from: relayer,
|
||||
gasPrice: "0",
|
||||
});
|
||||
|
||||
const balanceTornadoAfter = await token.balanceOf(tornado.address);
|
||||
const balanceRelayerAfter = await token.balanceOf(relayer);
|
||||
const ethBalanceOperatorAfter = await web3.eth.getBalance(operator);
|
||||
const balanceRecieverAfter = await token.balanceOf(
|
||||
toFixedHex(recipient, 20)
|
||||
);
|
||||
const ethBalanceRecieverAfter = await web3.eth.getBalance(
|
||||
toFixedHex(recipient, 20)
|
||||
);
|
||||
const ethBalanceRelayerAfter = await web3.eth.getBalance(relayer);
|
||||
const balanceFeeToAfter = await token.balanceOf(accounts[5]);
|
||||
const feeBN = toBN(fee.toString());
|
||||
const feeToFee = toBN(tokenDenomination).div(toBN(200));
|
||||
balanceTornadoAfter.should.be.eq.BN(
|
||||
toBN(balanceTornadoBefore).sub(toBN(tokenDenomination))
|
||||
);
|
||||
balanceRelayerAfter.should.be.eq.BN(
|
||||
toBN(balanceRelayerBefore).add(feeBN)
|
||||
);
|
||||
balanceRecieverAfter.should.be.eq.BN(
|
||||
toBN(balanceRecieverBefore).add(
|
||||
toBN(tokenDenomination).sub(feeBN).sub(feeToFee)
|
||||
)
|
||||
);
|
||||
|
||||
ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore));
|
||||
ethBalanceRecieverAfter.should.be.eq.BN(
|
||||
toBN(ethBalanceRecieverBefore).add(toBN(refund))
|
||||
);
|
||||
ethBalanceRelayerAfter.should.be.eq.BN(
|
||||
toBN(ethBalanceRelayerBefore).sub(toBN(refund))
|
||||
);
|
||||
|
||||
balanceFeeToAfter.sub(balanceFeeToBefore).should.be.eq.BN(feeToFee);
|
||||
|
||||
logs[0].event.should.be.equal("Withdrawal");
|
||||
logs[0].args.nullifierHash.should.be.equal(
|
||||
toFixedHex(input.nullifierHash)
|
||||
);
|
||||
logs[0].args.relayer.should.be.eq.BN(relayer);
|
||||
logs[0].args.fee.should.be.eq.BN(feeBN);
|
||||
isSpent = await tornado.isSpent(toFixedHex(input.nullifierHash));
|
||||
isSpent.should.be.equal(true);
|
||||
});
|
||||
|
||||
it('should return refund to the relayer is case of fail', async () => {
|
||||
const deposit = generateDeposit()
|
||||
const user = accounts[4]
|
||||
|
@ -1,592 +0,0 @@
|
||||
/* global artifacts, web3, contract */
|
||||
require('chai')
|
||||
.use(require('bn-chai')(web3.utils.BN))
|
||||
.use(require('chai-as-promised'))
|
||||
.should()
|
||||
const fs = require('fs')
|
||||
|
||||
const { toBN, randomHex } = require('web3-utils')
|
||||
const { takeSnapshot, revertSnapshot } = require('../lib/ganacheHelper')
|
||||
|
||||
const Tornado = artifacts.require('./ETHTornado.sol')
|
||||
const { ETH_AMOUNT, MERKLE_TREE_HEIGHT } = process.env
|
||||
|
||||
const websnarkUtils = require('websnark/src/utils')
|
||||
const buildGroth16 = require('websnark/src/groth16')
|
||||
const stringifyBigInts = require('websnark/tools/stringifybigint').stringifyBigInts
|
||||
const unstringifyBigInts2 = require('snarkjs/src/stringifybigint').unstringifyBigInts
|
||||
const snarkjs = require('snarkjs')
|
||||
const bigInt = snarkjs.bigInt
|
||||
const crypto = require('crypto')
|
||||
const circomlib = require('circomlib')
|
||||
const MerkleTree = require('../lib/MerkleTree')
|
||||
|
||||
const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes))
|
||||
const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0]
|
||||
const toFixedHex = (number, length = 32) => '0x' + bigInt(number).toString(16).padStart(length * 2, '0')
|
||||
const getRandomRecipient = () => rbigint(20)
|
||||
|
||||
function generateDeposit() {
|
||||
let deposit = {
|
||||
secret: rbigint(31),
|
||||
nullifier: rbigint(31),
|
||||
}
|
||||
const preimage = Buffer.concat([deposit.nullifier.leInt2Buff(31), deposit.secret.leInt2Buff(31)])
|
||||
deposit.commitment = pedersenHash(preimage)
|
||||
return deposit
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function BNArrayToStringArray(array) {
|
||||
const arrayToPrint = []
|
||||
array.forEach(item => {
|
||||
arrayToPrint.push(item.toString())
|
||||
})
|
||||
return arrayToPrint
|
||||
}
|
||||
|
||||
function snarkVerify(proof) {
|
||||
proof = unstringifyBigInts2(proof)
|
||||
const verification_key = unstringifyBigInts2(require('../build/circuits/withdraw_verification_key.json'))
|
||||
return snarkjs['groth'].isValid(verification_key, proof, proof.publicSignals)
|
||||
}
|
||||
|
||||
contract('ETHTornado', accounts => {
|
||||
let tornado
|
||||
const sender = accounts[0]
|
||||
const operator = accounts[0]
|
||||
const levels = MERKLE_TREE_HEIGHT || 16
|
||||
const value = ETH_AMOUNT || '1000000000000000000' // 1 ether
|
||||
let snapshotId
|
||||
let prefix = 'test'
|
||||
let tree
|
||||
const fee = bigInt(ETH_AMOUNT).shr(1) || bigInt(1e17)
|
||||
const refund = bigInt(0)
|
||||
const recipient = getRandomRecipient()
|
||||
const relayer = accounts[1]
|
||||
let groth16
|
||||
let circuit
|
||||
let proving_key
|
||||
|
||||
before(async () => {
|
||||
tree = new MerkleTree(
|
||||
levels,
|
||||
null,
|
||||
prefix,
|
||||
)
|
||||
tornado = await Tornado.deployed()
|
||||
snapshotId = await takeSnapshot()
|
||||
groth16 = await buildGroth16()
|
||||
circuit = require('../build/circuits/withdraw.json')
|
||||
proving_key = fs.readFileSync('build/circuits/withdraw_proving_key.bin').buffer
|
||||
})
|
||||
|
||||
describe('#constructor', () => {
|
||||
it('should initialize', async () => {
|
||||
const etherDenomination = await tornado.denomination()
|
||||
etherDenomination.should.be.eq.BN(toBN(value))
|
||||
})
|
||||
})
|
||||
|
||||
describe('#deposit', () => {
|
||||
it('should emit event', async () => {
|
||||
let commitment = toFixedHex(42)
|
||||
let { logs } = await tornado.deposit(commitment, { value, from: sender })
|
||||
|
||||
logs[0].event.should.be.equal('Deposit')
|
||||
logs[0].args.commitment.should.be.equal(commitment)
|
||||
logs[0].args.leafIndex.should.be.eq.BN(0)
|
||||
|
||||
commitment = toFixedHex(12);
|
||||
({ logs } = await tornado.deposit(commitment, { value, from: accounts[2] }))
|
||||
|
||||
logs[0].event.should.be.equal('Deposit')
|
||||
logs[0].args.commitment.should.be.equal(commitment)
|
||||
logs[0].args.leafIndex.should.be.eq.BN(1)
|
||||
})
|
||||
|
||||
it('should throw if there is a such commitment', async () => {
|
||||
const commitment = toFixedHex(42)
|
||||
await tornado.deposit(commitment, { value, from: sender }).should.be.fulfilled
|
||||
const error = await tornado.deposit(commitment, { value, from: sender }).should.be.rejected
|
||||
error.reason.should.be.equal('The commitment has been submitted')
|
||||
})
|
||||
})
|
||||
|
||||
describe('snark proof verification on js side', () => {
|
||||
it('should detect tampering', async () => {
|
||||
const deposit = generateDeposit()
|
||||
await tree.insert(deposit.commitment)
|
||||
const { root, path_elements, path_index } = await tree.path(0)
|
||||
|
||||
const input = stringifyBigInts({
|
||||
root,
|
||||
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
|
||||
nullifier: deposit.nullifier,
|
||||
relayer: operator,
|
||||
recipient,
|
||||
fee,
|
||||
refund,
|
||||
secret: deposit.secret,
|
||||
pathElements: path_elements,
|
||||
pathIndices: path_index,
|
||||
})
|
||||
|
||||
let proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||
const originalProof = JSON.parse(JSON.stringify(proofData))
|
||||
let result = snarkVerify(proofData)
|
||||
result.should.be.equal(true)
|
||||
|
||||
// nullifier
|
||||
proofData.publicSignals[1] = '133792158246920651341275668520530514036799294649489851421007411546007850802'
|
||||
result = snarkVerify(proofData)
|
||||
result.should.be.equal(false)
|
||||
proofData = originalProof
|
||||
|
||||
// try to cheat with recipient
|
||||
proofData.publicSignals[2] = '133738360804642228759657445999390850076318544422'
|
||||
result = snarkVerify(proofData)
|
||||
result.should.be.equal(false)
|
||||
proofData = originalProof
|
||||
|
||||
// fee
|
||||
proofData.publicSignals[3] = '1337100000000000000000'
|
||||
result = snarkVerify(proofData)
|
||||
result.should.be.equal(false)
|
||||
proofData = originalProof
|
||||
})
|
||||
})
|
||||
|
||||
describe('#withdraw', () => {
|
||||
it('should work', async () => {
|
||||
const deposit = generateDeposit()
|
||||
const user = accounts[4]
|
||||
await tree.insert(deposit.commitment)
|
||||
|
||||
const balanceUserBefore = await web3.eth.getBalance(user)
|
||||
|
||||
// Uncomment to measure gas usage
|
||||
// let gas = await tornado.deposit.estimateGas(toBN(deposit.commitment.toString()), { value, from: user, gasPrice: '0' })
|
||||
// console.log('deposit gas:', gas)
|
||||
await tornado.deposit(toFixedHex(deposit.commitment), { value, from: user, gasPrice: '0' })
|
||||
|
||||
const balanceUserAfter = await web3.eth.getBalance(user)
|
||||
balanceUserAfter.should.be.eq.BN(toBN(balanceUserBefore).sub(toBN(value)))
|
||||
|
||||
const { root, path_elements, path_index } = await tree.path(0)
|
||||
|
||||
// Circuit input
|
||||
const input = stringifyBigInts({
|
||||
// public
|
||||
root,
|
||||
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
|
||||
relayer: operator,
|
||||
recipient,
|
||||
fee,
|
||||
refund,
|
||||
|
||||
// private
|
||||
nullifier: deposit.nullifier,
|
||||
secret: deposit.secret,
|
||||
pathElements: path_elements,
|
||||
pathIndices: path_index,
|
||||
})
|
||||
|
||||
|
||||
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||
const { proof } = websnarkUtils.toSolidityInput(proofData)
|
||||
|
||||
const balanceTornadoBefore = await web3.eth.getBalance(tornado.address)
|
||||
const balanceRelayerBefore = await web3.eth.getBalance(relayer)
|
||||
const balanceOperatorBefore = await web3.eth.getBalance(operator)
|
||||
const balanceRecieverBefore = await web3.eth.getBalance(toFixedHex(recipient, 20))
|
||||
let isSpent = await tornado.isSpent(toFixedHex(input.nullifierHash))
|
||||
isSpent.should.be.equal(false)
|
||||
|
||||
// Uncomment to measure gas usage
|
||||
// gas = await tornado.withdraw.estimateGas(proof, publicSignals, { from: relayer, gasPrice: '0' })
|
||||
// console.log('withdraw gas:', gas)
|
||||
const args = [
|
||||
toFixedHex(input.root),
|
||||
toFixedHex(input.nullifierHash),
|
||||
toFixedHex(input.recipient, 20),
|
||||
toFixedHex(input.relayer, 20),
|
||||
toFixedHex(input.fee),
|
||||
toFixedHex(input.refund)
|
||||
]
|
||||
const { logs } = await tornado.withdraw(proof, ...args, { from: relayer, gasPrice: '0' })
|
||||
|
||||
const balanceTornadoAfter = await web3.eth.getBalance(tornado.address)
|
||||
const balanceRelayerAfter = await web3.eth.getBalance(relayer)
|
||||
const balanceOperatorAfter = await web3.eth.getBalance(operator)
|
||||
const balanceRecieverAfter = await web3.eth.getBalance(toFixedHex(recipient, 20))
|
||||
const feeBN = toBN(fee.toString())
|
||||
balanceTornadoAfter.should.be.eq.BN(toBN(balanceTornadoBefore).sub(toBN(value)))
|
||||
balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore))
|
||||
balanceOperatorAfter.should.be.eq.BN(toBN(balanceOperatorBefore).add(feeBN))
|
||||
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(value)).sub(feeBN))
|
||||
|
||||
|
||||
logs[0].event.should.be.equal('Withdrawal')
|
||||
logs[0].args.nullifierHash.should.be.equal(toFixedHex(input.nullifierHash))
|
||||
logs[0].args.relayer.should.be.eq.BN(operator)
|
||||
logs[0].args.fee.should.be.eq.BN(feeBN)
|
||||
isSpent = await tornado.isSpent(toFixedHex(input.nullifierHash))
|
||||
isSpent.should.be.equal(true)
|
||||
})
|
||||
|
||||
it('should prevent double spend', async () => {
|
||||
const deposit = generateDeposit()
|
||||
await tree.insert(deposit.commitment)
|
||||
await tornado.deposit(toFixedHex(deposit.commitment), { value, from: sender })
|
||||
|
||||
const { root, path_elements, path_index } = await tree.path(0)
|
||||
|
||||
const input = stringifyBigInts({
|
||||
root,
|
||||
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
|
||||
nullifier: deposit.nullifier,
|
||||
relayer: operator,
|
||||
recipient,
|
||||
fee,
|
||||
refund,
|
||||
secret: deposit.secret,
|
||||
pathElements: path_elements,
|
||||
pathIndices: path_index,
|
||||
})
|
||||
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||
const { proof } = websnarkUtils.toSolidityInput(proofData)
|
||||
const args = [
|
||||
toFixedHex(input.root),
|
||||
toFixedHex(input.nullifierHash),
|
||||
toFixedHex(input.recipient, 20),
|
||||
toFixedHex(input.relayer, 20),
|
||||
toFixedHex(input.fee),
|
||||
toFixedHex(input.refund)
|
||||
]
|
||||
await tornado.withdraw(proof, ...args, { from: relayer }).should.be.fulfilled
|
||||
const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
|
||||
error.reason.should.be.equal('The note has been already spent')
|
||||
})
|
||||
|
||||
it('should prevent double spend with overflow', async () => {
|
||||
const deposit = generateDeposit()
|
||||
await tree.insert(deposit.commitment)
|
||||
await tornado.deposit(toFixedHex(deposit.commitment), { value, from: sender })
|
||||
|
||||
const { root, path_elements, path_index } = await tree.path(0)
|
||||
|
||||
const input = stringifyBigInts({
|
||||
root,
|
||||
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
|
||||
nullifier: deposit.nullifier,
|
||||
relayer: operator,
|
||||
recipient,
|
||||
fee,
|
||||
refund,
|
||||
secret: deposit.secret,
|
||||
pathElements: path_elements,
|
||||
pathIndices: path_index,
|
||||
})
|
||||
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||
const { proof } = websnarkUtils.toSolidityInput(proofData)
|
||||
const args = [
|
||||
toFixedHex(input.root),
|
||||
toFixedHex(toBN(input.nullifierHash).add(toBN('21888242871839275222246405745257275088548364400416034343698204186575808495617'))),
|
||||
toFixedHex(input.recipient, 20),
|
||||
toFixedHex(input.relayer, 20),
|
||||
toFixedHex(input.fee),
|
||||
toFixedHex(input.refund)
|
||||
]
|
||||
const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
|
||||
error.reason.should.be.equal('verifier-gte-snark-scalar-field')
|
||||
})
|
||||
|
||||
it('fee should be less or equal transfer value', async () => {
|
||||
const deposit = generateDeposit()
|
||||
await tree.insert(deposit.commitment)
|
||||
await tornado.deposit(toFixedHex(deposit.commitment), { value, from: sender })
|
||||
|
||||
const { root, path_elements, path_index } = await tree.path(0)
|
||||
const largeFee = bigInt(value).add(bigInt(1))
|
||||
const input = stringifyBigInts({
|
||||
root,
|
||||
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
|
||||
nullifier: deposit.nullifier,
|
||||
relayer: operator,
|
||||
recipient,
|
||||
fee: largeFee,
|
||||
refund,
|
||||
secret: deposit.secret,
|
||||
pathElements: path_elements,
|
||||
pathIndices: path_index,
|
||||
})
|
||||
|
||||
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||
const { proof } = websnarkUtils.toSolidityInput(proofData)
|
||||
const args = [
|
||||
toFixedHex(input.root),
|
||||
toFixedHex(input.nullifierHash),
|
||||
toFixedHex(input.recipient, 20),
|
||||
toFixedHex(input.relayer, 20),
|
||||
toFixedHex(input.fee),
|
||||
toFixedHex(input.refund)
|
||||
]
|
||||
const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
|
||||
error.reason.should.be.equal('Fee exceeds transfer value')
|
||||
})
|
||||
|
||||
it('should throw for corrupted merkle tree root', async () => {
|
||||
const deposit = generateDeposit()
|
||||
await tree.insert(deposit.commitment)
|
||||
await tornado.deposit(toFixedHex(deposit.commitment), { value, from: sender })
|
||||
|
||||
const { root, path_elements, path_index } = await tree.path(0)
|
||||
|
||||
const input = stringifyBigInts({
|
||||
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
|
||||
root,
|
||||
nullifier: deposit.nullifier,
|
||||
relayer: operator,
|
||||
recipient,
|
||||
fee,
|
||||
refund,
|
||||
secret: deposit.secret,
|
||||
pathElements: path_elements,
|
||||
pathIndices: path_index,
|
||||
})
|
||||
|
||||
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||
const { proof } = websnarkUtils.toSolidityInput(proofData)
|
||||
|
||||
const args = [
|
||||
toFixedHex(randomHex(32)),
|
||||
toFixedHex(input.nullifierHash),
|
||||
toFixedHex(input.recipient, 20),
|
||||
toFixedHex(input.relayer, 20),
|
||||
toFixedHex(input.fee),
|
||||
toFixedHex(input.refund)
|
||||
]
|
||||
const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
|
||||
error.reason.should.be.equal('Cannot find your merkle root')
|
||||
})
|
||||
|
||||
it('should reject with tampered public inputs', async () => {
|
||||
const deposit = generateDeposit()
|
||||
await tree.insert(deposit.commitment)
|
||||
await tornado.deposit(toFixedHex(deposit.commitment), { value, from: sender })
|
||||
|
||||
let { root, path_elements, path_index } = await tree.path(0)
|
||||
|
||||
const input = stringifyBigInts({
|
||||
root,
|
||||
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
|
||||
nullifier: deposit.nullifier,
|
||||
relayer: operator,
|
||||
recipient,
|
||||
fee,
|
||||
refund,
|
||||
secret: deposit.secret,
|
||||
pathElements: path_elements,
|
||||
pathIndices: path_index,
|
||||
})
|
||||
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||
let { proof } = websnarkUtils.toSolidityInput(proofData)
|
||||
const args = [
|
||||
toFixedHex(input.root),
|
||||
toFixedHex(input.nullifierHash),
|
||||
toFixedHex(input.recipient, 20),
|
||||
toFixedHex(input.relayer, 20),
|
||||
toFixedHex(input.fee),
|
||||
toFixedHex(input.refund)
|
||||
]
|
||||
let incorrectArgs
|
||||
const originalProof = proof.slice()
|
||||
|
||||
// recipient
|
||||
incorrectArgs = [
|
||||
toFixedHex(input.root),
|
||||
toFixedHex(input.nullifierHash),
|
||||
toFixedHex('0x0000000000000000000000007a1f9131357404ef86d7c38dbffed2da70321337', 20),
|
||||
toFixedHex(input.relayer, 20),
|
||||
toFixedHex(input.fee),
|
||||
toFixedHex(input.refund)
|
||||
]
|
||||
let error = await tornado.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected
|
||||
error.reason.should.be.equal('Invalid withdraw proof')
|
||||
|
||||
// fee
|
||||
incorrectArgs = [
|
||||
toFixedHex(input.root),
|
||||
toFixedHex(input.nullifierHash),
|
||||
toFixedHex(input.recipient, 20),
|
||||
toFixedHex(input.relayer, 20),
|
||||
toFixedHex('0x000000000000000000000000000000000000000000000000015345785d8a0000'),
|
||||
toFixedHex(input.refund)
|
||||
]
|
||||
error = await tornado.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected
|
||||
error.reason.should.be.equal('Invalid withdraw proof')
|
||||
|
||||
// nullifier
|
||||
incorrectArgs = [
|
||||
toFixedHex(input.root),
|
||||
toFixedHex('0x00abdfc78211f8807b9c6504a6e537e71b8788b2f529a95f1399ce124a8642ad'),
|
||||
toFixedHex(input.recipient, 20),
|
||||
toFixedHex(input.relayer, 20),
|
||||
toFixedHex(input.fee),
|
||||
toFixedHex(input.refund)
|
||||
]
|
||||
error = await tornado.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected
|
||||
error.reason.should.be.equal('Invalid withdraw proof')
|
||||
|
||||
// proof itself
|
||||
proof = '0xbeef' + proof.substr(6)
|
||||
await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
|
||||
|
||||
// should work with original values
|
||||
await tornado.withdraw(originalProof, ...args, { from: relayer }).should.be.fulfilled
|
||||
})
|
||||
|
||||
it('should reject with non zero refund', async () => {
|
||||
const deposit = generateDeposit()
|
||||
await tree.insert(deposit.commitment)
|
||||
await tornado.deposit(toFixedHex(deposit.commitment), { value, from: sender })
|
||||
|
||||
const { root, path_elements, path_index } = await tree.path(0)
|
||||
|
||||
const input = stringifyBigInts({
|
||||
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
|
||||
root,
|
||||
nullifier: deposit.nullifier,
|
||||
relayer: operator,
|
||||
recipient,
|
||||
fee,
|
||||
refund: bigInt(1),
|
||||
secret: deposit.secret,
|
||||
pathElements: path_elements,
|
||||
pathIndices: path_index,
|
||||
})
|
||||
|
||||
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||
const { proof } = websnarkUtils.toSolidityInput(proofData)
|
||||
|
||||
const args = [
|
||||
toFixedHex(input.root),
|
||||
toFixedHex(input.nullifierHash),
|
||||
toFixedHex(input.recipient, 20),
|
||||
toFixedHex(input.relayer, 20),
|
||||
toFixedHex(input.fee),
|
||||
toFixedHex(input.refund)
|
||||
]
|
||||
const error = await tornado.withdraw(proof, ...args, { from: relayer }).should.be.rejected
|
||||
error.reason.should.be.equal('Refund value is supposed to be zero for ETH instance')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#changeOperator', () => {
|
||||
it('should work', async () => {
|
||||
let operator = await tornado.operator()
|
||||
operator.should.be.equal(sender)
|
||||
|
||||
const newOperator = accounts[7]
|
||||
await tornado.changeOperator(newOperator).should.be.fulfilled
|
||||
|
||||
operator = await tornado.operator()
|
||||
operator.should.be.equal(newOperator)
|
||||
})
|
||||
|
||||
it('cannot change from different address', async () => {
|
||||
let operator = await tornado.operator()
|
||||
operator.should.be.equal(sender)
|
||||
|
||||
const newOperator = accounts[7]
|
||||
const error = await tornado.changeOperator(newOperator, { from: accounts[7] }).should.be.rejected
|
||||
error.reason.should.be.equal('Only operator can call this function.')
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
describe('#updateVerifier', () => {
|
||||
it('should work', async () => {
|
||||
let operator = await tornado.operator()
|
||||
operator.should.be.equal(sender)
|
||||
|
||||
const newVerifier = accounts[7]
|
||||
await tornado.updateVerifier(newVerifier).should.be.fulfilled
|
||||
|
||||
const verifier = await tornado.verifier()
|
||||
verifier.should.be.equal(newVerifier)
|
||||
})
|
||||
|
||||
it('cannot change from different address', async () => {
|
||||
let operator = await tornado.operator()
|
||||
operator.should.be.equal(sender)
|
||||
|
||||
const newVerifier = accounts[7]
|
||||
const error = await tornado.updateVerifier(newVerifier, { from: accounts[7] }).should.be.rejected
|
||||
error.reason.should.be.equal('Only operator can call this function.')
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
describe('#isSpent', () => {
|
||||
it('should work', async () => {
|
||||
const deposit1 = generateDeposit()
|
||||
const deposit2 = generateDeposit()
|
||||
await tree.insert(deposit1.commitment)
|
||||
await tree.insert(deposit2.commitment)
|
||||
await tornado.deposit(toFixedHex(deposit1.commitment), { value, gasPrice: '0' })
|
||||
await tornado.deposit(toFixedHex(deposit2.commitment), { value, gasPrice: '0' })
|
||||
|
||||
const { root, path_elements, path_index } = await tree.path(1)
|
||||
|
||||
// Circuit input
|
||||
const input = stringifyBigInts({
|
||||
// public
|
||||
root,
|
||||
nullifierHash: pedersenHash(deposit2.nullifier.leInt2Buff(31)),
|
||||
relayer: operator,
|
||||
recipient,
|
||||
fee,
|
||||
refund,
|
||||
|
||||
// private
|
||||
nullifier: deposit2.nullifier,
|
||||
secret: deposit2.secret,
|
||||
pathElements: path_elements,
|
||||
pathIndices: path_index,
|
||||
})
|
||||
|
||||
|
||||
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key)
|
||||
const { proof } = websnarkUtils.toSolidityInput(proofData)
|
||||
|
||||
const args = [
|
||||
toFixedHex(input.root),
|
||||
toFixedHex(input.nullifierHash),
|
||||
toFixedHex(input.recipient, 20),
|
||||
toFixedHex(input.relayer, 20),
|
||||
toFixedHex(input.fee),
|
||||
toFixedHex(input.refund)
|
||||
]
|
||||
|
||||
await tornado.withdraw(proof, ...args, { from: relayer, gasPrice: '0' })
|
||||
|
||||
const nullifierHash1 = toFixedHex(pedersenHash(deposit1.nullifier.leInt2Buff(31)))
|
||||
const nullifierHash2 = toFixedHex(pedersenHash(deposit2.nullifier.leInt2Buff(31)))
|
||||
const spentArray = await tornado.isSpentArray([nullifierHash1, nullifierHash2])
|
||||
spentArray.should.be.deep.equal([false, true])
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await revertSnapshot(snapshotId.result)
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
snapshotId = await takeSnapshot()
|
||||
tree = new MerkleTree(
|
||||
levels,
|
||||
null,
|
||||
prefix,
|
||||
)
|
||||
})
|
||||
})
|
@ -10,6 +10,7 @@ const path = require('path')
|
||||
const web3 = new Web3(process.env.RPC_URL)
|
||||
const kit = ContractKit.newKitFromWeb3(web3)
|
||||
kit.addAccount(process.env.PRIVATE_KEY)
|
||||
// const kit = Kit.newKit('https://forno.celo.org') // mainnet endpoint
|
||||
|
||||
// const infuraKey = "fj4jll3k.....";
|
||||
//
|
||||
@ -84,11 +85,15 @@ module.exports = {
|
||||
// CELO networks
|
||||
alfajores: {
|
||||
provider: kit.web3.currentProvider,
|
||||
network_id: 44787
|
||||
network_id: 44787,
|
||||
gas: 6000000,
|
||||
gasPrice: utils.toWei('0.1', 'gwei'),
|
||||
},
|
||||
mainnet: {
|
||||
provider: kit.web3.currentProvider,
|
||||
network_id: 42220
|
||||
network_id: 42220,
|
||||
gas: 6000000,
|
||||
gasPrice: utils.toWei('0.1', 'gwei'),
|
||||
}
|
||||
|
||||
// Useful for private networks
|
||||
|
Loading…
x
Reference in New Issue
Block a user