Skip to content

Contract Creation in Solidity

Introduction

Contract creation in Solidity involves various methods and patterns for deploying new contracts. Understanding these mechanisms is crucial for building complex DApp architectures.

Basic Creation

Direct Creation

solidity
contract SimpleFactory {
    address public lastCreated;
    
    function createContract() public {
        SimpleContract newContract = new SimpleContract();
        lastCreated = address(newContract);
    }
}

contract SimpleContract {
    address public creator;
    uint256 public creationTime;
    
    constructor() {
        creator = msg.sender;
        creationTime = block.timestamp;
    }
}

Creation with Parameters

solidity
contract ParameterizedFactory {
    event ContractCreated(address contractAddress, string name);
    
    function createContract(string memory name, uint256 initialValue) public {
        ParameterizedContract newContract = new ParameterizedContract(name, initialValue);
        emit ContractCreated(address(newContract), name);
    }
}

contract ParameterizedContract {
    string public name;
    uint256 public value;
    
    constructor(string memory _name, uint256 _value) {
        name = _name;
        value = _value;
    }
}

Advanced Creation

Create2 Opcode

solidity
contract Create2Factory {
    function createContract(bytes32 salt, bytes memory bytecode) public {
        address addr;
        assembly {
            addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
            if iszero(extcodesize(addr)) {
                revert(0, 0)
            }
        }
        emit ContractCreated(addr, salt);
    }
    
    function computeAddress(bytes32 salt, bytes memory bytecode) 
        public 
        view 
        returns (address) 
    {
        return address(uint160(uint(keccak256(abi.encodePacked(
            bytes1(0xff),
            address(this),
            salt,
            keccak256(bytecode)
        )))));
    }
    
    event ContractCreated(address addr, bytes32 salt);
}

Minimal Proxy (EIP-1167)

solidity
contract CloneFactory {
    function createClone(address target) internal returns (address result) {
        bytes20 targetBytes = bytes20(target);
        assembly {
            let clone := mload(0x40)
            mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(clone, 0x14), targetBytes)
            mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            result := create(0, clone, 0x37)
        }
    }
    
    function isClone(address target, address query) internal view returns (bool) {
        bytes20 targetBytes = bytes20(target);
        assembly {
            let clone := mload(0x40)
            mstore(clone, 0x363d3d373d3d3d363d7300000000000000000000000000000000000000000000)
            mstore(add(clone, 0xa), targetBytes)
            mstore(add(clone, 0x1e), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            
            let other := add(clone, 0x40)
            extcodecopy(query, other, 0, 0x2d)
            
            result := and(
                eq(mload(clone), mload(other)),
                eq(mload(add(clone, 0xd)), mload(add(other, 0xd)))
            )
        }
    }
}

Factory Patterns

Standard Factory

solidity
contract TokenFactory {
    mapping(address => address[]) public createdTokens;
    
    function createToken(
        string memory name,
        string memory symbol,
        uint8 decimals,
        uint256 initialSupply
    ) public returns (address) {
        Token newToken = new Token(name, symbol, decimals, initialSupply, msg.sender);
        createdTokens[msg.sender].push(address(newToken));
        return address(newToken);
    }
    
    function getTokens(address creator) public view returns (address[] memory) {
        return createdTokens[creator];
    }
}

Registry Factory

solidity
contract RegistryFactory {
    address public registry;
    
    constructor(address _registry) {
        registry = _registry;
    }
    
    function createContract() public returns (address) {
        ManagedContract newContract = new ManagedContract(registry);
        IRegistry(registry).register(address(newContract));
        return address(newContract);
    }
}

interface IRegistry {
    function register(address contractAddress) external;
}

contract ManagedContract {
    address public registry;
    
    constructor(address _registry) {
        registry = _registry;
    }
}

Best Practices

  1. Creation Security

    • Validate parameters
    • Check return values
    • Handle creation failure
    • Control access rights
  2. Gas Optimization

    • Use minimal proxies
    • Optimize constructor code
    • Consider deployment costs
    • Batch creations when possible
  3. Maintenance

    • Track created contracts
    • Implement upgrades
    • Monitor deployments
    • Document patterns

Common Patterns

Initialization Pattern

solidity
contract InitializableContract {
    bool private initialized;
    
    modifier initializer() {
        require(!initialized, "Already initialized");
        _;
        initialized = true;
    }
    
    function initialize(address owner) public initializer {
        // Initialization logic
    }
}

Deterministic Deployment

solidity
contract DeterministicDeployer {
    function deploy(bytes memory bytecode, bytes32 salt) 
        public 
        returns (address) 
    {
        address addr;
        assembly {
            addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
        }
        require(addr != address(0), "Deployment failed");
        return addr;
    }
}

Practice Exercise

Create a system that:

  1. Implements a factory pattern
  2. Uses create2 for deterministic addresses
  3. Creates minimal proxies
  4. Manages contract registry
  5. Handles initialization

Key Takeaways

  • Understand creation methods
  • Use appropriate patterns
  • Consider gas costs
  • Implement security checks
  • Maintain contract records

Remember: Contract creation is a fundamental aspect of building scalable DApps.

Released under the MIT License by Vogeb.