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:
ContractName: The name of the contract to be deployed.instance: A reference (address) to the newly created contract.{value: amount}: Optional — sends ETH to the new contract if the constructor is payable.(constructorParams): Arguments passed to the new contract’s constructor.
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:
- UniswapV2Factory: The factory contract responsible for creating new trading pairs.
- UniswapV2Pair: Each individual pair contract manages liquidity and swaps between two tokens.
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:
Pair: Represents a trading pair between two tokens.PairFactory: Deploys newPairinstances 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:
- The constructor sets
factorytomsg.sender, ensuring only the deploying contract (the factory) has authority. - The
initializefunction sets token addresses but is restricted to the factory via a modifier-like check.
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:
getPair: A nested mapping allowing O(1) lookup of a pair by any two token addresses.allPairs: An array that stores every deployed pair address — useful for enumeration.- Symmetric storage: Both
getPair[tokenA][tokenB]andgetPair[tokenB][tokenA]are set to support unordered input.
When createPair is called with two token addresses, it:
- Deploys a new
Paircontract. - Initializes it with the provided tokens.
- 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:
- Deploy the
PairFactorycontract. Call
createPair()with two test token addresses, such as:- WBNB:
0x2c44b726ADF1963cA47Af88B284C06f30380fC78 - PEOPLE (on BSC):
0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c
- WBNB:
- Observe the returned pair address, e.g.,
0xD3e2008b4Da2cD6DEAF73471590fF30C86778A48.
Then:
- Check
allPairsto confirm the new entry. - Query
getPair(WBNB, PEOPLE)to retrieve the same address. - Inspect the
Paircontract’stoken0andtoken1values.
👉 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
- Solidity contract creation
- Create contract in Solidity
- Factory pattern Solidity
- Dynamic contract deployment
- Uniswap pair creation
- Smart contract factory
- Solidity new keyword
- Ethereum CREATE opcode
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.