wighawag / hardhat-deploy

hardhat deployment plugin
MIT License
1.17k stars 283 forks source link

Allow deterministic support for zkSync #463

Closed rmeissner closed 10 months ago

rmeissner commented 1 year ago

This PR will allow the usage of custom singleton factories that support zkSync.

For this all code related to ContractFactory instances and address calculation have been moved into a separate class (DeploymentFactory).

Following methods have been added/moved into this class:

The getCreate2Address is separated into logic for EVM and zkSync. The logic assumes that the salt provided by hardhat-deploy is used and overrides the one provided by default by the zkSync ContractFactory.

An example for a deterministic deployment factory that works on zkSync is

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@matterlabs/zksync-contracts/l2/system-contracts/Constants.sol";
import "@matterlabs/zksync-contracts/l2/system-contracts/libraries/SystemContractsCaller.sol";

/**
 * The purpose of this contract is to provide a factory that deterministically deploys arbitrary contracts.
 * The advantage is that once such a contract has been deployed and verified, deployment flows can be 
 * easily reproduced in a deterministic way. This removed centralization (reliance on a specific deployment key)
 * and improves security (as it is easy to verify deployed code).
 * The factory should follow the same pattern as https://github.com/Arachnid/deterministic-deployment-proxy:
 * - only the `to` of a transaction has to be adjusted
 * - allow to specify a `salt` by prepending it to the deployment data
 */
contract DeploymentFactory {

    fallback() external payable {
        // The expected data format is <salt:bytes32><deploymentCalldata:bytes>
        // Where deploymenCalldata is a call to create/create2 of the DEPLOYER_SYSTEM_CONTRACT contract.
        bytes32 salt = bytes32(msg.data[:32]);
        bytes32 calldataSalt = bytes32(msg.data[36:68]);
        // The factory overrides the salt provided in the deploymentCalldata,
        // Therefore it is checked that both are the same or 
        // that the salt in the deploymentCalldata was not set (is a zero hash)
        require(calldataSalt == bytes32(0) || calldataSalt == salt, "Unexpected salt");
        // We cut off the method id (4 bytes) and salt (32 bytes) of the deploymentCalldata
        // as we overwrite it with the salt provided to the factory.
        bytes memory truncatedDeploymentCalldata = msg.data[68:];
        (bool success,) = SystemContractsCaller
            .systemCallWithReturndata(
                uint32(gasleft()),
                address(DEPLOYER_SYSTEM_CONTRACT),
                uint128(msg.value),
                abi.encodePacked(
                    DEPLOYER_SYSTEM_CONTRACT.create2.selector,
                    salt,
                    truncatedDeploymentCalldata
                )
            );
        require(success, "Deployment failed");
    }
}

the Safe team will include one into their safe-singleton-factory repository.

For reference see:

wighawag commented 10 months ago

Thanks @rmeissner ! I made the tweak available in 0.11.36

ElvisKrop commented 10 months ago

Thanks @rmeissner ! I made the tweak available in 0.11.36

Thank you, @wighawag!