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
Creation Security
- Validate parameters
- Check return values
- Handle creation failure
- Control access rights
Gas Optimization
- Use minimal proxies
- Optimize constructor code
- Consider deployment costs
- Batch creations when possible
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:
- Implements a factory pattern
- Uses create2 for deterministic addresses
- Creates minimal proxies
- Manages contract registry
- 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.