From d6919f2797af20374b387ef163f0f11f2ccdb654 Mon Sep 17 00:00:00 2001 From: Drygin Date: Fri, 11 Feb 2022 23:53:36 +0300 Subject: [PATCH] add create2 deploy --- .env.example | 2 + .github/workflows/build.yml | 2 + README.md | 27 ++++++++++ config.js | 26 +++++++++ contracts/bridge/L1Unwrapper.sol | 6 +-- contracts/libraries/SingletonFactory.sol | 28 ++++++++++ hardhat.config.js | 24 +++++++++ scripts/deployBSCHelper.js | 21 -------- scripts/deployL1Unwrapper.js | 28 ++++++++++ scripts/deployTornadoUpgrade.js | 28 ++++++++++ src/0_generateAddresses.js | 68 ++++++++++++++++++++++++ test/full.test.js | 13 ++++- 12 files changed, 248 insertions(+), 25 deletions(-) create mode 100644 config.js create mode 100644 contracts/libraries/SingletonFactory.sol delete mode 100644 scripts/deployBSCHelper.js create mode 100644 scripts/deployL1Unwrapper.js create mode 100644 scripts/deployTornadoUpgrade.js create mode 100644 src/0_generateAddresses.js diff --git a/.env.example b/.env.example index a6eed85..16a0da8 100644 --- a/.env.example +++ b/.env.example @@ -4,3 +4,5 @@ XDAI_RPC=https:// BSC_RPC=https:// MINIMUM_WITHDRAWAL_AMOUNT=0.05 MAXIMUM_DEPOSIT_AMOUNT=1 +ALCHEMY_KEY= +INFURA_API_KEY= diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4ed858d..4d73f80 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,6 +19,8 @@ jobs: - run: yarn lint - run: yarn build - run: yarn test + env: + ALCHEMY_KEY: ${{ secrets.ALCHEMY_KEY }} - name: Telegram Failure Notification uses: appleboy/telegram-action@0.0.7 if: failure() diff --git a/README.md b/README.md index 448db99..47fdbef 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,30 @@ yarn download yarn build yarn test ``` + +## Deploy + +Check config.js for actual values. + +With `salt` = `0x0000000000000000000000000000000000000000000000000000000047941987` addresses must be: + +1. `L1Unwrapper` - `0xfEADF5e7e453c664D903d1b1945c524c4328e1c5` +2. `TornadoPool` - `0xEb314843E39A2D67c7bA31150fA243b30b70e97c` + +Check addresses with current config: + +```shell +node -e 'require("./src/0_generateAddresses").generateWithLog()' +``` + +Deploy L1Unwrapper: + +```shell +npx hardhat run scripts/deployL1Unwrapper.js --network mainnet +``` + +Deploy TornadoPool: + +```shell +npx hardhat run scripts/deployTornadoUpgrade.js --network xdai +``` diff --git a/config.js b/config.js new file mode 100644 index 0000000..075eb6b --- /dev/null +++ b/config.js @@ -0,0 +1,26 @@ +module.exports = { + //// L1 ------------------- + // ETH + multisig: '0xb04E030140b30C27bcdfaafFFA98C57d80eDa7B4', + omniBridge: '0x88ad09518695c6c3712AC10a214bE5109a655671', + weth: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + // BSC + // multisig: '0xBAE5aBfa98466Dbe68836763B087f2d189f4D28f' + // omniBridge: '0xf0b456250dc9990662a6f25808cc74a6d1131ea9' + // weth: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c' // WBNB + singletonFactory: '0xce0042B868300000d44A59004Da54A005ffdcf9f', + salt: '0x0000000000000000000000000000000000000000000000000000000047941987', + + //// L2 ------------------- + // Gnosis chain + verifier2: '0xdf3a408c53e5078af6e8fb2a85088d46ee09a61b', + verifier16: '0x743494b60097a2230018079c02fe21a7b687eaa5', + MERKLE_TREE_HEIGHT: 23, + hasher: '0x94c92f096437ab9958fc0a37f09348f30389ae79', + gcWeth: '0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1', + gcOmniBridge: '0xf6a78083ca3e2a662d6dd1703c939c8ace2e268d', + l1Unwrapper: '0xfEADF5e7e453c664D903d1b1945c524c4328e1c5', + govAddress: '0x5efda50f22d34f262c29268506c5fa42cb56a1ce', + l1ChainId: 1, + gcMultisig: '0x1f727de610030a88863d7da45bdea4eb84655b52', +} diff --git a/contracts/bridge/L1Unwrapper.sol b/contracts/bridge/L1Unwrapper.sol index e438cd6..a17da04 100644 --- a/contracts/bridge/L1Unwrapper.sol +++ b/contracts/bridge/L1Unwrapper.sol @@ -79,9 +79,9 @@ contract L1Unwrapper is WETHOmnibridgeRouter { uint256 _value, bytes memory _data ) external override { - require(_token == address(WETH)); - require(msg.sender == address(bridge)); - require(_data.length == 52); + require(_token == address(WETH), "only WETH token"); + require(msg.sender == address(bridge), "only from bridge address"); + require(_data.length == 52, "incorrect data length"); WETH.withdraw(_value); diff --git a/contracts/libraries/SingletonFactory.sol b/contracts/libraries/SingletonFactory.sol new file mode 100644 index 0000000..13653e5 --- /dev/null +++ b/contracts/libraries/SingletonFactory.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT + +/** + *Submitted for verification at Etherscan.io on 2020-03-30 + */ + +pragma solidity 0.6.2; + +/** + * @title Singleton Factory (EIP-2470) + * @notice Exposes CREATE2 (EIP-1014) to deploy bytecode on deterministic addresses based on initialization code and salt. + * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) + */ +contract SingletonFactory { + /** + * @notice Deploys `_initCode` using `_salt` for defining the deterministic address. + * @param _initCode Initialization code. + * @param _salt Arbitrary value to modify resulting address. + * @return createdContract Created contract address. + */ + function deploy(bytes memory _initCode, bytes32 _salt) public returns (address payable createdContract) { + assembly { + createdContract := create2(0, add(_initCode, 0x20), mload(_initCode), _salt) + } + } +} +// IV is a value changed to generate the vanity address. +// IV: 6583047 diff --git a/hardhat.config.js b/hardhat.config.js index 2a1f3c9..48d83b9 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -21,6 +21,15 @@ const config = { }, }, }, + { + version: '0.6.2', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, { version: '0.7.5', settings: { @@ -42,6 +51,21 @@ const config = { ], }, networks: { + hardhat: { + forking: { + url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`, + blockNumber: 13685625, + }, + chainId: 1, + initialBaseFeePerGas: 5, + loggingEnabled: false, + allowUnlimitedContractSize: false, + blockGasLimit: 50000000, + }, + rinkeby: { + url: `https://rinkeby.infura.io/v3/${process.env.INFURA_API_KEY}`, + accounts: [process.env.PRIVATE_KEY], + }, xdai: { url: process.env.ETH_RPC || 'https://rpc.xdaichain.com/', accounts: process.env.PRIVATE_KEY diff --git a/scripts/deployBSCHelper.js b/scripts/deployBSCHelper.js deleted file mode 100644 index 27952c4..0000000 --- a/scripts/deployBSCHelper.js +++ /dev/null @@ -1,21 +0,0 @@ -const { ethers } = require('hardhat') - -// This script deploys L1Helper to FOREIGN chain (mainnet) - -async function main() { - const owner = '0xBAE5aBfa98466Dbe68836763B087f2d189f4D28f' - const omniBridge = '0xf0b456250dc9990662a6f25808cc74a6d1131ea9' - const token = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c' // WBNB - - const Helper = await ethers.getContractFactory('L1Unwrapper') - const helper = await Helper.deploy(omniBridge, token, owner) - await helper.deployed() - console.log(`L1Helper address: ${helper.address}`) -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error) - process.exit(1) - }) diff --git a/scripts/deployL1Unwrapper.js b/scripts/deployL1Unwrapper.js new file mode 100644 index 0000000..5c466de --- /dev/null +++ b/scripts/deployL1Unwrapper.js @@ -0,0 +1,28 @@ +const { ethers } = require('hardhat') +const config = require('../config') +const { generate } = require('../src/0_generateAddresses') + +// This script deploys L1Helper to FOREIGN chain (mainnet) + +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: 3000000 }) +} + +async function main() { + const singletonFactory = await ethers.getContractAt('SingletonFactory', config.singletonFactory) + const contracts = await generate() + await deploy({ ...contracts.unwrapperContract, singletonFactory }) + console.log(`L1 unwrapper contract have been deployed on ${contracts.unwrapperContract.address} address`) +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) diff --git a/scripts/deployTornadoUpgrade.js b/scripts/deployTornadoUpgrade.js new file mode 100644 index 0000000..b08d4c0 --- /dev/null +++ b/scripts/deployTornadoUpgrade.js @@ -0,0 +1,28 @@ +const { ethers } = require('hardhat') +const config = require('../config') +const { generate } = require('../src/0_generateAddresses') + +// This script deploys Tornado Pool upgrade to L2 (Gnosis Chain) + +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: 5000000 }) +} + +async function main() { + const singletonFactory = await ethers.getContractAt('SingletonFactory', config.singletonFactory) + const contracts = await generate() + await deploy({ ...contracts.poolContract, singletonFactory }) + console.log(`Upgraded pool contract have been deployed on ${contracts.poolContract.address} address`) +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) diff --git a/src/0_generateAddresses.js b/src/0_generateAddresses.js new file mode 100644 index 0000000..27ad3b3 --- /dev/null +++ b/src/0_generateAddresses.js @@ -0,0 +1,68 @@ +const { ethers } = require('hardhat') +const defaultConfig = require('../config') + +async function generate(config = defaultConfig) { + const singletonFactory = await ethers.getContractAt('SingletonFactory', config.singletonFactory) + + const UnwrapperFactory = await ethers.getContractFactory('L1Unwrapper') + const deploymentBytecodeUnwrapper = + UnwrapperFactory.bytecode + + UnwrapperFactory.interface.encodeDeploy([config.omniBridge, config.weth, config.multisig]).slice(2) + + const unwrapperAddress = ethers.utils.getCreate2Address( + singletonFactory.address, + config.salt, + ethers.utils.keccak256(deploymentBytecodeUnwrapper), + ) + + const PoolFactory = await ethers.getContractFactory('TornadoPool') + const deploymentBytecodePool = + PoolFactory.bytecode + + PoolFactory.interface + .encodeDeploy([ + config.verifier2, + config.verifier16, + config.MERKLE_TREE_HEIGHT, + config.hasher, + config.gcWeth, + config.gcOmniBridge, + config.l1Unwrapper, + config.govAddress, + config.l1ChainId, + config.gcMultisig, + ]) + .slice(2) + + const poolAddress = ethers.utils.getCreate2Address( + singletonFactory.address, + config.salt, + ethers.utils.keccak256(deploymentBytecodePool), + ) + + const result = { + unwrapperContract: { + address: unwrapperAddress, + bytecode: deploymentBytecodeUnwrapper, + isProxy: false, + }, + poolContract: { + address: poolAddress, + bytecode: deploymentBytecodePool, + isProxy: false, + }, + } + + return result +} + +async function generateWithLog() { + const contracts = await generate() + console.log('L1 unwrapper contract: ', contracts.unwrapperContract.address) + console.log('Upgraded pool contract: ', contracts.poolContract.address) + return contracts +} + +module.exports = { + generate, + generateWithLog, +} diff --git a/test/full.test.js b/test/full.test.js index 5fb53e4..9c3cd86 100644 --- a/test/full.test.js +++ b/test/full.test.js @@ -9,6 +9,8 @@ const { transaction, registerAndTransact, prepareTransaction, buildMerkleTree } const { toFixedHex, poseidonHash } = require('../src/utils') const { Keypair } = require('../src/keypair') const { encodeDataForBridge } = require('./utils') +const config = require('../config') +const { generate } = require('../src/0_generateAddresses') const MERKLE_TREE_HEIGHT = 5 const l1ChainId = 1 @@ -39,7 +41,16 @@ describe('TornadoPool', function () { const amb = await deploy('MockAMB', gov.address, l1ChainId) const omniBridge = await deploy('MockOmniBridge', amb.address) - const l1Unwrapper = await deploy('L1Unwrapper', amb.address, l1Token.address, gov.address) + + // deploy L1Unwrapper with CREATE2 + const singletonFactory = await ethers.getContractAt('SingletonFactory', config.singletonFactory) + + let customConfig = Object.assign({}, config) + customConfig.omniBridge = amb.address + customConfig.weth = l1Token.address + const contracts = await generate(customConfig) + await singletonFactory.deploy(contracts.unwrapperContract.bytecode, config.salt) + const l1Unwrapper = await ethers.getContractAt('L1Unwrapper', contracts.unwrapperContract.address) /** @type {TornadoPool} */ const tornadoPoolImpl = await deploy(