Home Web3 SecuritySmart Contract Audit Explained: Create2 Opcode in Solidity

Explained: Create2 Opcode in Solidity

by ImmuneBytes
A-Comprehensive-Guide-to-the-CREATE2-Opcode-in-Solidity

Introduction

The CREATE2 opcode is a robust feature within the Solidity language, enabling developers to create contracts at predetermined addresses with determinism. Its inception occurred during the Byzantium hard fork in the Ethereum network, gaining popularity among developers for its versatility and cost-effectiveness.

This article aims to deliver a thorough guide on the CREATE2 opcode, exploring its rationale, appropriate use cases, its implementation, and providing a practical example.

Advantage CREATE2 over older CREATE

With CREATE, the address is determined by the factory contract’s nonce. Every time CREATE is called in the factory, its nonce is increased by 1, whereas CREATE2 determines the address by an arbitrary salt value and the init_code.

In other words, with CREATE2, the destination address is not dependent on the exact state (i.e. the nonce) of the factory when it’s called.

Due to CREATE’s dependence on the factory contract’s nonce and address, it is no longer considered safe.

Automated Market Maker (AMM) Wintermute lost around 20M OP, worth ~$27.6M in June 2022 due to this dependence.

Wintermute’s Gnosis Safe, created in 2020, was deployed using an old version of the ProxyFactory contract, which included the out-of-date CREATE opcode.

The exploiter did multiple deployments on Optimism (setting themself as owner) until the nonce matched the original mainnet deployment and a matching proxy address was created.

This was eventually achieved after running batched deployments of 162 safes at a time, until the matching address was created in this transaction. This eventually paved path for the Wintermute’s exploit.

The CREATE2 Opcode

CREATE2 possesses a notable characteristic that enhances its suitability in specific contexts: it operates independently of the current state of the deploying address.

Consequently, one can ascertain that the contract address computed today will remain consistent with the address calculated a year from now.

This feature is significant because it allows interactions with the address and facilitates the transfer of ETH to it even before the smart contract has been deployed onto it.

In simpler terms, the CREATE2 opcode possesses the capability to forecast the deployment address of a contract without actual deployment and provides numerous opportunities for enhancing user onboarding and scalability without the necessity of pre-executing the deployment.

How does the CREATE2 opcode do that? Let’s see!

There are two primary methods for deploying a smart contract: utilizing the CREATE and CREATE2 flows. In this brief overview, we will explore how each method functions and highlight their fundamental distinctions.

Using CREATE

Smart contracts can be generated through either contracts or standard accounts. In both these scenarios, the address for the newly created contract follows the same calculation method: derived from the sender’s address and a nonce.

new_address = hash(sender, nonce)

Each account possesses a corresponding nonce, incremented with each transaction for regular accounts and with each contract creation for contract accounts. Nonces are non-reusable and must follow a sequential order.

While it is possible to anticipate the address for the next deployed contract, this prediction holds true only in the absence of intervening transactions — a less-than-ideal characteristic for counterfactual systems.

Using CREATE2

CREATE2 effectively addresses the issue by ensuring that the resulting address remains unaffected by future events. Irrespective of the events occurring on the blockchain, deploying the contract at the precalculated address will consistently remain feasible.

new_address = hash(0xFF, sender, salt, bytecode)

The computation of new addresses involves these elements:

  • 0xFF, serving as a constant to avert collisions with CREATE.
  • The sender’s address.
  • A salt (32 bytes), an arbitrary value supplied by the sender.
  • The bytecode of the contract slated for deployment.

Upon execution, the CREATE2 opcode generates a smart contract with an address determined by the combination of the salt value and the contract creation code.

When employing identical salt values and contract creation codes, the resulting contract will consistently exhibit the same address, irrespective of the deploying node utilized.

Using CREATE2Factory

We employ the Create2Factory to initiate the deployment of the DeployWithCreate2 contract.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.14;

//Using Create2 to find out the contract address before its deployment.

contract DeployWithCreate2 {

    address public owner;

    constructor(address _owner) {

        owner = _owner;

    }

}

contract Create2Factory {

    event Deploy(address addr);

    // to deploy another contract using owner address and salt specified

    function deploy(uint _salt) external {
        DeployWithCreate2 _contract = new DeployWithCreate2{

            salt: bytes32(_salt)

        }(msg.sender);

        emit Deploy(address(_contract));

    }

    // get the computed address before the contract DeployWithCreate2 deployed using Bytecode of contract DeployWithCreate2 and salt specified by the sender

    function getAddress(bytes memory bytecode, uint _salt) public view returns (address) {

        bytes32 hash = keccak256(

            abi.encodePacked(

                bytes1(0xff), address(this), _salt, keccak256(bytecode)

          )

        );

        return address (uint160(uint(hash)));

    }

    // get the ByteCode of the contract DeployWithCreate2

    function getBytecode(address _owner) public pure returns (bytes memory) {

        bytes memory bytecode = type(DeployWithCreate2).creationCode;

        return abi.encodePacked(bytecode, abi.encode(_owner));

    }

}

Initially, we retrieve the bytecode of the DeployWithCreate2 contract through the getBytecode function.

Subsequently, utilizing both the bytecode and a salt specified by the owner, we calculate the address for the DeployWithCreate2 contract.

Lastly, we utilize the deploy function to execute the deployment of the DeployWithCreate2 contract and ascertain its actual contract address.

Upon checking the logs, we see that the actual deployed address is same as the address calculated.

How Is CREATE2 Useful?

The predictability inherent in the CREATE2 opcode proves beneficial in various situations. For instance, it simplifies coordination among distinct smart contracts or decentralized applications (dApps) that require interaction with a particular contract, as the contract’s address can be anticipated in advance.

The CREATE2 opcode in Ethereum offers several benefits:

Improved Interoperability: The deterministic nature of addresses simplifies coordination and interaction between different smart contracts or decentralized applications. This can lead to more efficient and seamless interoperability in complex decentralized systems.

Efficient State Channels and Layer 2 Scaling Solutions: In state channels and layer2 scaling solutions, where off-chain transactions are coordinated, the deterministic nature of CREATE2 addresses can streamline the management of contract addresses and improve the efficiency of these scaling solutions.

Enhanced Security: The ability to calculate contract addresses deterministically can enhance security by allowing parties to verify and validate addresses before interacting with contracts. This reduces the risk of accidental errors in address generation.

Cost Efficiency: Anticipatable contract addresses may result in savings in gas fees. Participants in a decentralized system can plan and optimize their interactions, potentially reducing the computational overhead and associated costs.

Facilitates Upgradeability: Deterministic addresses can be advantageous in upgradable contract scenarios. Developers have the capability to deploy updated versions of a contract at the identical deterministic address, simplifying interactions for users and other contracts with the seamlessly upgraded version.

Useful in Decentralized Finance (DeFi): DeFi protocols often benefit from the deterministic nature of CREATE2 when deploying various financial instruments and smart contracts that require precise addressing for seamless integration within the DeFi ecosystem.

The Pitfalls of Create2 OpCode

In an alarming development within the cryptocurrency realm, cybercriminals have started to exploit the Ethereum network’s CREATE2 opcode to hoodwink security measures in select wallets causing significant financial losses for investors.

The widely used decentralized exchange Uniswap uses Create2 to create pair contracts.

However, malicious actors are now using Create2 to generate new addresses with fraudulent signatures, enabling them to evade security protocols.

Utilizing the ‘Create2’ technique, hackers pre-calculate contract addresses, enabling them to produce a fraudulent address closely resembling the one regularly used by the victim.

This results in unsuspecting victims unwittingly approving transactions that facilitate illicit fund transfers. This scamming technique is known as address poisoning.

This approach has played a significant role in Wallet Drainers pilfering around $60 million from nearly 100,000 victims within a span of six months.

Conclusion

Utilizing CREATE and CREATE2 presents significant possibilities for establishing contract factories. In this blog we have tried to cover the most important points one should know of before using CREATE2 opcode in a smart contract.

To sum it up, CREATE2 is a notch higher than the CREATE opcode and is proving to be highly useful for Solidity developers. It allows users to predict the address of a smart contract even before it is deployed. However, the use of CREATE2 by crypto scammers for address poisoning is a matter of concern and invokes the need of greater scrutiny when authorizing transactions in blockchain.

You may also like