Skip to content

Commit

Permalink
Use create2 for deploying target smart contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
vladbochok committed Dec 23, 2021
1 parent 22612e5 commit 1653481
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 25 deletions.
40 changes: 40 additions & 0 deletions contracts/contracts/Create2Factory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
pragma solidity ^0.7.0;

contract Create2Factory {
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}. Note that
* a contract cannot be deployed twice using the same salt.
*/
function deploy(bytes32 salt, bytes memory bytecode) public returns (address) {
address addr;
// solhint-disable-next-line no-inline-assembly
assembly {
addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
}
require(addr != address(0), "Create2: Failed on deploy");
return addr;
}

/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the `bytecode`
* or `salt` will result in a new destination address.
*/
function computeAddress(bytes32 salt, bytes memory bytecode) external view returns (address) {
return computeAddress(salt, bytecode, address(this));
}

/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/
function computeAddress(
bytes32 salt,
bytes memory bytecodeHash,
address deployer
) public pure returns (address) {
bytes32 bytecodeHashHash = keccak256(bytecodeHash);
bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHashHash));
return address(bytes20(_data << 96));
}
}
4 changes: 4 additions & 0 deletions contracts/scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ async function main() {
await deployer.deployRegenesisMultisig({ gasPrice, nonce: args.nonce });
}

if (args.contract === 'Create2Factory' || args.contract == null) {
await deployer.deployCreate2Factory({ gasPrice, nonce: args.nonce });
}

if (args.contract === 'AdditionalZkSync' || args.contract == null) {
await deployer.deployAdditionalZkSync({ gasPrice, nonce: args.nonce });
}
Expand Down
105 changes: 80 additions & 25 deletions contracts/src.ts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import {
ForcedExit,
ForcedExitFactory,
TokenGovernanceFactory,
TokenGovernance
TokenGovernance,
Create2Factory,
Create2FactoryFactory
} from '../typechain';

export interface Contracts {
Expand All @@ -29,6 +31,7 @@ export interface Contracts {
nftFactory;
additionalZkSync;
tokenGovernance;
create2Factory;
}

export interface DeployedAddresses {
Expand All @@ -45,6 +48,7 @@ export interface DeployedAddresses {
NFTFactory: string;
AdditionalZkSync: string;
TokenGovernance: string;
Create2Factory: string;
}

export interface DeployerConfig {
Expand Down Expand Up @@ -72,7 +76,8 @@ export function readProductionContracts(): Contracts {
forcedExit: readContractCode('ForcedExit'),
regenesisMultisig: readContractCode('RegenesisMultisig'),
additionalZkSync: readContractCode('AdditionalZkSync'),
tokenGovernance: readContractCode('TokenGovernance')
tokenGovernance: readContractCode('TokenGovernance'),
create2Factory: readContractCode('Create2Factory')
};
}

Expand All @@ -90,7 +95,8 @@ export function deployedAddressesFromEnv(): DeployedAddresses {
ForcedExit: process.env.CONTRACTS_FORCED_EXIT_ADDR,
RegenesisMultisig: process.env.MISC_REGENESIS_MULTISIG_ADDRESS,
AdditionalZkSync: process.env.CONTRACTS_ADDITIONAL_ZKSYNC_ADDR,
TokenGovernance: process.env.CONTRACTS_LISTING_GOVERNANCE
TokenGovernance: process.env.CONTRACTS_LISTING_GOVERNANCE,
Create2Factory: process.env.CONTRACTS_CREATE2_FACTORY_ADDR
};
}

Expand All @@ -111,80 +117,124 @@ export class Deployer {
this.governorAddress = config.governorAddress != null ? config.governorAddress : this.deployWallet.address;
}

public async deployCreate2Factory(ethTxOptions?: ethers.providers.TransactionRequest) {
if (this.verbose) {
console.log('Deploying create2 factory');
}

if (this.addresses.Create2Factory != '') {
if (this.verbose) {
console.log(`CONTRACTS_CREATE2_FACTORY_ADDR=${this.addresses.Create2Factory}`);
console.log('Create2 factory already deployed');
}
return;
}

const create2Factory = await deployContract(this.deployWallet, this.contracts.create2Factory, [], {
gasLimit: 1500000,
...ethTxOptions
});
const rec = await create2Factory.deployTransaction.wait();
const gasUsed = rec.gasUsed;
let gasPrice = create2Factory.deployTransaction.gasPrice;
if (gasPrice == null) {
gasPrice = await this.deployWallet.provider.getGasPrice();
}

if (this.verbose) {
console.log(`CONTRACTS_CREATE2_FACTORY_ADDR=${create2Factory.address}`);
console.log(
`Create2 factory deployed, gasUsed: ${gasUsed.toString()}, eth spent: ${formatEther(
gasUsed.mul(gasPrice)
)}`
);
}
this.addresses.Create2Factory = create2Factory.address;
}

private async deployViaCreate2(bytecode, ethTxOptions: ethers.providers.TransactionRequest) {
const create2Factory = this.create2FactoryContract(this.deployWallet);
const tx = await create2Factory.deploy(ethers.constants.HashZero, bytecode, ethTxOptions);
const address = await create2Factory['computeAddress(bytes32,bytes)'](ethers.constants.HashZero, bytecode);

return { tx, address };
}

public async deployGovernanceTarget(ethTxOptions?: ethers.providers.TransactionRequest) {
if (this.verbose) {
console.log('Deploying governance target');
}

const govContract = await deployContract(this.deployWallet, this.contracts.governance, [], {
const { tx, address } = await this.deployViaCreate2(this.contracts.governance.bytecode, {
gasLimit: 1500000,
...ethTxOptions
});
const govRec = await govContract.deployTransaction.wait();
const govRec = await tx.wait();
const govGasUsed = govRec.gasUsed;
let gasPrice = govContract.deployTransaction.gasPrice;
let gasPrice = tx.gasPrice;
if (gasPrice == null) {
gasPrice = await this.deployWallet.provider.getGasPrice();
}
if (this.verbose) {
console.log(`CONTRACTS_GOVERNANCE_TARGET_ADDR=${govContract.address}`);
console.log(`CONTRACTS_GOVERNANCE_TARGET_ADDR=${address}`);
console.log(
`Governance target deployed, gasUsed: ${govGasUsed.toString()}, eth spent: ${formatEther(
govGasUsed.mul(gasPrice)
)}`
);
}
this.addresses.GovernanceTarget = govContract.address;
this.addresses.GovernanceTarget = address;
}

public async deployVerifierTarget(ethTxOptions?: ethers.providers.TransactionRequest) {
if (this.verbose) {
console.log('Deploying verifier target');
}
const verifierContract = await deployContract(this.deployWallet, this.contracts.verifier, [], {
const { tx, address } = await this.deployViaCreate2(this.contracts.verifier.bytecode, {
gasLimit: 8000000,
...ethTxOptions
});
const verRec = await verifierContract.deployTransaction.wait();

const verRec = await tx.wait();
const verGasUsed = verRec.gasUsed;
let gasPrice = verifierContract.deployTransaction.gasPrice;
let gasPrice = tx.gasPrice;
if (gasPrice == null) {
gasPrice = await this.deployWallet.provider.getGasPrice();
}
if (this.verbose) {
console.log(`CONTRACTS_VERIFIER_TARGET_ADDR=${verifierContract.address}`);
console.log(`CONTRACTS_VERIFIER_TARGET_ADDR=${address}`);
console.log(
`Verifier target deployed, gasUsed: ${verGasUsed.toString()}, eth spent: ${formatEther(
verGasUsed.mul(gasPrice)
)}`
);
}
this.addresses.VerifierTarget = verifierContract.address;
this.addresses.VerifierTarget = address;
}

public async deployZkSyncTarget(ethTxOptions?: ethers.providers.TransactionRequest) {
if (this.verbose) {
console.log('Deploying zkSync target');
}
const zksContract = await deployContract(this.deployWallet, this.contracts.zkSync, [], {
const { tx, address } = await this.deployViaCreate2(this.contracts.zkSync.bytecode, {
gasLimit: 6000000,
...ethTxOptions
});
const zksRec = await zksContract.deployTransaction.wait();

const zksRec = await tx.wait();
const zksGasUsed = zksRec.gasUsed;
let gasPrice = zksContract.deployTransaction.gasPrice;
let gasPrice = tx.gasPrice;
if (gasPrice == null) {
gasPrice = await this.deployWallet.provider.getGasPrice();
}
if (this.verbose) {
console.log(`CONTRACTS_CONTRACT_TARGET_ADDR=${zksContract.address}`);
console.log(`CONTRACTS_CONTRACT_TARGET_ADDR=${address}`);
console.log(
`zkSync target deployed, gasUsed: ${zksGasUsed.toString()}, eth spent: ${formatEther(
zksGasUsed.mul(gasPrice)
)}`
);
}
this.addresses.ZkSyncTarget = zksContract.address;
this.addresses.ZkSyncTarget = address;
}

public async deployProxiesAndGatekeeper(ethTxOptions?: ethers.providers.TransactionRequest) {
Expand Down Expand Up @@ -353,26 +403,26 @@ export class Deployer {
if (this.verbose) {
console.log('Deploying Additional Zksync contract');
}

const additionalZkSyncContract = await deployContract(this.deployWallet, this.contracts.additionalZkSync, [], {
const { tx, address } = await this.deployViaCreate2(this.contracts.additionalZkSync.bytecode, {
gasLimit: 6000000,
...ethTxOptions
});
const zksRec = await additionalZkSyncContract.deployTransaction.wait();

const zksRec = await tx.wait();
const zksGasUsed = zksRec.gasUsed;
let gasPrice = additionalZkSyncContract.deployTransaction.gasPrice;
let gasPrice = tx.gasPrice;
if (gasPrice == null) {
gasPrice = await this.deployWallet.provider.getGasPrice();
}
if (this.verbose) {
console.log(`CONTRACTS_ADDITIONAL_ZKSYNC_ADDR=${additionalZkSyncContract.address}`);
console.log(`CONTRACTS_ADDITIONAL_ZKSYNC_ADDR=${address}`);
console.log(
`Additiinal zkSync contract deployed, gasUsed: ${zksGasUsed.toString()}, eth spent: ${formatEther(
zksGasUsed.mul(gasPrice)
)}`
);
}
this.addresses.AdditionalZkSync = additionalZkSyncContract.address;
this.addresses.AdditionalZkSync = address;
}

public async deployRegenesisMultisig(ethTxOptions?: ethers.providers.TransactionRequest) {
Expand Down Expand Up @@ -457,6 +507,7 @@ export class Deployer {
}

public async deployAll(ethTxOptions?: ethers.providers.TransactionRequest) {
await this.deployCreate2Factory(ethTxOptions);
await this.deployAdditionalZkSync(ethTxOptions);
await this.deployZkSyncTarget(ethTxOptions);
await this.deployGovernanceTarget(ethTxOptions);
Expand All @@ -466,6 +517,10 @@ export class Deployer {
await this.deployNFTFactory(ethTxOptions);
}

public create2FactoryContract(signerOrProvider: Signer | providers.Provider): Create2Factory {
return Create2FactoryFactory.connect(this.addresses.Create2Factory, signerOrProvider);
}

public governanceContract(signerOrProvider: Signer | providers.Provider): Governance {
return GovernanceFactory.connect(this.addresses.Governance, signerOrProvider);
}
Expand Down
3 changes: 3 additions & 0 deletions core/lib/config/src/configs/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct ContractsConfig {
pub verifier_addr: Address,
pub deploy_factory_addr: Address,
pub forced_exit_addr: Address,
pub create2_factory_addr: Address,
pub genesis_tx_hash: H256,
pub init_contract_version: u32,
pub upgrade_eth_blocks: Vec<u64>,
Expand Down Expand Up @@ -46,6 +47,7 @@ mod tests {
verifier_addr: addr("DAbb67b676F5b01FcC8997Cc8439846D0d8078ca"),
deploy_factory_addr: addr("FC073319977e314F251EAE6ae6bE76B0B3BAeeCF"),
forced_exit_addr: addr("9c7AeE886D6FcFc14e37784f143a6dAccEf50Db7"),
create2_factory_addr: addr("5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9"),
genesis_tx_hash: hash(
"b99ebfea46cbe05a21cd80fe5597d97b204befc52a16303f579c607dc1ac2e2e",
),
Expand All @@ -61,6 +63,7 @@ CONTRACTS_UPGRADE_GATEKEEPER_ADDR="0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9"
CONTRACTS_GOVERNANCE_TARGET_ADDR="0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9"
CONTRACTS_VERIFIER_TARGET_ADDR="0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9"
CONTRACTS_ADDITIONAL_ZKSYNC_ADDR="0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9"
CONTRACTS_CREATE2_FACTORY_ADDR="0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9"
CONTRACTS_CONTRACT_TARGET_ADDR="0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9"
CONTRACTS_CONTRACT_ADDR="0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55"
CONTRACTS_GOVERNANCE_ADDR="0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9"
Expand Down
1 change: 1 addition & 0 deletions etc/env/base/contracts.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ GENESIS_ROOT="0x2d5ab622df708ab44944bb02377be85b6f27812e9ae520734873b7a193898ba4
LISTING_GOVERNANCE="0xaFe6A91979021206ad79F58562Eef4204720E2A3"
NFT_FACTORY_ADDR=""
ADDITIONAL_ZKSYNC_ADDR="0x7fbaD9d9C9a1204F45FA38CcbF732B0930F8B582"
CREATE2_FACTORY_ADDR=""
# The initial version of the deployed zkSync contract.
# Data restore uses this variable for tracking contract updates and
# setting correct available block chunk sizes.
Expand Down

0 comments on commit 1653481

Please sign in to comment.