Advanced Solidity Lesson: Creating Contracts from Within Contracts

·

Smart contract development on Ethereum goes far beyond writing standalone programs. One of the most powerful and widely used features in advanced Solidity programming is the ability for a contract to create other contracts dynamically. This capability lies at the heart of many decentralized finance (DeFi) protocols, including major platforms like Uniswap, where a central factory contract spawns countless pair contracts for new token trading pairs.

In this guide, we’ll explore how to implement contract creation using the create method in Solidity, understand its mechanics, and build a simplified version of a real-world factory-and-pair system. Whether you're building your own DeFi protocol or simply deepening your understanding of Ethereum internals, mastering dynamic contract deployment is essential.


Understanding Contract Creation in Solidity

In Ethereum, both external accounts (EOAs) and smart contracts can deploy new smart contracts. While EOAs typically do so via wallets like MetaMask, smart contracts can programmatically create new contracts using built-in mechanisms — primarily through two opcodes: CREATE and CREATE2.

This article focuses on the CREATE opcode, implemented in Solidity via the new keyword.

👉 Discover how blockchain developers deploy scalable contract systems with precision and efficiency.

The new Keyword and CREATE Opcode

To create a new contract from within another contract, Solidity provides a clean syntax using the new keyword:

ContractName instance = new ContractName{value: amount}(constructorParams);

Here’s what each part means:

This line compiles down to the EVM’s CREATE opcode, which uses the contract creation code to generate a new address based on the creator’s address and nonce.


Real-World Use Case: Uniswap-Style Factory Pattern

One of the most iconic applications of dynamic contract creation is the factory pattern, as seen in Uniswap V2. In that system:

By separating concerns this way, Uniswap achieves scalability — one factory can manage thousands of trading pairs without bloating a single contract.

Let’s implement a minimal version of this architecture.


Building a Minimal Pair Factory System

We’ll create two contracts:

  1. Pair: Represents a trading pair between two tokens.
  2. PairFactory: Deploys new Pair instances and tracks them.

Pair Contract

The Pair contract stores basic metadata about a token pair and ensures only the factory can initialize it.

contract Pair {
    address public factory;
    address public token0;
    address public token1;

    constructor() payable {
        factory = msg.sender;
    }

    function initialize(address _token0, address _token1) external {
        require(msg.sender == factory, "Only factory can call this");
        token0 = _token0;
        token1 = _token1;
    }
}

Key Points:


PairFactory Contract

The factory maintains records of all created pairs and provides a clean interface for deployment.

contract PairFactory {
    mapping(address => mapping(address => address)) public getPair;
    address[] public allPairs;

    function createPair(address tokenA, address tokenB) external returns (address pairAddr) {
        Pair newPair = new Pair();
        newPair.initialize(tokenA, tokenB);

        pairAddr = address(newPair);
        allPairs.push(pairAddr);
        getPair[tokenA][tokenB] = pairAddr;
        getPair[tokenB][tokenA] = pairAddr;
    }
}

Key Features:

When createPair is called with two token addresses, it:

  1. Deploys a new Pair contract.
  2. Initializes it with the provided tokens.
  3. Records the new contract's address in mappings and arrays.

This pattern enables trustless, permissionless listing of new trading pairs — a cornerstone of DeFi innovation.


Testing in Remix IDE

You can easily test this system in Remix:

  1. Deploy the PairFactory contract.
  2. Call createPair() with two test token addresses, such as:

    • WBNB: 0x2c44b726ADF1963cA47Af88B284C06f30380fC78
    • PEOPLE (on BSC): 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c
  3. Observe the returned pair address, e.g., 0xD3e2008b4Da2cD6DEAF73471590fF30C86778A48.

Then:

👉 Learn how professional developers simulate complex DeFi deployments before going live.


Frequently Asked Questions (FAQ)

Q: What happens to the nonce when a contract creates another contract?

A: When a contract deploys a new contract using CREATE, its nonce increases by one. The address of the new contract is computed as hash(creator_address, nonce). This ensures uniqueness across deployments.

Q: Can a created contract self-destruct?

A: Yes. If the child contract includes a selfdestruct function, it can be terminated, and its stored ETH sent to a specified address. However, this doesn't remove its address from the factory's records unless explicitly handled.

Q: Is there gas overhead when creating contracts inside other contracts?

A: Yes. Contract creation is expensive in terms of gas because it involves storing bytecode on-chain. Developers should optimize initialization logic and consider lazy-loading patterns where possible.

Q: Why use a factory instead of deploying pairs directly?

A: Factories enable standardization, tracking, and discoverability. They allow frontends and other contracts to find existing pairs without prior knowledge of their addresses — crucial for composability in DeFi ecosystems.

Q: Can I prevent duplicate pairs from being created?

A: Absolutely. In production systems, you’d add checks in createPair() to revert if getPair[tokenA][tokenB] != address(0), preventing redundant deployments.

Q: What's the difference between CREATE and CREATE2?

A: While CREATE uses the creator’s nonce to determine the child address, CREATE2 allows predicting the address before deployment using a salt and hash of the init code. This enables counterfactual deployments and is covered in our next lesson.


Core Keywords for SEO

These keywords naturally appear throughout this article and align with common developer search queries related to advanced Solidity patterns.


Final Thoughts

Creating contracts from within contracts unlocks powerful architectural patterns in decentralized applications. From DeFi exchanges to NFT mints and modular upgradable systems, the factory pattern empowers scalable, composable designs.

Understanding how new Contract() works under the hood — and how platforms like Uniswap leverage it — gives you a critical edge as a blockchain developer.

👉 Explore advanced tools and environments where you can test contract creation workflows securely.