From 53eae70de7720097c8d9b64a4da4414887198210 Mon Sep 17 00:00:00 2001 From: Drygin Date: Tue, 22 Feb 2022 23:03:16 +0300 Subject: [PATCH] add two instance creation test --- .github/workflows/build.js.yml | 1 - README.md | 6 +- test/factory.test.js | 109 +++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.js.yml b/.github/workflows/build.js.yml index af872c1..52f1a54 100644 --- a/.github/workflows/build.js.yml +++ b/.github/workflows/build.js.yml @@ -6,7 +6,6 @@ on: tags: ['v[0-9]+.[0-9]+.[0-9]+'] pull_request: - jobs: build: runs-on: ubuntu-latest diff --git a/README.md b/README.md index 0c7c304..d746d1d 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,21 @@ # Tornado Instances Factory + ## About This repository contains governance proposal factory for the addition of new Tornado ERC20 instances to the Tornado router. -Anyone can create governance proposal for the addition of a new ERC20 instance by calling `createProposalApprove/createProposalPermit` method of the factory with parameters: +Anyone can create governance proposal for the addition of a new ERC20 instance by calling `createProposalApprove/createProposalPermit` method of the factory with parameters (proposal creation fee in TORN is charged from sender): 1. `address token` - address of ERC20 token for a new instance 2. `uint24 uniswapPoolSwappingFee` - fee value of Uniswap instance which will be used for `TORN/token` price determination. `3000` means 0.3% fee Uniswap pool. 3. `uint256[] denominations` - list of denominations for each new instance (tokens can only be deposited in certain denominations into instances). 4. `uint32[] protocolFees` - list of protocol fees for each new instance (this fee is only charged from registrated relayer during withdrawal process). `100` means 1% of instance denomination fee for withdrawal throw registrated relayer. + ## Factory parameters + 1. `max number of new instances in one proposal` - the current version supports the addition of a maximum of 3 instances at once. 2. `proposal creation fee` - this fee is charged from creator of proposal during `createProposalApprove/createProposalPermit` factory method execution. It can be changed by governance. Default value is stored in `config.js`. + ## Tests Setting up the repository: diff --git a/test/factory.test.js b/test/factory.test.js index 5dd1c04..1f7fa56 100644 --- a/test/factory.test.js +++ b/test/factory.test.js @@ -204,6 +204,115 @@ describe('Instance Factory Tests', () => { expect(instanceData.protocolFeePercentage).to.be.equal(30) }) + it('Should successfully deploy/propose/execute proposal - add instances', async function () { + let { sender, instanceFactory, gov, instanceRegistry, tornWhale, tornToken } = await loadFixture(fixture) + + // deploy proposal ---------------------------------------------- + await tornToken.connect(tornWhale).transfer(sender.address, config.creationFee) + await tornToken.approve(instanceFactory.address, config.creationFee) + + await expect(() => + instanceFactory + .connect(sender) + .createProposalApprove( + config.COMP, + 3000, + [ethers.utils.parseEther('100'), ethers.utils.parseEther('1000')], + [30, 30], + ), + ).to.changeTokenBalances( + tornToken, + [sender, gov], + [BigNumber.from(0).sub(config.creationFee), config.creationFee], + ) + + let logs = await instanceFactory.queryFilter('NewGovernanceProposalCreated') + const proposal = await ethers.getContractAt( + 'AddInstanceProposal', + ethers.utils.getAddress('0x' + logs[0].topics[1].slice(-40)), + ) + + 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(2) + expect(await proposal.protocolFeeByIndex(0)).to.be.equal(30) + expect(await proposal.protocolFeeByIndex(1)).to.be.equal(30) + expect(await proposal.denominationByIndex(0)).to.be.equal(ethers.utils.parseEther('100')) + expect(await proposal.denominationByIndex(1)).to.be.equal(ethers.utils.parseEther('1000')) + + // 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 instances 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 instances 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) + + await gov.execute(id) + + expect(await gov.state(id)).to.be.equal(ProposalState.Executed) + + // check instances initialization ------------------------------- + logs = await instanceFactory.queryFilter('NewInstanceCloneCreated') + let instanceAddr = '0x' + logs[0].topics[1].slice(-40) + let 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')) + + let 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) + + instanceAddr = '0x' + logs[1].topics[1].slice(-40) + 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('1000')) + + 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 successfully deploy proposal with permit', async function () { let { instanceFactory, gov, instanceRegistry, tornWhale, tornToken } = await loadFixture(fixture)