In blockchain development, mining sets of data into a blockchain is quite an expensive process because fees are charged for each new data mined into the next block. Deploying smart contracts on blockchain results in mining the contract’s data into the next block, which will cost some gas fees that are charged in Ether if deployed on the Ethereum blockchain.
This article will demonstrate practically how to deploy multiple instances of your smart contract the right way using the factory pattern. Also, we’ll discuss the factory pattern, its benefits, and when to use it.
To follow along with this article, you should have prior knowledge of smart contract development with Solidity.
The following will be covered in this article:
The factory design pattern is a well-known programming pattern. The concept is simple: instead of directly creating instances of objects, you have a single object (the factory) that does it for you.
This is the same with Solidity because smart contracts are objects. In Solidity, a factory is a contract that will deploy multiple instances of other contracts.
We sometimes need to create different types of objects, but we don’t know what kind of object we’ll instantiate until the code is executed at runtime. In such cases, the factory technique comes in handy.
Generally, a basic factory contract should be able to deploy multiple instances of the same contract, store these created instances on the blockchain, and retrieve them when needed. You may want to add more functionality for managing deployed contracts like retrieving a specific instance of the contract, disabling an instance of the contract, and so on.
The following are benefits of using the factory pattern in Solidity:
The factory pattern is effective in the following situations:
We’ll discuss two types of factory patterns commonly implemented in Solidity smart contracts. These patterns include:
We’ll create a simple smart contract that will be used by the factory contract to deploy multiple instances of it:
// SPDX-License-Identifier: MIT pragma solidity >0.4.23 <0.9.0; contract Foundation { string public name; address public owner; constructor( string memory _name, address _owner ) public { name = _name; owner = _owner; } }
Because Ethereum is an open source project, the first line shows the contract’s open source license. The second line specifies the Solidity version necessary to execute this contract.
Following that, we establish the Foundation
contract, which is analogous to a class in other object-oriented programming languages. The constructor function here initializes the contract’s state variables with the values supplied in as arguments. When we create an instance of the contract, the constructor
function is called.
The Foundation contract currently has no means of being created. So, we are going to make a factory contract that will create the individual instances of the Foundation contract using the normal factory pattern.
Below is what a normal factory contract should look like:
// SPDX-License-Identifier: MIT pragma solidity >0.4.23 <0.9.0; import "./Foundation.sol"; contract FoundationFactory { Foundation[] private _foundations; function createFoundation( string memory name ) public { Foundation foundation = new Foundation( name, msg.sender ); _foundations.push(foundation); } function allFoundations(uint256 limit, uint256 offset) public view returns (Foundation[] memory coll) { return coll; } }
Here, this code imports the Foundation
contract, which we want to create multiple instances of. The _foundations
variable holds the instances of the Foundation
contract created.
The createFoundation
function deploys an instance of the Foundation
contract and stores it in the blockchain while the function allFoundations
retrieves all instances of the Foundation
contract stored in the blockchain.
The gas cost for the CREATE opcode is presently 32,000 Gwei. Each time an instance of the Foundation
contract is deployed, a gas fee of 32,000 Gwei is charged.
The major drawback of the normal factory pattern is high gas costs. And that’s where the cloned factory pattern comes in handy.
Since we’re deploying the same contract, each instance of the contract will have identical bytecode. So, storing all bytecode for each deployment repeatedly promotes wastage of gas costs for the bytecode.
The solution to this is a mechanism to deploy only one instance of the Foundation
contract and have all other instances of the Foundation
contract behave as proxies, delegating calls to the first instance of the Foundation
contract and allowing functions to run in the context of the proxy contracts. With this, each instance of the Foundation
contract will have its own state and simply uses the instance of the Foundation
contract established by us as a library.
The eip-1167 created by Peter Murray, Nate Welch, Joe Messerman provides this mechanism. According to its documentation, “This standard specifies a minimal bytecode implementation that delegates all calls to a known, fixed address to simply and cheaply clone contract functionality in an immutable way.”
The clone factory contract is a reference implementation of the eip-1167 standard.
To implement the clone factory contract, you’ll have to install the clone-factory package as follows:
npm install @optionality.io/clone-factory
Now we can implement the clone factory contract on our FoundationFactory
as follows
// SPDX-License-Identifier: MIT pragma solidity >0.4.23 <0.9.0; import "./Foundation.sol"; import "@optionality.io/clone-factory/contracts/CloneFactory.sol"; import "zeppelin-solidity/contracts/ownership/Ownable.sol"; contract FoundationFactory is Ownable, CloneFactory { address public libraryAddress; event FoundationCreated(address newFoundation); function FoundationFactory(address _libraryAddress) public { libraryAddress = _libraryAddress; } function setLibraryAddress(address _libraryAddress) public onlyOwner { libraryAddress = _libraryAddress; } function createFoundation(string _name) public onlyOwner { address clone = createClone(libraryAddress); Foundation(clone).init(_name); FoundationCreated(clone); } }
Here, we import the Ownable
contract from the OpenZeppelin library in order to make use of its onlyOwner
modifier since it’s audited and more secured compared to writing one ourselves.
With the above snippet, we have a contract that will delegate all calls to the contract libraryAddress
at a lower gas cost.
While this is a better mechanism, you should look out for the following:
Now, if you are developing with Truffle, you can deploy the factory contract by creating a new migration file with the following:
const FoundationFactoryContract = artifacts.require("FoundationFactory"); module.exports = function(deployer) { deployer.deploy(FoundationFactoryContract); }
If you are developing with Hardhat, the ethers.getContractFactory
method has you covered so there’s no need to create a factory contract yourself.
You can go ahead and deploy the Foundation
contract as follows:
const hre = require("hardhat"); async function main() { const Foundation= await hre.ethers.getContractFactory("Foundation"); const foundation= await NFTMarket.deploy(); await foundation.deployed(); console.log("Foundation contract deployed to: ", foundation.address); }
In order to examine the difference in gas cost between the CloneFactory
and the normal factory, we’ll deploy each contract to a testnet, followed by executing each contract’s createFoundation
function, then check the transaction hash in the explorer to know how much gas was used. Below is the gas fee charged on executing the createFoundation
function:
Clone factory
Gas Fees:
Base: 12.959896517 Gwei
Normal factory
Gas Fees:
Base: 25.529794514 Gwei
In this article, we’ve explored the factory pattern, its benefits, its types, and when it is best fitted for our smart contracts. Also, we’ve established that the cloned factory pattern is the right pattern for deploying multiple instances of our Solidity smart contract due to its gas cost efficiency.
Hopefully, you’ve found this post informative and helpful. You can follow me on Twitter and LinkedIn where I share daily tips on web3 and web development.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — Start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowSOLID principles help us keep code flexible. In this article, we’ll examine all of those principles and their implementation using JavaScript.
JavaScript’s Date API has many limitations. Explore alternative libraries like Moment.js, date-fns, and the new Temporal API.
Explore use cases for using npm vs. npx such as long-term dependency management or temporary tasks and running packages on the fly.
Validating and auditing AI-generated code reduces code errors and ensures that code is compliant.
4 Replies to "Cloning Solidity smart contracts using the factory pattern"
In the first snippet of code, I think the constructor shouldn’t have the _owner parameter
Should be corrected now — thank you!
In the section ‘Using the clone factory pattern’, the first code snippet for `FoundationFactory` defines a constructor in the old way (<v0.4.23); i.e. instead of `function FoundationFactory(address _libraryAddress) public {…`, it should be `constructor(address _libraryAddress) public {…`.
See: https://stackoverflow.com/questions/50456067/in-solidity-can-the-constructor-function-have-the-same-name-as-the-contract
Am I crazy or is the most important part missing? It doesn’t display the total gas paid when using either option, just shows the gas price in gwei…
“`
Clone factory
Gas Fees:
Base: 12.959896517 Gwei
Normal factory
Gas Fees:
Base: 25.529794514 Gwei
“`