diff --git a/README.md b/README.md index 4057afe..e629cd9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ +# Deployments + +- `MinimalInstanceFactory`: [0x9d00007c0f5037157b5be8bff174b194a99118d0](https://etherscan.io/address/0x9d00007c0f5037157b5be8bff174b194a99118d0) +- `InstanceProposalFactory`: [0x10715a092e160793C278a9830F0f8D4417B94D71](https://etherscan.io/address/0x10715a092e160793C278a9830F0f8D4417B94D71) +- `ERC20TornadoCloneable (impl contract)`: [0xED2c9A637379DBF045982335db34Ea948F5FDB10](https://etherscan.io/address/0xED2c9A637379DBF045982335db34Ea948F5FDB10) +- `ETHTornadoCloneable (impl contract)`: [0xFDE7d58A869B7D4B11cA57A35a10D3D95B1683B2](https://etherscan.io/address/0xFDE7d58A869B7D4B11cA57A35a10D3D95B1683B2) + # Reconstruction of `tornado-instances` This is a (NOT-MINIMAL) reconstruction of the Tornado Cash `tornado-instances` repository with additional contract variants added. It is not minimal because some dependencies, because of the Github takedowns (and other stuff), had annoying build issues (at least for me) which made me inline a lot of the contract code. diff --git a/config.js b/config.js index 80cc129..4867027 100644 --- a/config.js +++ b/config.js @@ -21,4 +21,8 @@ module.exports = { WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', UniswapV3Factory: '0x1F98431c8aD98523631AE4a59f267346ea31F984', TWAPSlotsMin: 80, + deployments: { + minimalFactory: '0x9d00007c0f5037157b5be8bff174b194a99118d0', + proposalFactory: '0x10715a092e160793C278a9830F0f8D4417B94D71', + }, } diff --git a/scripts/deploy.js b/scripts/deploy.js index 99ec1b7..2d2e366 100644 --- a/scripts/deploy.js +++ b/scripts/deploy.js @@ -27,10 +27,8 @@ const prompter = createInterface({ input: process.stdin, output: process.stdout function _prompt(prompt, resolve) { prompter.question(prompt, (answer) => { if (answer == 'y') { - userInput.close() resolve(true) } else if (answer == 'n') { - userInput.close() resolve(false) } else _prompt('', resolve) }) @@ -48,34 +46,42 @@ function happyVerifiedMessage(name, address) { return `\n${name} @ ${address} successfully verified on Etherscan! 🥳\n` } +function timeout(seconds) { + return new Promise((resolve) => setTimeout(resolve, seconds * 1000)) +} + const promptMessageBase = (middle) => `\n${middle}\n\nAre you sure you would like to continue? 🧐 (y/n): ` async function main() { - const minimalFactoryContractFactory = await ethers.getContractFactory('MinimalInstanceFactory') - const proposalFactoryContractFactory = await ethers.getContractFactory('InstanceProposalFactory') + const minimalFactoryFactory = await ethers.getContractFactory('MinimalInstanceFactory') + const proposalFactoryFactory = await ethers.getContractFactory('InstanceProposalFactory') + const chainId = (await ethers.provider.getNetwork()).chainId + + const signer = await ethers.getSigner() let minimalFactory, proposalFactory, nativeCloneableImplAddr, erc20CloneableImplAddr if (await prompt(promptMessageBase('Continuing to MinimalInstanceFactory deployment.'))) { - minimalFactory = await minimalFactoryContractFactory.deploy( + minimalFactory = await minimalFactoryFactory.deploy( config.verifier, config.hasher, config.merkleTreeHeight, ) + console.log(happyDeployedMessage('MinimalInstanceFactory', chainId, minimalFactory.address)) } else { - return '\nDecided to stop at InstanceProposalFactory deployment.\n' + return '\nDecided to stop at MinimalInstanceFactory deployment.\n' } - console.log(happyDeployedMessage('MinimalInstanceFactory', minimalFactory.address)) + console.log(happyDeployedMessage('MinimalInstanceFactory', chainId, minimalFactory.address)) nativeCloneableImplAddr = await minimalFactory.nativeCurImpl() erc20CloneableImplAddr = await minimalFactory.ERC20Impl() - console.log(happyDeployedMessage('ETHTornadoCloneable', nativeCloneableImplAddr)) - console.log(happyDeployedMessage('ERC20TornadoCloneable', erc20CloneableImplAddr)) + console.log(happyDeployedMessage('ETHTornadoCloneable', chainId, nativeCloneableImplAddr)) + console.log(happyDeployedMessage('ERC20TornadoCloneable', chainId, erc20CloneableImplAddr)) if (await prompt(promptMessageBase('Continuing to InstanceProposalFactory deployment.'))) { - proposalFactory = await proposalFactoryContractFactory.deploy( + proposalFactory = await proposalFactoryFactory.deploy( config.governance, minimalFactory.address, config.instanceRegistry, @@ -84,7 +90,7 @@ async function main() { config.TWAPSlotsMin, ) - console.log(happyDeployedMessage('InstanceProposalFactory', proposalFactory.address)) + console.log(happyDeployedMessage('InstanceProposalFactory', chainId, proposalFactory.address)) } else { return '\nDecided to stop at InstanceProposalFactory deployment.\n' } @@ -96,6 +102,8 @@ async function main() { }) console.log(happyVerifiedMessage('MinimalInstanceFactory', minimalFactory.address)) + console.log('\nWaiting 5 seconds.\n') + await timeout(5) await hre.run('verify:verify', { address: proposalFactory.address, @@ -110,6 +118,8 @@ async function main() { }) console.log(happyVerifiedMessage('InstanceProposalFactory', proposalFactory.address)) + console.log('\nWaiting 5 seconds.\n') + await timeout(5) await hre.run('verify:verify', { address: nativeCloneableImplAddr, @@ -117,6 +127,8 @@ async function main() { }) console.log(happyVerifiedMessage('ETHTornadoCloneable', nativeCloneableImplAddr)) + console.log('\nWaiting 5 seconds.\n') + await timeout(5) await hre.run('verify:verify', { address: erc20CloneableImplAddr, diff --git a/scripts/deployInstanceProposalCreator.js b/scripts/deployInstanceProposalCreator.js deleted file mode 100755 index 9bcf547..0000000 --- a/scripts/deployInstanceProposalCreator.js +++ /dev/null @@ -1,32 +0,0 @@ -const { ethers } = require('hardhat') -const config = require('../config') -const { generate } = require('../src/generateAddresses') - -async function deploy({ address, bytecode, singletonFactory }) { - const contractCode = await ethers.provider.getCode(address) - if (contractCode !== '0x') { - console.log(`Contract ${address} already deployed. Skipping...`) - return - } - await singletonFactory.deploy(bytecode, config.salt, { gasLimit: config.deployGasLimit }) -} - -async function main() { - const singletonFactory = await ethers.getContractAt('SingletonFactory', config.singletonFactory) - const contracts = await generate() - await deploy({ ...contracts.factory.implementation, singletonFactory }) - console.log(`Instance factory contract have been deployed on ${contracts.factory.implementation.address}`) - await deploy({ ...contracts.factory.proxy, singletonFactory }) - console.log(`Instance factory proxy contract have been deployed on ${contracts.factory.proxy.address}`) - await deploy({ ...contracts.proposalCreator.implementation, singletonFactory }) - console.log(`Proposal creator have been deployed on ${contracts.proposalCreator.implementation.address}`) - await deploy({ ...contracts.proposalCreator.proxy, singletonFactory }) - console.log(`Proposal creator proxy have been deployed on ${contracts.proposalCreator.proxy.address}`) -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error) - process.exit(1) - }) diff --git a/scripts/deploySidechainInstanceFactory.js b/scripts/deploySidechainInstanceFactory.js deleted file mode 100755 index 557c1c4..0000000 --- a/scripts/deploySidechainInstanceFactory.js +++ /dev/null @@ -1,32 +0,0 @@ -const { ethers } = require('hardhat') -const config = require('../config') -const { generate } = require('../src/generateAddresses') - -async function deploy({ address, bytecode, singletonFactory }) { - const contractCode = await ethers.provider.getCode(address) - if (contractCode !== '0x') { - console.log(`Contract ${address} already deployed. Skipping...`) - return - } - await singletonFactory.deploy(bytecode, config.salt, { gasLimit: config.deployGasLimit }) -} - -async function main() { - const singletonFactory = await ethers.getContractAt('SingletonFactory', config.singletonFactory) - const contracts = await generate() - await deploy({ ...contracts.sidechainFactory.implementation, singletonFactory }) - await deploy({ ...contracts.sidechainFactory.proxy, singletonFactory }) - console.log( - `SidechainInstanceFactory contract have been deployed on ${contracts.sidechainFactory.implementation.address} address`, - ) - console.log( - `SidechainInstanceFactory proxy contract have been deployed on ${contracts.sidechainFactory.proxy.address} address`, - ) -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error) - process.exit(1) - }) diff --git a/scripts/verify.js b/scripts/verify.js new file mode 100644 index 0000000..add6131 --- /dev/null +++ b/scripts/verify.js @@ -0,0 +1,93 @@ +require('dotenv').config() + +const hre = require('hardhat') +const config = require('../config') +const { createInterface } = require('readline') + +const prompter = createInterface({ input: process.stdin, output: process.stdout }) + +function _prompt(promptMessage, predicate, resolve) { + prompter.question(promptMessage, (answer) => { + if (predicate(answer)) { + resolve(answer) + } else { + _prompt('', predicate, resolve) + } + }) +} + +const addressPromptMessage = (contractName) => + `\n\nPlease enter the deploy address of the ${contractName} contract (CTRL-D/C to exit): ` + +function addressPrompt(name) { + return new Promise((resolve) => + _prompt(addressPromptMessage(name), (answer) => answer.length === 42 || answer === 'skip', resolve), + ) +} + +function happyVerifiedMessage(name, address) { + return `\n${name} @ ${address} successfully verified on Etherscan! 🥳\n` +} + +function timeout(seconds) { + return new Promise((resolve) => setTimeout(resolve, seconds * 1000)) +} + +async function main() { + let minimalFactoryAddr = await addressPrompt('MinimalInstanceFactory') + let proposalFactoryAddr = await addressPrompt('InstanceProposalFactory') + let nativeCloneableImplAddr = await addressPrompt('ETH Instance Clone') + let erc20CloneableImplAddr = await addressPrompt('Token Instance Clone') + + if (minimalFactoryAddr !== 'skip') { + await hre.run('verify:verify', { + address: minimalFactoryAddr, + constructorArguments: [config.verifier, config.hasher, config.merkleTreeHeight], + }) + console.log(happyVerifiedMessage('MinimalInstanceFactory', minimalFactoryAddr)) + } + + console.log('\nWaiting 5 seconds.\n') + await timeout(5) + + if (proposalFactoryAddr !== 'skip') { + await hre.run('verify:verify', { + address: proposalFactoryAddr, + constructorArguments: [ + config.governance, + minimalFactoryAddr, + config.instanceRegistry, + config.UniswapV3Factory, + config.WETH, + config.TWAPSlotsMin, + ], + }) + console.log(happyVerifiedMessage('InstanceProposalFactory', proposalFactoryAddr)) + } + + console.log('\nWaiting 5 seconds.\n') + await timeout(5) + + if (nativeCloneableImplAddr !== 'skip') { + await hre.run('verify:verify', { + address: nativeCloneableImplAddr, + constructorArguments: [config.verifier, config.hasher], + }) + console.log(happyVerifiedMessage('ETHTornadoCloneable', nativeCloneableImplAddr)) + } + + console.log('\nWaiting 5 seconds.\n') + await timeout(5) + + if (erc20CloneableImplAddr !== 'skip') { + await hre.run('verify:verify', { + address: erc20CloneableImplAddr, + constructorArguments: [config.verifier, config.hasher], + }) + console.log(happyVerifiedMessage('ERC20TornadoCloneable', erc20CloneableImplAddr)) + } +} + +main().then((res) => { + console.log(res ?? '\nScript succesfully finished.\n') +})