mirror of
https://github.com/tornadocash/tornado-pool-factory
synced 2024-02-02 15:04:08 +01:00
add instance factory tests
This commit is contained in:
parent
35004bc184
commit
41ddd8023d
15
.env.example
15
.env.example
@ -1,14 +1,3 @@
|
||||
ETHERSCAN_KEY=
|
||||
ALCHEMY_KEY=
|
||||
etherscan_api_key=
|
||||
goerli_rpc_key=
|
||||
mainnet_rpc_key=
|
||||
goerli_account_pk=
|
||||
mainnet_account_pk=
|
||||
use_latest_block=false
|
||||
|
||||
PROXY=0x722122dF12D4e14e13Ac3b6895a86e84145b6967
|
||||
DEPLOYER=0xCEe71753C9820f063b38FDbE4cFDAf1d3D928A80
|
||||
HASHER=0x83584f83f26aF4eDDA9CBe8C730bc87C364b28fe
|
||||
VERIFIER=0xce172ce1F20EC0B3728c9965470eaf994A03557A
|
||||
SALT=0x0000000000000000000000000000000000000000000000000000000047941987
|
||||
COMP_ADDRESS=0xc00e94Cb662C3520282E6f5717214004A7f26888
|
||||
PRIVATE_KEY=
|
||||
|
@ -1,8 +0,0 @@
|
||||
use_latest_block=false
|
||||
|
||||
PROXY=0x722122dF12D4e14e13Ac3b6895a86e84145b6967
|
||||
DEPLOYER=0xCEe71753C9820f063b38FDbE4cFDAf1d3D928A80
|
||||
HASHER=0x83584f83f26aF4eDDA9CBe8C730bc87C364b28fe
|
||||
VERIFIER=0xce172ce1F20EC0B3728c9965470eaf994A03557A
|
||||
SALT=0x0000000000000000000000000000000000000000000000000000000047941987
|
||||
COMP_ADDRESS=0xc00e94Cb662C3520282E6f5717214004A7f26888
|
12
config.js
Normal file
12
config.js
Normal file
@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
verifier: '0xce172ce1F20EC0B3728c9965470eaf994A03557A',
|
||||
hasher: '0x83584f83f26aF4eDDA9CBe8C730bc87C364b28fe',
|
||||
governance: '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
|
||||
instanceRegistry: '0xB20c66C4DE72433F3cE747b58B86830c459CA911',
|
||||
merkleTreeHeight: 20,
|
||||
singletonFactory: '0xce0042B868300000d44A59004Da54A005ffdcf9f',
|
||||
salt: '0x0000000000000000000000000000000000000000000000000000000047941987',
|
||||
COMP: '0xc00e94Cb662C3520282E6f5717214004A7f26888',
|
||||
TORN: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
|
||||
tornWhale: '0xF977814e90dA44bFA03b6295A0616a897441aceC',
|
||||
}
|
@ -12,7 +12,7 @@ contract AddInstanceProposal {
|
||||
address public immutable token;
|
||||
uint24 public immutable uniswapPoolSwappingFee;
|
||||
|
||||
uint256 internal immutable numInstances;
|
||||
uint256 public immutable numInstances;
|
||||
uint256 internal immutable denomination0;
|
||||
uint256 internal immutable denomination1;
|
||||
uint256 internal immutable denomination2;
|
||||
|
@ -10,11 +10,13 @@ import "./ERC20TornadoCloneable.sol";
|
||||
import "./AddInstanceProposal.sol";
|
||||
import "./interfaces/IGovernance.sol";
|
||||
|
||||
|
||||
contract InstanceFactory is Ownable {
|
||||
using Clones for address;
|
||||
using Address for address;
|
||||
|
||||
address immutable governance;
|
||||
address public immutable governance;
|
||||
address public immutable instanceRegistry;
|
||||
address public implementation;
|
||||
address public verifier;
|
||||
address public hasher;
|
||||
@ -25,19 +27,24 @@ contract InstanceFactory is Ownable {
|
||||
event NewTreeHeightSet(uint32 indexed newTreeHeight);
|
||||
event NewImplementationSet(address indexed newImplemenentation);
|
||||
event NewInstanceCloneCreated(address indexed clone);
|
||||
event NewGovernanceProposalCreated(address indexed proposal);
|
||||
|
||||
constructor(
|
||||
address _verifier,
|
||||
address _hasher,
|
||||
uint32 _merkleTreeHeight,
|
||||
address _governance
|
||||
address _governance,
|
||||
address _instanceRegistry
|
||||
) {
|
||||
verifier = _verifier;
|
||||
hasher = _hasher;
|
||||
merkleTreeHeight = _merkleTreeHeight;
|
||||
governance = _governance;
|
||||
instanceRegistry = _instanceRegistry;
|
||||
|
||||
ERC20TornadoCloneable implContract = new ERC20TornadoCloneable(_verifier, _hasher);
|
||||
implementation = address(implContract);
|
||||
governance = _governance;
|
||||
|
||||
transferOwnership(_governance);
|
||||
}
|
||||
|
||||
@ -59,26 +66,25 @@ contract InstanceFactory is Ownable {
|
||||
}
|
||||
|
||||
function createNewProposal(
|
||||
string calldata _description,
|
||||
address _instanceRegistry,
|
||||
address _token,
|
||||
uint24 _uniswapPoolSwappingFee,
|
||||
uint256[] memory _denominations,
|
||||
uint32[] memory _protocolFees
|
||||
) external returns (address) {
|
||||
// TODO test params
|
||||
require(_denominations.length == _protocolFees.length);
|
||||
require(_token.isContract(), "Token is not contract"); // TODO should we check that such instance already exist?
|
||||
require(_uniswapPoolSwappingFee > 0, "uniswapPoolSwappingFee is zero"); // TODO should we check > 0 ?
|
||||
require(_denominations.length > 0, "Empty denominations");
|
||||
require(_denominations.length == _protocolFees.length, "Incorrect denominations/fees length");
|
||||
|
||||
address proposal = address(new AddInstanceProposal(
|
||||
address(this),
|
||||
_instanceRegistry,
|
||||
instanceRegistry,
|
||||
_token,
|
||||
_uniswapPoolSwappingFee,
|
||||
_denominations,
|
||||
_protocolFees
|
||||
));
|
||||
|
||||
IGovernance(governance).propose(proposal, _description);
|
||||
emit NewGovernanceProposalCreated(proposal);
|
||||
|
||||
return proposal;
|
||||
}
|
||||
|
@ -3,5 +3,6 @@
|
||||
pragma solidity 0.6.12 || 0.7.6;
|
||||
|
||||
import { Governance } from "tornado-governance/contracts/v1/Governance.sol";
|
||||
import { InstanceRegistry } from "tornado-relayer-registry/contracts/tornado-proxy/InstanceRegistry.sol";
|
||||
|
||||
contract CompileDummy {}
|
||||
|
@ -1,13 +1,9 @@
|
||||
require('dotenv').config()
|
||||
require('@nomiclabs/hardhat-ethers')
|
||||
require('@nomiclabs/hardhat-etherscan')
|
||||
require('@nomiclabs/hardhat-waffle')
|
||||
require('@nomiclabs/hardhat-etherscan')
|
||||
require('hardhat-log-remover')
|
||||
require('solidity-coverage')
|
||||
|
||||
// require('./tasks/deploy_proposal.js')
|
||||
// require('./tasks/deploy_factory_proposal.js')
|
||||
// require('./tasks/propose_proposal.js')
|
||||
/**
|
||||
* @type import('hardhat/config').HardhatUserConfig
|
||||
*/
|
||||
@ -38,22 +34,17 @@ module.exports = {
|
||||
hardhat: {
|
||||
forking: {
|
||||
url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
|
||||
blockNumber: process.env.use_latest_block == 'true' ? undefined : 13017436,
|
||||
blockNumber: 14220000,
|
||||
},
|
||||
chainId: 1,
|
||||
initialBaseFeePerGas: 5,
|
||||
loggingEnabled: false,
|
||||
},
|
||||
localhost: {
|
||||
url: 'http://localhost:8545',
|
||||
timeout: 120000,
|
||||
allowUnlimitedContractSize: false,
|
||||
blockGasLimit: 50000000,
|
||||
},
|
||||
mainnet: {
|
||||
url: `https://mainnet.infura.io/v3/${process.env.mainnet_rpc_key}`,
|
||||
accounts: [`${process.env.mainnet_account_pk}`],
|
||||
timeout: 2147483647,
|
||||
},
|
||||
goerli: {
|
||||
url: `https://goerli.infura.io/v3/${process.env.goerli_rpc_key}`,
|
||||
accounts: [`${process.env.goerli_account_pk}`],
|
||||
url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
|
||||
accounts: [process.env.PRIVATE_KEY],
|
||||
timeout: 2147483647,
|
||||
},
|
||||
},
|
||||
@ -63,6 +54,6 @@ module.exports = {
|
||||
runOnCompile: true,
|
||||
},
|
||||
etherscan: {
|
||||
apiKey: `${process.env.etherscan_api_key}`,
|
||||
apiKey: `${process.env.ETHERSCAN_KEY}`,
|
||||
},
|
||||
}
|
||||
|
@ -18,7 +18,8 @@
|
||||
"torn-token": "^1.0.4",
|
||||
"tornado-cli": "^0.0.1",
|
||||
"tornado-core": "https://github.com/tornadocash/tornado-core.git",
|
||||
"tornado-governance": "2.0.0"
|
||||
"tornado-governance": "2.0.0",
|
||||
"tornado-relayer-registry": "https://github.com/tornadocash/tornado-relayer-registry.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.2",
|
||||
|
@ -1,22 +0,0 @@
|
||||
require('dotenv').config()
|
||||
const { ethers } = require('hardhat')
|
||||
|
||||
// THIS IS ASSUMING YOU HAVE ENOUGH LOCKED TORN TO PROPOSE
|
||||
async function propose(proposalArgs) {
|
||||
const proposer = proposalArgs[0]
|
||||
const ProposalContract = proposalArgs[1]
|
||||
|
||||
let GovernanceContract = await ethers.getContractAt(
|
||||
'../artifacts/tornado-governance/contracts/Governance.sol:Governance',
|
||||
'0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
|
||||
)
|
||||
GovernanceContract = await GovernanceContract.connect(proposer)
|
||||
|
||||
const response = await GovernanceContract.propose(ProposalContract.address, proposalArgs[2])
|
||||
|
||||
const id = await GovernanceContract.latestProposalIds(proposer.address)
|
||||
const state = await GovernanceContract.state(id)
|
||||
|
||||
return [response, id, state]
|
||||
}
|
||||
module.exports.propose = propose
|
@ -1,27 +0,0 @@
|
||||
require('dotenv').config()
|
||||
const { task } = require('hardhat/config')
|
||||
const { BigNumber } = require('@ethersproject/bignumber')
|
||||
const instancesData = require('../resources/instances')
|
||||
|
||||
task('deploy_factory_proposal', 'deploy the proposal that creates the factory').setAction(
|
||||
async (taskArgs, hre) => {
|
||||
const ProposalFactory = await hre.ethers.getContractFactory('CreateFactoryAndAddInstancesProposal')
|
||||
|
||||
let denominations = []
|
||||
for (let i = 0; i < 4; i++) {
|
||||
denominations[i] = BigNumber.from(instancesData[i].denomination)
|
||||
}
|
||||
const tokenAddress = instancesData[0].tokenAddress
|
||||
|
||||
const ProposalContract = await ProposalFactory.deploy(`${process.env.PROXY}`, denominations, tokenAddress)
|
||||
|
||||
await ProposalContract.deployTransaction.wait(5)
|
||||
|
||||
await hre.run('verify:verify', {
|
||||
address: ProposalContract.address,
|
||||
constructorArguments: [`${process.env.PROXY}`, denominations, tokenAddress],
|
||||
})
|
||||
|
||||
console.log('Verified CreateFactoryAndAddInstancesProposal deployed at: ', ProposalContract.address)
|
||||
},
|
||||
)
|
@ -1,40 +0,0 @@
|
||||
require('dotenv').config()
|
||||
const { task } = require('hardhat/config')
|
||||
const { BigNumber } = require('@ethersproject/bignumber')
|
||||
const instancesData = require('../resources/instances')
|
||||
|
||||
task('deploy_proposal', 'deploy proposal that uses factory')
|
||||
.addParam('factoryAddress', 'address of factory')
|
||||
.setAction(async (taskArgs, hre) => {
|
||||
const contractName = `Add${instancesData.length}Instance${instancesData.length == 1 ? '' : 's'}`
|
||||
|
||||
const ProposalFactory = await hre.ethers.getContractFactory(contractName)
|
||||
|
||||
let denominations = []
|
||||
for (let i = 0; i < instancesData.length; i++) {
|
||||
denominations[i] = BigNumber.from(instancesData[i].denomination)
|
||||
}
|
||||
|
||||
const tokenAddress = instancesData[0].tokenAddress
|
||||
|
||||
const ProposalContract = await ProposalFactory.deploy(
|
||||
`${process.env.PROXY}`,
|
||||
taskArgs.factoryAddress,
|
||||
denominations.length == 1 ? denominations[0] : denominations,
|
||||
tokenAddress,
|
||||
)
|
||||
|
||||
await ProposalContract.deployTransaction.wait(5)
|
||||
|
||||
await hre.run('verify:verify', {
|
||||
address: ProposalContract.address,
|
||||
constructorArguments: [
|
||||
`${process.env.PROXY}`,
|
||||
taskArgs.factoryAddress,
|
||||
denominations.length == 1 ? denominations[0] : denominations,
|
||||
tokenAddress,
|
||||
],
|
||||
})
|
||||
|
||||
console.log(`Verified ${contractName} deployed at: `, ProposalContract.address)
|
||||
})
|
@ -1,20 +0,0 @@
|
||||
require('dotenv').config()
|
||||
const { task } = require('hardhat/config')
|
||||
const instancesData = require('../resources/instances')
|
||||
|
||||
task('propose_proposal', 'propose proposal that uses factory')
|
||||
.addParam('proposalAddress', 'address of proposal')
|
||||
.setAction(async (taskArgs, hre) => {
|
||||
const proposalName = `add-${instancesData[0].symbol}-instances`
|
||||
|
||||
const GovernanceContract = await hre.ethers.getContractAt(
|
||||
'../artifacts/tornado-governance/contracts/Governance.sol:Governance',
|
||||
'0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
|
||||
)
|
||||
await GovernanceContract.propose(taskArgs.proposalAddress, proposalName)
|
||||
|
||||
const id = await GovernanceContract.latestProposalIds((await hre.ethers.getSigners())[0].address)
|
||||
const state = await GovernanceContract.state(id)
|
||||
|
||||
console.log('Proposal with name: ', proposalName, ' proposed with id: ', id, ', has state: ', state)
|
||||
})
|
@ -1,348 +0,0 @@
|
||||
require('dotenv').config()
|
||||
const { ethers } = require('hardhat')
|
||||
const { expect } = require('chai')
|
||||
const { BigNumber } = require('@ethersproject/bignumber')
|
||||
const { rbigint, createDeposit, toHex, generateProof, initialize } = require('tornado-cli')
|
||||
|
||||
const { propose } = require('../scripts/helper/propose_proposal.js')
|
||||
|
||||
const MixerContractABI = require('tornado-cli/build/contracts/Mixer.abi.json')
|
||||
|
||||
describe('Deployments test setup', () => {
|
||||
const Verifier = `${process.env.VERIFIER}`
|
||||
const Hasher = `${process.env.HASHER}`
|
||||
const Proxy = `${process.env.PROXY}`
|
||||
|
||||
//// IMPERSONATED ACCOUNTS
|
||||
let accounts
|
||||
let whale
|
||||
let impGov
|
||||
|
||||
//// CONTRACTS / FACTORIES
|
||||
let ProposalFactory
|
||||
let ProposalContract
|
||||
|
||||
let GovernanceContract
|
||||
let TornToken
|
||||
let RAIToken
|
||||
let TornadoProxy
|
||||
|
||||
let TornadoInstanceFactoryContract
|
||||
|
||||
/// HARDCODED // TODO take from config
|
||||
let denominations = [
|
||||
'33333333333333333333',
|
||||
'333333333333333333333',
|
||||
'3333333333333333333333',
|
||||
'33333333333333333333333',
|
||||
]
|
||||
let tokenAddress = '0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919'
|
||||
|
||||
let minewait = async (time) => {
|
||||
await ethers.provider.send('evm_increaseTime', [time])
|
||||
await ethers.provider.send('evm_mine', [])
|
||||
}
|
||||
|
||||
let sendr = async (method, params) => {
|
||||
return await ethers.provider.send(method, params)
|
||||
}
|
||||
|
||||
let clog = (...x) => {
|
||||
console.log(x)
|
||||
}
|
||||
|
||||
let pE = (x) => {
|
||||
return ethers.utils.parseEther(`${x}`)
|
||||
}
|
||||
|
||||
const ProposalState = {
|
||||
Pending: 0,
|
||||
Active: 1,
|
||||
Defeated: 2,
|
||||
Timelocked: 3,
|
||||
AwaitingExecution: 4,
|
||||
Executed: 5,
|
||||
Expired: 6,
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
accounts = await ethers.getSigners()
|
||||
ProposalFactory = await ethers.getContractFactory('CreateFactoryAndAddInstancesProposal')
|
||||
GovernanceContract = await ethers.getContractAt(
|
||||
'Governance',
|
||||
'0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
|
||||
)
|
||||
TornToken = await ethers.getContractAt(
|
||||
'@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20',
|
||||
'0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
|
||||
)
|
||||
TornadoProxy = await ethers.getContractAt('TornadoProxy', '0x722122dF12D4e14e13Ac3b6895a86e84145b6967')
|
||||
})
|
||||
|
||||
describe('Test instance deployment', () => {
|
||||
let snapshotId
|
||||
|
||||
it('Should have initialized all successfully', () => {
|
||||
expect(accounts[0].address).to.exist
|
||||
expect(GovernanceContract.address).to.exist
|
||||
expect(TornToken.address).to.exist
|
||||
expect(TornadoProxy.address).to.exist
|
||||
})
|
||||
|
||||
it('Should successfully imitate whale', async () => {
|
||||
await sendr('hardhat_impersonateAccount', ['0xA2b2fBCaC668d86265C45f62dA80aAf3Fd1dEde3'])
|
||||
whale = await ethers.getSigner('0xA2b2fBCaC668d86265C45f62dA80aAf3Fd1dEde3')
|
||||
GovernanceContract = await GovernanceContract.connect(whale)
|
||||
|
||||
let balance = await TornToken.balanceOf(whale.address)
|
||||
TornToken = await TornToken.connect(whale)
|
||||
|
||||
await TornToken.approve(GovernanceContract.address, ethers.utils.parseEther('8000000000'))
|
||||
await expect(GovernanceContract.lockWithApproval(balance)).to.not.be.reverted
|
||||
|
||||
expect((await GovernanceContract.lockedBalance(whale.address)).toString()).to.equal(balance.toString())
|
||||
})
|
||||
|
||||
it('Should successfully deploy proposal', async () => {
|
||||
ProposalContract = await ProposalFactory.deploy(Proxy, denominations, tokenAddress)
|
||||
expect(await ProposalContract.token()).to.equal(tokenAddress)
|
||||
expect(await ProposalContract.denomination1()).to.equal(denominations[0])
|
||||
expect(await ProposalContract.denomination2()).to.equal(denominations[1])
|
||||
expect(await ProposalContract.denomination3()).to.equal(denominations[2])
|
||||
expect(await ProposalContract.denomination4()).to.equal(denominations[3])
|
||||
|
||||
TornadoInstanceFactoryContract = await ethers.getContractAt(
|
||||
'TornadoInstanceCloneFactory',
|
||||
await ProposalContract.instanceFactory(),
|
||||
)
|
||||
})
|
||||
|
||||
it('Should successfully pass the proposal', async () => {
|
||||
let response, id, state
|
||||
;[response, id, state] = await propose([whale, ProposalContract, 'Instances'])
|
||||
|
||||
let { events } = await response.wait()
|
||||
let args = events.find(({ event }) => event == 'ProposalCreated').args
|
||||
expect(args.id).to.be.equal(id)
|
||||
expect(args.proposer).to.be.equal(whale.address)
|
||||
expect(args.target).to.be.equal(ProposalContract.address)
|
||||
expect(args.description).to.be.equal('Instances')
|
||||
expect(state).to.be.equal(ProposalState.Pending)
|
||||
|
||||
await minewait((await GovernanceContract.VOTING_DELAY()).add(1).toNumber())
|
||||
await expect(GovernanceContract.castVote(id, true)).to.not.be.reverted
|
||||
state = await GovernanceContract.state(id)
|
||||
expect(state).to.be.equal(ProposalState.Active)
|
||||
await minewait(
|
||||
(
|
||||
await GovernanceContract.VOTING_PERIOD()
|
||||
)
|
||||
.add(await GovernanceContract.EXECUTION_DELAY())
|
||||
.add(86400)
|
||||
.toNumber(),
|
||||
)
|
||||
const overrides = {
|
||||
gasLimit: BigNumber.from('30000000'),
|
||||
}
|
||||
await GovernanceContract.execute(id, overrides)
|
||||
|
||||
expect(await GovernanceContract.state(id)).to.be.equal(ProposalState.Executed)
|
||||
})
|
||||
|
||||
it('Should set correct params for factory', async () => {
|
||||
expect(await TornadoInstanceFactoryContract.verifier()).to.equal(Verifier)
|
||||
expect(await TornadoInstanceFactoryContract.hasher()).to.equal(Hasher)
|
||||
expect(await TornadoInstanceFactoryContract.merkleTreeHeight()).to.equal(20)
|
||||
// clog(await TornadoInstanceFactoryContract.implementation())
|
||||
})
|
||||
|
||||
it('Factory should be able to generate an instance without reverting', async () => {
|
||||
const OHMAddress = '0x383518188C0C6d7730D91b2c03a03C837814a899'
|
||||
|
||||
await sendr('hardhat_impersonateAccount', [GovernanceContract.address])
|
||||
await sendr('hardhat_setBalance', [GovernanceContract.address, '0xDE0B6B3A764000000'])
|
||||
impGov = await ethers.getSigner(GovernanceContract.address)
|
||||
|
||||
const factory = await TornadoInstanceFactoryContract.connect(impGov)
|
||||
|
||||
await factory.createInstanceClone(333, OHMAddress)
|
||||
const instanceAddress = await TornadoInstanceFactoryContract.getInstanceAddress(333, OHMAddress)
|
||||
|
||||
const instance = await ethers.getContractAt('ERC20TornadoCloneable', instanceAddress)
|
||||
|
||||
const token = await instance.token()
|
||||
const denomination = await instance.denomination()
|
||||
const verifier = await instance.verifier()
|
||||
const hasher = await instance.hasher()
|
||||
const levels = await instance.levels()
|
||||
|
||||
expect(token).to.equal(OHMAddress)
|
||||
expect(denomination).to.equal(333)
|
||||
expect(verifier).to.equal(Verifier)
|
||||
expect(hasher).to.equal(Hasher)
|
||||
expect(levels).to.equal(20)
|
||||
})
|
||||
|
||||
it('Governance should be able to set factory params', async () => {
|
||||
const zeroAddress = '0x0000000000000000000000000000000000000000'
|
||||
|
||||
const factory = await TornadoInstanceFactoryContract.connect(impGov)
|
||||
await factory.setVerifier(zeroAddress)
|
||||
await factory.setHasher(zeroAddress)
|
||||
await factory.setMerkleTreeHeight(25)
|
||||
|
||||
let fverifier = await factory.verifier()
|
||||
let fhasher = await factory.hasher()
|
||||
let merkleTreeHeight = await factory.merkleTreeHeight()
|
||||
|
||||
expect(fverifier).to.equal(zeroAddress)
|
||||
expect(fhasher).to.equal(zeroAddress)
|
||||
expect(merkleTreeHeight).to.equal(25)
|
||||
|
||||
await factory.setVerifier(Verifier)
|
||||
await factory.setHasher(Hasher)
|
||||
await factory.setMerkleTreeHeight(20)
|
||||
|
||||
fverifier = await factory.verifier()
|
||||
fhasher = await factory.hasher()
|
||||
merkleTreeHeight = await factory.merkleTreeHeight()
|
||||
|
||||
expect(fverifier).to.equal(Verifier)
|
||||
expect(fhasher).to.equal(Hasher)
|
||||
expect(merkleTreeHeight).to.equal(20)
|
||||
})
|
||||
|
||||
let whaleRAI, whaleRAIBalance, TornadoInstance, mixerContract, instanceAddresses
|
||||
instanceAddresses = []
|
||||
|
||||
it('Should prepare data for instance deposit/withdraw tests', async () => {
|
||||
const RAITokenAddress = '0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919'
|
||||
await sendr('hardhat_impersonateAccount', ['0x46a0B4Fa58141ABa23185e79f7047A7dFd0FF100'])
|
||||
RAIToken = await ethers.getContractAt(
|
||||
'@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20',
|
||||
RAITokenAddress,
|
||||
)
|
||||
whaleRAI = await ethers.getSigner('0x46a0B4Fa58141ABa23185e79f7047A7dFd0FF100')
|
||||
|
||||
const tx = {
|
||||
to: whaleRAI.address,
|
||||
value: pE(50),
|
||||
}
|
||||
await accounts[0].sendTransaction(tx)
|
||||
|
||||
whaleRAIBalance = await RAIToken.balanceOf(whaleRAI.address)
|
||||
RAIToken = await RAIToken.connect(whaleRAI)
|
||||
TornadoProxy = await TornadoProxy.connect(whaleRAI)
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
instanceAddresses[i] = await TornadoInstanceFactoryContract.getInstanceAddress(
|
||||
denominations[i],
|
||||
RAIToken.address,
|
||||
)
|
||||
}
|
||||
|
||||
mixerContract = await ethers.getContractAt(MixerContractABI, instanceAddresses[0])
|
||||
mixerContract = await mixerContract.connect(whaleRAI)
|
||||
|
||||
snapshotId = await sendr('evm_snapshot', [])
|
||||
})
|
||||
|
||||
it('Should test depositing and withdrawing into the new instance over proxy', async () => {
|
||||
const depo = createDeposit({
|
||||
nullifier: rbigint(31),
|
||||
secret: rbigint(31),
|
||||
})
|
||||
|
||||
// const note = toHex(depo.preimage, 62)
|
||||
// const noteString = `tornado-RAI-33-1-${note}`
|
||||
// clog('Note: ', note)
|
||||
// clog('Note string: ', noteString)
|
||||
// clog('Commitment: ', toHex(depo.commitment))
|
||||
|
||||
await expect(RAIToken.approve(TornadoProxy.address, pE(5000000))).to.not.be.reverted
|
||||
TornadoInstance = await ethers.getContractAt(
|
||||
'contracts/tornado_proxy/ITornadoInstance.sol:ITornadoInstance',
|
||||
instanceAddresses[0],
|
||||
)
|
||||
|
||||
await expect(() =>
|
||||
TornadoProxy.deposit(instanceAddresses[0], toHex(depo.commitment), []),
|
||||
).to.changeTokenBalance(RAIToken, whaleRAI, BigNumber.from(0).sub(await TornadoInstance.denomination()))
|
||||
|
||||
let pevents = await mixerContract.queryFilter('Deposit')
|
||||
await initialize({ merkleTreeHeight: 20 })
|
||||
|
||||
const { proof, args } = await generateProof({
|
||||
deposit: depo,
|
||||
recipient: whaleRAI.address,
|
||||
events: pevents,
|
||||
})
|
||||
|
||||
await expect(() =>
|
||||
TornadoProxy.withdraw(TornadoInstance.address, proof, ...args),
|
||||
).to.changeTokenBalance(RAIToken, whaleRAI, await TornadoInstance.denomination())
|
||||
|
||||
await sendr('evm_revert', [snapshotId])
|
||||
snapshotId = await sendr('evm_snapshot', [])
|
||||
})
|
||||
|
||||
it('Should prepare for multiple account deposits', async () => {
|
||||
let toSend = whaleRAIBalance.div(5)
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await RAIToken.transfer(accounts[i].address, toSend)
|
||||
const rai = await RAIToken.connect(accounts[i])
|
||||
await rai.approve(TornadoProxy.address, pE(600000))
|
||||
}
|
||||
})
|
||||
|
||||
it('Should test depositing with multiple accounts over proxy', async () => {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const depo = createDeposit({
|
||||
nullifier: rbigint(31),
|
||||
secret: rbigint(31),
|
||||
})
|
||||
// const note = toHex(depo.preimage, 62)
|
||||
// const noteString = `tornado-RAI-33-1-${note}`
|
||||
// clog('Note: ', note)
|
||||
// clog('Note string: ', noteString)
|
||||
// clog('Commitment: ', toHex(depo.commitment))
|
||||
const proxy = await TornadoProxy.connect(accounts[i])
|
||||
|
||||
await expect(() =>
|
||||
proxy.deposit(TornadoInstance.address, toHex(depo.commitment), []),
|
||||
).to.changeTokenBalance(
|
||||
RAIToken,
|
||||
accounts[i],
|
||||
BigNumber.from(0).sub(await TornadoInstance.denomination()),
|
||||
)
|
||||
|
||||
let pevents = await mixerContract.queryFilter('Deposit')
|
||||
await initialize({ merkleTreeHeight: 20 })
|
||||
|
||||
const { proof, args } = await generateProof({
|
||||
deposit: depo,
|
||||
recipient: accounts[i].address,
|
||||
events: pevents,
|
||||
})
|
||||
|
||||
await expect(() => proxy.withdraw(TornadoInstance.address, proof, ...args)).to.changeTokenBalance(
|
||||
RAIToken,
|
||||
accounts[i],
|
||||
await TornadoInstance.denomination(),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
await ethers.provider.send('hardhat_reset', [
|
||||
{
|
||||
forking: {
|
||||
jsonRpcUrl: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
|
||||
blockNumber: process.env.use_latest_block == 'true' ? undefined : 13017436,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
318
test/instance.factory.test.js
Normal file
318
test/instance.factory.test.js
Normal file
@ -0,0 +1,318 @@
|
||||
const hre = require('hardhat')
|
||||
const { ethers, waffle } = hre
|
||||
const { loadFixture } = waffle
|
||||
const { expect } = require('chai')
|
||||
const { BigNumber } = require('@ethersproject/bignumber')
|
||||
const { rbigint, createDeposit, toHex, generateProof, initialize } = require('tornado-cli')
|
||||
const MixerContractABI = require('tornado-cli/build/contracts/Mixer.abi.json')
|
||||
const config = require('../config')
|
||||
const { getSignerFromAddress, minewait } = require('./utils')
|
||||
|
||||
|
||||
describe('Instance Factory Tests', () => {
|
||||
const ProposalState = {
|
||||
Pending: 0,
|
||||
Active: 1,
|
||||
Defeated: 2,
|
||||
Timelocked: 3,
|
||||
AwaitingExecution: 4,
|
||||
Executed: 5,
|
||||
Expired: 6,
|
||||
}
|
||||
|
||||
const addressZero = ethers.constants.AddressZero
|
||||
|
||||
async function fixture() {
|
||||
const [sender, deployer, multisig] = await ethers.getSigners()
|
||||
|
||||
const tornWhale = await getSignerFromAddress(config.tornWhale)
|
||||
|
||||
const gov = await ethers.getContractAt(
|
||||
'Governance',
|
||||
config.governance,
|
||||
)
|
||||
|
||||
const tornToken = await ethers.getContractAt(
|
||||
'@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20',
|
||||
config.TORN,
|
||||
)
|
||||
|
||||
const compToken = await ethers.getContractAt(
|
||||
'@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20',
|
||||
config.COMP,
|
||||
)
|
||||
|
||||
instanceRegistry = await ethers.getContractAt(
|
||||
'tornado-relayer-registry/contracts/tornado-proxy/InstanceRegistry.sol:InstanceRegistry',
|
||||
config.instanceRegistry,
|
||||
)
|
||||
|
||||
// execute relayer registry proposal
|
||||
const proposalId = await gov.proposalCount()
|
||||
expect(await gov.state(proposalId)).to.be.equal(ProposalState.Active)
|
||||
await minewait(596400)
|
||||
expect(await gov.state(proposalId)).to.be.equal(ProposalState.AwaitingExecution)
|
||||
|
||||
await gov.execute(proposalId)
|
||||
expect(await gov.state(proposalId)).to.be.equal(ProposalState.Executed)
|
||||
|
||||
// deploy instance factory
|
||||
InstanceFactory = await ethers.getContractFactory('InstanceFactory')
|
||||
const instanceFactory = await InstanceFactory.connect(deployer).deploy(
|
||||
config.verifier,
|
||||
config.hasher,
|
||||
config.merkleTreeHeight,
|
||||
config.governance,
|
||||
config.instanceRegistry
|
||||
)
|
||||
await instanceFactory.deployed()
|
||||
|
||||
return { sender, deployer, multisig, tornWhale, gov, tornToken, compToken, instanceRegistry, instanceFactory }
|
||||
}
|
||||
|
||||
it('Should have initialized all successfully', async function () {
|
||||
const { sender, gov, tornToken, instanceRegistry, instanceFactory } = await loadFixture(fixture)
|
||||
expect(sender.address).to.exist
|
||||
expect(gov.address).to.exist
|
||||
expect(tornToken.address).to.exist
|
||||
expect(instanceRegistry.address).to.exist
|
||||
expect(instanceFactory.address).to.exist
|
||||
})
|
||||
|
||||
it('Should set correct params for factory', async function () {
|
||||
const { instanceFactory } = await loadFixture(fixture)
|
||||
|
||||
expect( await instanceFactory.governance()).to.be.equal(config.governance)
|
||||
expect( await instanceFactory.verifier()).to.be.equal(config.verifier)
|
||||
expect( await instanceFactory.hasher()).to.be.equal(config.hasher)
|
||||
expect( await instanceFactory.merkleTreeHeight()).to.be.equal(config.merkleTreeHeight)
|
||||
expect(await instanceFactory.implementation()).to.exist
|
||||
})
|
||||
|
||||
it('Governance should be able to set factory params', async function () {
|
||||
let { instanceFactory, gov } = await loadFixture(fixture)
|
||||
|
||||
await expect(instanceFactory.setVerifier(addressZero)).to.be.reverted
|
||||
|
||||
const govSigner = await getSignerFromAddress(gov.address)
|
||||
instanceFactory = await instanceFactory.connect(govSigner)
|
||||
|
||||
await instanceFactory.setVerifier(addressZero)
|
||||
await instanceFactory.setHasher(addressZero)
|
||||
await instanceFactory.setMerkleTreeHeight(1)
|
||||
|
||||
expect( await instanceFactory.verifier()).to.be.equal(addressZero)
|
||||
expect( await instanceFactory.hasher()).to.be.equal(addressZero)
|
||||
expect( await instanceFactory.merkleTreeHeight()).to.be.equal(1)
|
||||
|
||||
await instanceFactory.setVerifier(config.verifier)
|
||||
await instanceFactory.setHasher(config.hasher)
|
||||
await instanceFactory.setMerkleTreeHeight(config.merkleTreeHeight)
|
||||
|
||||
expect( await instanceFactory.verifier()).to.be.equal(config.verifier)
|
||||
expect( await instanceFactory.hasher()).to.be.equal(config.hasher)
|
||||
expect( await instanceFactory.merkleTreeHeight()).to.be.equal(config.merkleTreeHeight)
|
||||
})
|
||||
|
||||
it('Should successfully deploy/propose/execute proposal - add instance', async function () {
|
||||
let { instanceFactory, gov, instanceRegistry, tornWhale, tornToken } = await loadFixture(fixture)
|
||||
|
||||
// deploy proposal ----------------------------------------------
|
||||
let tx = await instanceFactory.createNewProposal(
|
||||
config.COMP,
|
||||
3000,
|
||||
[ethers.utils.parseEther('100')],
|
||||
[30]
|
||||
)
|
||||
let receipt = await tx.wait()
|
||||
|
||||
const proposal = await ethers.getContractAt(
|
||||
'AddInstanceProposal',
|
||||
receipt.events[0].args[0],
|
||||
)
|
||||
|
||||
expect( await proposal.instanceFactory()).to.be.equal(instanceFactory.address)
|
||||
expect( await proposal.instanceRegistry()).to.be.equal(instanceRegistry.address)
|
||||
expect( await proposal.token()).to.be.equal(config.COMP)
|
||||
expect( await proposal.uniswapPoolSwappingFee()).to.be.equal(3000)
|
||||
expect( await proposal.numInstances()).to.be.equal(1)
|
||||
expect( await proposal.protocolFeeByIndex(0)).to.be.equal(30)
|
||||
expect( await proposal.denominationByIndex(0)).to.be.equal(ethers.utils.parseEther('100'))
|
||||
|
||||
// propose proposal ---------------------------------------------
|
||||
let response, id, state
|
||||
gov = await gov.connect(tornWhale)
|
||||
await tornToken.connect(tornWhale).approve(gov.address, ethers.utils.parseEther('26000'))
|
||||
await gov.lockWithApproval(ethers.utils.parseEther('26000'))
|
||||
|
||||
response = await gov.propose(proposal.address, 'COMP token instance proposal')
|
||||
id = await gov.latestProposalIds(tornWhale.address)
|
||||
state = await gov.state(id)
|
||||
|
||||
const { events } = await response.wait()
|
||||
const args = events.find(({ event }) => event == 'ProposalCreated').args
|
||||
expect(args.id).to.be.equal(id)
|
||||
expect(args.proposer).to.be.equal(tornWhale.address)
|
||||
expect(args.target).to.be.equal(proposal.address)
|
||||
expect(args.description).to.be.equal('COMP token instance proposal')
|
||||
expect(state).to.be.equal(ProposalState.Pending)
|
||||
|
||||
// execute proposal ---------------------------------------------
|
||||
await minewait((await gov.VOTING_DELAY()).add(1).toNumber())
|
||||
await expect(gov.castVote(id, true)).to.not.be.reverted
|
||||
expect(await gov.state(id)).to.be.equal(ProposalState.Active)
|
||||
await minewait(
|
||||
(
|
||||
await gov.VOTING_PERIOD()
|
||||
)
|
||||
.add(await gov.EXECUTION_DELAY())
|
||||
.add(96400)
|
||||
.toNumber(),
|
||||
)
|
||||
expect(await gov.state(id)).to.be.equal(ProposalState.AwaitingExecution)
|
||||
|
||||
tx = await gov.execute(id)
|
||||
|
||||
expect(await gov.state(id)).to.be.equal(ProposalState.Executed)
|
||||
|
||||
// check instance initialization --------------------------------
|
||||
receipt = await tx.wait()
|
||||
const instanceAddr = '0x' + receipt.events[0].topics[1].toString().slice(-40)
|
||||
const instance = await ethers.getContractAt(
|
||||
'ERC20TornadoCloneable',
|
||||
instanceAddr,
|
||||
)
|
||||
|
||||
expect( await instance.token()).to.be.equal(config.COMP)
|
||||
expect( await instance.verifier()).to.be.equal(config.verifier)
|
||||
expect( await instance.hasher()).to.be.equal(config.hasher)
|
||||
expect( await instance.levels()).to.be.equal(config.merkleTreeHeight)
|
||||
expect( await instance.denomination()).to.equal(ethers.utils.parseEther('100'))
|
||||
|
||||
const instanceData = await instanceRegistry.instances(instance.address)
|
||||
expect(instanceData.isERC20).to.be.equal(true)
|
||||
expect(instanceData.token).to.be.equal(config.COMP)
|
||||
expect(instanceData.state).to.be.equal(1)
|
||||
expect(instanceData.uniswapPoolSwappingFee).to.be.equal(3000)
|
||||
expect(instanceData.protocolFeePercentage).to.be.equal(30)
|
||||
})
|
||||
|
||||
// it('Should prepare data for instance deposit/withdraw tests', async () => {
|
||||
// const RAITokenAddress = '0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919'
|
||||
// await sendr('hardhat_impersonateAccount', ['0x46a0B4Fa58141ABa23185e79f7047A7dFd0FF100'])
|
||||
// RAIToken = await ethers.getContractAt(
|
||||
// '@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20',
|
||||
// RAITokenAddress,
|
||||
// )
|
||||
// whaleRAI = await ethers.getSigner('0x46a0B4Fa58141ABa23185e79f7047A7dFd0FF100')
|
||||
|
||||
// const tx = {
|
||||
// to: whaleRAI.address,
|
||||
// value: pE(50),
|
||||
// }
|
||||
// await accounts[0].sendTransaction(tx)
|
||||
|
||||
// whaleRAIBalance = await RAIToken.balanceOf(whaleRAI.address)
|
||||
// RAIToken = await RAIToken.connect(whaleRAI)
|
||||
// TornadoProxy = await TornadoProxy.connect(whaleRAI)
|
||||
|
||||
// for (let i = 0; i < 4; i++) {
|
||||
// instanceAddresses[i] = await TornadoInstanceFactoryContract.getInstanceAddress(
|
||||
// denominations[i],
|
||||
// RAIToken.address,
|
||||
// )
|
||||
// }
|
||||
|
||||
// mixerContract = await ethers.getContractAt(MixerContractABI, instanceAddresses[0])
|
||||
// mixerContract = await mixerContract.connect(whaleRAI)
|
||||
|
||||
// snapshotId = await sendr('evm_snapshot', [])
|
||||
// })
|
||||
|
||||
// it('Should test depositing and withdrawing into the new instance over proxy', async () => {
|
||||
// const depo = createDeposit({
|
||||
// nullifier: rbigint(31),
|
||||
// secret: rbigint(31),
|
||||
// })
|
||||
|
||||
// // const note = toHex(depo.preimage, 62)
|
||||
// // const noteString = `tornado-RAI-33-1-${note}`
|
||||
// // clog('Note: ', note)
|
||||
// // clog('Note string: ', noteString)
|
||||
// // clog('Commitment: ', toHex(depo.commitment))
|
||||
|
||||
// await expect(RAIToken.approve(TornadoProxy.address, pE(5000000))).to.not.be.reverted
|
||||
// TornadoInstance = await ethers.getContractAt(
|
||||
// 'contracts/tornado_proxy/ITornadoInstance.sol:ITornadoInstance',
|
||||
// instanceAddresses[0],
|
||||
// )
|
||||
|
||||
// await expect(() =>
|
||||
// TornadoProxy.deposit(instanceAddresses[0], toHex(depo.commitment), []),
|
||||
// ).to.changeTokenBalance(RAIToken, whaleRAI, BigNumber.from(0).sub(await TornadoInstance.denomination()))
|
||||
|
||||
// let pevents = await mixerContract.queryFilter('Deposit')
|
||||
// await initialize({ merkleTreeHeight: 20 })
|
||||
|
||||
// const { proof, args } = await generateProof({
|
||||
// deposit: depo,
|
||||
// recipient: whaleRAI.address,
|
||||
// events: pevents,
|
||||
// })
|
||||
|
||||
// await expect(() =>
|
||||
// TornadoProxy.withdraw(TornadoInstance.address, proof, ...args),
|
||||
// ).to.changeTokenBalance(RAIToken, whaleRAI, await TornadoInstance.denomination())
|
||||
|
||||
// await sendr('evm_revert', [snapshotId])
|
||||
// snapshotId = await sendr('evm_snapshot', [])
|
||||
// })
|
||||
|
||||
// it('Should prepare for multiple account deposits', async () => {
|
||||
// let toSend = whaleRAIBalance.div(5)
|
||||
|
||||
// for (let i = 0; i < 3; i++) {
|
||||
// await RAIToken.transfer(accounts[i].address, toSend)
|
||||
// const rai = await RAIToken.connect(accounts[i])
|
||||
// await rai.approve(TornadoProxy.address, pE(600000))
|
||||
// }
|
||||
// })
|
||||
|
||||
// it('Should test depositing with multiple accounts over proxy', async () => {
|
||||
// for (let i = 0; i < 3; i++) {
|
||||
// const depo = createDeposit({
|
||||
// nullifier: rbigint(31),
|
||||
// secret: rbigint(31),
|
||||
// })
|
||||
// // const note = toHex(depo.preimage, 62)
|
||||
// // const noteString = `tornado-RAI-33-1-${note}`
|
||||
// // clog('Note: ', note)
|
||||
// // clog('Note string: ', noteString)
|
||||
// // clog('Commitment: ', toHex(depo.commitment))
|
||||
// const proxy = await TornadoProxy.connect(accounts[i])
|
||||
|
||||
// await expect(() =>
|
||||
// proxy.deposit(TornadoInstance.address, toHex(depo.commitment), []),
|
||||
// ).to.changeTokenBalance(
|
||||
// RAIToken,
|
||||
// accounts[i],
|
||||
// BigNumber.from(0).sub(await TornadoInstance.denomination()),
|
||||
// )
|
||||
|
||||
// let pevents = await mixerContract.queryFilter('Deposit')
|
||||
// await initialize({ merkleTreeHeight: 20 })
|
||||
|
||||
// const { proof, args } = await generateProof({
|
||||
// deposit: depo,
|
||||
// recipient: accounts[i].address,
|
||||
// events: pevents,
|
||||
// })
|
||||
|
||||
// await expect(() => proxy.withdraw(TornadoInstance.address, proof, ...args)).to.changeTokenBalance(
|
||||
// RAIToken,
|
||||
// accounts[i],
|
||||
// await TornadoInstance.denomination(),
|
||||
// )
|
||||
// }
|
||||
// })
|
||||
})
|
@ -1,355 +0,0 @@
|
||||
require('dotenv').config()
|
||||
const { ethers } = require('hardhat')
|
||||
const { expect } = require('chai')
|
||||
const { BigNumber } = require('@ethersproject/bignumber')
|
||||
const { rbigint, createDeposit, toHex, generateProof, initialize } = require('tornado-cli')
|
||||
|
||||
const { propose } = require('../scripts/helper/propose_proposal.js')
|
||||
|
||||
const MixerContractABI = require('tornado-cli/build/contracts/Mixer.abi.json')
|
||||
|
||||
describe('Deployments test setup', () => {
|
||||
const Verifier = `${process.env.VERIFIER}`
|
||||
const Hasher = `${process.env.HASHER}`
|
||||
const Proxy = `${process.env.PROXY}`
|
||||
|
||||
//// IMPERSONATED ACCOUNTS
|
||||
let accounts
|
||||
let whale
|
||||
let impGov
|
||||
|
||||
//// CONTRACTS / FACTORIES
|
||||
let ProposalFactory
|
||||
let ProposalContract
|
||||
|
||||
let GovernanceContract
|
||||
let TornToken
|
||||
let RAIToken
|
||||
let TornadoProxy
|
||||
|
||||
let TornadoInstanceFactoryFactory
|
||||
let TornadoInstanceFactoryContract
|
||||
|
||||
/// HARDCODED
|
||||
let denominations = [
|
||||
'33333333333333333333',
|
||||
'333333333333333333333',
|
||||
'3333333333333333333333',
|
||||
'33333333333333333333333',
|
||||
]
|
||||
let tokenAddress = '0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919'
|
||||
|
||||
let minewait = async (time) => {
|
||||
await ethers.provider.send('evm_increaseTime', [time])
|
||||
await ethers.provider.send('evm_mine', [])
|
||||
}
|
||||
|
||||
let sendr = async (method, params) => {
|
||||
return await ethers.provider.send(method, params)
|
||||
}
|
||||
|
||||
let clog = (...x) => {
|
||||
console.log(x)
|
||||
}
|
||||
|
||||
let pE = (x) => {
|
||||
return ethers.utils.parseEther(`${x}`)
|
||||
}
|
||||
|
||||
const ProposalState = {
|
||||
Pending: 0,
|
||||
Active: 1,
|
||||
Defeated: 2,
|
||||
Timelocked: 3,
|
||||
AwaitingExecution: 4,
|
||||
Executed: 5,
|
||||
Expired: 6,
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
accounts = await ethers.getSigners()
|
||||
ProposalFactory = await ethers.getContractFactory('Add4Instances')
|
||||
GovernanceContract = await ethers.getContractAt(
|
||||
'Governance',
|
||||
'0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
|
||||
)
|
||||
TornToken = await ethers.getContractAt(
|
||||
'@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20',
|
||||
'0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
|
||||
)
|
||||
TornadoProxy = await ethers.getContractAt('TornadoProxy', '0x722122dF12D4e14e13Ac3b6895a86e84145b6967')
|
||||
TornadoInstanceFactoryFactory = await ethers.getContractFactory('TornadoInstanceCloneFactory')
|
||||
})
|
||||
|
||||
describe('Test instance deployment', () => {
|
||||
let snapshotId
|
||||
|
||||
it('Should have initialized all successfully', () => {
|
||||
expect(accounts[0].address).to.exist
|
||||
expect(GovernanceContract.address).to.exist
|
||||
expect(TornToken.address).to.exist
|
||||
expect(TornadoProxy.address).to.exist
|
||||
})
|
||||
|
||||
it('Should set correct params for factory', async () => {
|
||||
TornadoInstanceFactoryContract = await TornadoInstanceFactoryFactory.deploy(
|
||||
Verifier,
|
||||
Hasher,
|
||||
BigNumber.from(20),
|
||||
)
|
||||
await TornadoInstanceFactoryContract.transferOwnership(GovernanceContract.address)
|
||||
expect(await TornadoInstanceFactoryContract.verifier()).to.equal(Verifier)
|
||||
expect(await TornadoInstanceFactoryContract.hasher()).to.equal(Hasher)
|
||||
expect(await TornadoInstanceFactoryContract.merkleTreeHeight()).to.equal(20)
|
||||
// clog(await TornadoInstanceFactoryContract.implementation())
|
||||
})
|
||||
|
||||
it('Factory should be able to generate an instance without reverting', async () => {
|
||||
const OHMAddress = '0x383518188C0C6d7730D91b2c03a03C837814a899'
|
||||
|
||||
await sendr('hardhat_impersonateAccount', [GovernanceContract.address])
|
||||
await sendr('hardhat_setBalance', [GovernanceContract.address, '0xDE0B6B3A764000000'])
|
||||
impGov = await ethers.getSigner(GovernanceContract.address)
|
||||
|
||||
const factory = await TornadoInstanceFactoryContract.connect(impGov)
|
||||
|
||||
await factory.createInstanceClone(333, OHMAddress)
|
||||
const instanceAddress = await TornadoInstanceFactoryContract.getInstanceAddress(333, OHMAddress)
|
||||
|
||||
const instance = await ethers.getContractAt('ERC20TornadoCloneable', instanceAddress)
|
||||
|
||||
const token = await instance.token()
|
||||
const denomination = await instance.denomination()
|
||||
const verifier = await instance.verifier()
|
||||
const hasher = await instance.hasher()
|
||||
const levels = await instance.levels()
|
||||
|
||||
expect(token).to.equal(OHMAddress)
|
||||
expect(denomination).to.equal(333)
|
||||
expect(verifier).to.equal(Verifier)
|
||||
expect(hasher).to.equal(Hasher)
|
||||
expect(levels).to.equal(20)
|
||||
})
|
||||
|
||||
it('Governance should be able to set factory params', async () => {
|
||||
const zeroAddress = '0x0000000000000000000000000000000000000000'
|
||||
|
||||
const factory = await TornadoInstanceFactoryContract.connect(impGov)
|
||||
await factory.setVerifier(zeroAddress)
|
||||
await factory.setHasher(zeroAddress)
|
||||
await factory.setMerkleTreeHeight(25)
|
||||
|
||||
let fverifier = await factory.verifier()
|
||||
let fhasher = await factory.hasher()
|
||||
let merkleTreeHeight = await factory.merkleTreeHeight()
|
||||
|
||||
expect(fverifier).to.equal(zeroAddress)
|
||||
expect(fhasher).to.equal(zeroAddress)
|
||||
expect(merkleTreeHeight).to.equal(25)
|
||||
|
||||
await factory.setVerifier(Verifier)
|
||||
await factory.setHasher(Hasher)
|
||||
await factory.setMerkleTreeHeight(20)
|
||||
|
||||
fverifier = await factory.verifier()
|
||||
fhasher = await factory.hasher()
|
||||
merkleTreeHeight = await factory.merkleTreeHeight()
|
||||
|
||||
expect(fverifier).to.equal(Verifier)
|
||||
expect(fhasher).to.equal(Hasher)
|
||||
expect(merkleTreeHeight).to.equal(20)
|
||||
})
|
||||
|
||||
it('Should successfully imitate whale', async () => {
|
||||
await sendr('hardhat_impersonateAccount', ['0xA2b2fBCaC668d86265C45f62dA80aAf3Fd1dEde3'])
|
||||
whale = await ethers.getSigner('0xA2b2fBCaC668d86265C45f62dA80aAf3Fd1dEde3')
|
||||
GovernanceContract = await GovernanceContract.connect(whale)
|
||||
|
||||
let balance = await TornToken.balanceOf(whale.address)
|
||||
TornToken = await TornToken.connect(whale)
|
||||
|
||||
await TornToken.approve(GovernanceContract.address, ethers.utils.parseEther('8000000000'))
|
||||
await expect(GovernanceContract.lockWithApproval(balance)).to.not.be.reverted
|
||||
|
||||
expect((await GovernanceContract.lockedBalance(whale.address)).toString()).to.equal(balance.toString())
|
||||
})
|
||||
|
||||
it('Should successfully deploy proposal', async () => {
|
||||
ProposalContract = await ProposalFactory.deploy(
|
||||
Proxy,
|
||||
TornadoInstanceFactoryContract.address,
|
||||
denominations,
|
||||
tokenAddress,
|
||||
)
|
||||
expect(await ProposalContract.token()).to.equal(tokenAddress)
|
||||
expect(await ProposalContract.instanceFactory()).to.equal(TornadoInstanceFactoryContract.address)
|
||||
expect(await ProposalContract.denomination1()).to.equal(denominations[0])
|
||||
expect(await ProposalContract.denomination2()).to.equal(denominations[1])
|
||||
expect(await ProposalContract.denomination3()).to.equal(denominations[2])
|
||||
expect(await ProposalContract.denomination4()).to.equal(denominations[3])
|
||||
})
|
||||
|
||||
it('Should successfully pass the proposal', async () => {
|
||||
let response, id, state
|
||||
;[response, id, state] = await propose([whale, ProposalContract, 'Instances'])
|
||||
|
||||
let { events } = await response.wait()
|
||||
let args = events.find(({ event }) => event == 'ProposalCreated').args
|
||||
expect(args.id).to.be.equal(id)
|
||||
expect(args.proposer).to.be.equal(whale.address)
|
||||
expect(args.target).to.be.equal(ProposalContract.address)
|
||||
expect(args.description).to.be.equal('Instances')
|
||||
expect(state).to.be.equal(ProposalState.Pending)
|
||||
|
||||
await minewait((await GovernanceContract.VOTING_DELAY()).add(1).toNumber())
|
||||
await expect(GovernanceContract.castVote(id, true)).to.not.be.reverted
|
||||
state = await GovernanceContract.state(id)
|
||||
expect(state).to.be.equal(ProposalState.Active)
|
||||
await minewait(
|
||||
(
|
||||
await GovernanceContract.VOTING_PERIOD()
|
||||
)
|
||||
.add(await GovernanceContract.EXECUTION_DELAY())
|
||||
.add(86400)
|
||||
.toNumber(),
|
||||
)
|
||||
const overrides = {
|
||||
gasLimit: BigNumber.from('30000000'),
|
||||
}
|
||||
await GovernanceContract.execute(id, overrides)
|
||||
})
|
||||
|
||||
let whaleRAI, whaleRAIBalance, TornadoInstance, mixerContract, instanceAddresses
|
||||
instanceAddresses = []
|
||||
|
||||
it('Should prepare data for instance deposit/withdraw tests', async () => {
|
||||
const RAITokenAddress = '0x03ab458634910AaD20eF5f1C8ee96F1D6ac54919'
|
||||
await sendr('hardhat_impersonateAccount', ['0x46a0B4Fa58141ABa23185e79f7047A7dFd0FF100'])
|
||||
RAIToken = await ethers.getContractAt(
|
||||
'@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20',
|
||||
RAITokenAddress,
|
||||
)
|
||||
whaleRAI = await ethers.getSigner('0x46a0B4Fa58141ABa23185e79f7047A7dFd0FF100')
|
||||
|
||||
const tx = {
|
||||
to: whaleRAI.address,
|
||||
value: pE(50),
|
||||
}
|
||||
await accounts[0].sendTransaction(tx)
|
||||
|
||||
whaleRAIBalance = await RAIToken.balanceOf(whaleRAI.address)
|
||||
RAIToken = await RAIToken.connect(whaleRAI)
|
||||
TornadoProxy = await TornadoProxy.connect(whaleRAI)
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
instanceAddresses[i] = await TornadoInstanceFactoryContract.getInstanceAddress(
|
||||
denominations[i],
|
||||
RAIToken.address,
|
||||
)
|
||||
}
|
||||
|
||||
mixerContract = await ethers.getContractAt(MixerContractABI, instanceAddresses[0])
|
||||
mixerContract = await mixerContract.connect(whaleRAI)
|
||||
|
||||
snapshotId = await sendr('evm_snapshot', [])
|
||||
})
|
||||
|
||||
it('Should test depositing and withdrawing into the new instance over proxy', async () => {
|
||||
const depo = createDeposit({
|
||||
nullifier: rbigint(31),
|
||||
secret: rbigint(31),
|
||||
})
|
||||
|
||||
// const note = toHex(depo.preimage, 62)
|
||||
// const noteString = `tornado-RAI-33-1-${note}`
|
||||
// clog('Note: ', note)
|
||||
// clog('Note string: ', noteString)
|
||||
// clog('Commitment: ', toHex(depo.commitment))
|
||||
|
||||
await expect(RAIToken.approve(TornadoProxy.address, pE(5000000))).to.not.be.reverted
|
||||
TornadoInstance = await ethers.getContractAt(
|
||||
'contracts/tornado_proxy/ITornadoInstance.sol:ITornadoInstance',
|
||||
instanceAddresses[0],
|
||||
)
|
||||
|
||||
await expect(() =>
|
||||
TornadoProxy.deposit(instanceAddresses[0], toHex(depo.commitment), []),
|
||||
).to.changeTokenBalance(RAIToken, whaleRAI, BigNumber.from(0).sub(await TornadoInstance.denomination()))
|
||||
|
||||
let pevents = await mixerContract.queryFilter('Deposit')
|
||||
await initialize({ merkleTreeHeight: 20 })
|
||||
|
||||
const { proof, args } = await generateProof({
|
||||
deposit: depo,
|
||||
recipient: whaleRAI.address,
|
||||
events: pevents,
|
||||
})
|
||||
|
||||
await expect(() =>
|
||||
TornadoProxy.withdraw(TornadoInstance.address, proof, ...args),
|
||||
).to.changeTokenBalance(RAIToken, whaleRAI, await TornadoInstance.denomination())
|
||||
|
||||
await sendr('evm_revert', [snapshotId])
|
||||
snapshotId = await sendr('evm_snapshot', [])
|
||||
})
|
||||
|
||||
it('Should prepare for multiple account deposits', async () => {
|
||||
let toSend = whaleRAIBalance.div(5)
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await RAIToken.transfer(accounts[i].address, toSend)
|
||||
const rai = await RAIToken.connect(accounts[i])
|
||||
await rai.approve(TornadoProxy.address, pE(600000))
|
||||
}
|
||||
})
|
||||
|
||||
it('Should test depositing with multiple accounts over proxy', async () => {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const depo = createDeposit({
|
||||
nullifier: rbigint(31),
|
||||
secret: rbigint(31),
|
||||
})
|
||||
// const note = toHex(depo.preimage, 62)
|
||||
// const noteString = `tornado-RAI-33-1-${note}`
|
||||
// clog('Note: ', note)
|
||||
// clog('Note string: ', noteString)
|
||||
// clog('Commitment: ', toHex(depo.commitment))
|
||||
const proxy = await TornadoProxy.connect(accounts[i])
|
||||
|
||||
await expect(() =>
|
||||
proxy.deposit(TornadoInstance.address, toHex(depo.commitment), []),
|
||||
).to.changeTokenBalance(
|
||||
RAIToken,
|
||||
accounts[i],
|
||||
BigNumber.from(0).sub(await TornadoInstance.denomination()),
|
||||
)
|
||||
|
||||
let pevents = await mixerContract.queryFilter('Deposit')
|
||||
await initialize({ merkleTreeHeight: 20 })
|
||||
|
||||
const { proof, args } = await generateProof({
|
||||
deposit: depo,
|
||||
recipient: accounts[i].address,
|
||||
events: pevents,
|
||||
})
|
||||
|
||||
await expect(() => proxy.withdraw(TornadoInstance.address, proof, ...args)).to.changeTokenBalance(
|
||||
RAIToken,
|
||||
accounts[i],
|
||||
await TornadoInstance.denomination(),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
await ethers.provider.send('hardhat_reset', [
|
||||
{
|
||||
forking: {
|
||||
jsonRpcUrl: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
|
||||
blockNumber: process.env.use_latest_block == 'true' ? undefined : 13017436,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
43
test/utils.js
Normal file
43
test/utils.js
Normal file
@ -0,0 +1,43 @@
|
||||
/* global ethers, network */
|
||||
|
||||
async function setTime(timestamp) {
|
||||
await ethers.provider.send('evm_setNextBlockTimestamp', [timestamp])
|
||||
}
|
||||
|
||||
async function takeSnapshot() {
|
||||
return await ethers.provider.send('evm_snapshot', [])
|
||||
}
|
||||
|
||||
async function revertSnapshot(id) {
|
||||
await ethers.provider.send('evm_revert', [id])
|
||||
}
|
||||
|
||||
async function advanceTime(sec) {
|
||||
const now = (await ethers.provider.getBlock('latest')).timestamp
|
||||
await setTime(now + sec)
|
||||
}
|
||||
|
||||
async function getSignerFromAddress(address) {
|
||||
await network.provider.request({
|
||||
method: 'hardhat_impersonateAccount',
|
||||
params: [address],
|
||||
})
|
||||
|
||||
let signer = await ethers.provider.getSigner(address)
|
||||
signer.address = signer._address
|
||||
return signer
|
||||
}
|
||||
|
||||
async function minewait(time) {
|
||||
await ethers.provider.send('evm_increaseTime', [time])
|
||||
await ethers.provider.send('evm_mine', [])
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setTime,
|
||||
advanceTime,
|
||||
takeSnapshot,
|
||||
revertSnapshot,
|
||||
getSignerFromAddress,
|
||||
minewait,
|
||||
}
|
Loading…
Reference in New Issue
Block a user