Solidity: Using CREATE2 to Obtain Contract Addresses for Exchange Deposit Accounts

·

Introduction to CREATE2

The CREATE2 opcode was introduced in Ethereum's Constantinople hard fork (February 28, 2019) as part of EIP-1014. Originally designed for state channels, it has broader applications—including solving exchange deposit account challenges.

Why CREATE2 Matters for Exchanges

Exchanges need unique Ethereum addresses for users to deposit funds ("deposit addresses"). These addresses must:

Suboptimal Solutions

1. Direct Use of Ethereum Addresses

Pros:

Cons:

2. Individual Smart Contracts per User

Pros:

Cons:


The CREATE2 Solution

How CREATE2 Works

The address formula:

keccak256(0xff ++ creator_addr ++ salt ++ keccak256(init_code))[12:]

Where:

Implementation Steps

  1. Precompute Addresses
    Generate deposit addresses offline using the formula above.
  2. Monitor Deposits
    Watch for Transfer events to precomputed addresses.
  3. Deploy On-Demand
    When funds arrive:

    function deployWallet(uint256 salt) public {
        bytes memory bytecode = type(Wallet).creationCode;
        address newAddr;
        assembly {
            newAddr := create2(0, add(bytecode, 32), mload(bytecode), salt)
        }
    }
  4. Auto-Aggregate Funds
    Wallet constructor executes:

    constructor() {
        token.transfer(hotWallet, token.balanceOf(address(this)));
        selfdestruct(address(0));
    }

Key Benefits


Full Code Example

pragma solidity ^0.8.0;

interface IERC20 {
    function transfer(address to, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

contract Wallet {
    address private immutable token;
    address private immutable hotWallet;

    constructor(address _token, address _hotWallet) {
        token = _token;
        hotWallet = _hotWallet;
        IERC20(token).transfer(
            hotWallet,
            IERC20(token).balanceOf(address(this))
        );
        selfdestruct(payable(address(0)));
    }
}

contract WalletFactory {
    function createWallet(
        uint256 salt,
        address token,
        address hotWallet
    ) public returns (address) {
        bytes memory bytecode = abi.encodePacked(
            type(Wallet).creationCode,
            abi.encode(token, hotWallet)
        );
        address wallet;
        assembly {
            wallet := create2(0, add(bytecode, 32), mload(bytecode), salt)
        }
        return wallet;
    }
}

FAQ Section

Q1: Can CREATE2 addresses be reused?

Yes. Self-destructing a contract resets the nonce, allowing redeployment to the same address.

Q2: What's the gas cost for this solution?

Approximately equal to a standard transfer() call after accounting for gas refunds.

Q3: How are salts generated securely?

Use keccak256(user_id) to ensure deterministic yet unique values.

👉 Explore advanced Solidity patterns

Q4: Is this production-ready?

The provided code is simplified. Production implementations should optimize bytecode and add access controls.

👉 Learn about secure wallet management


This Markdown document adheres to SEO best practices with:
- Hierarchical headings
- Natural keyword integration ("CREATE2", "deposit addresses", "smart contracts")
- Structured FAQs
- Engaging anchor texts