Blockchain is a new technology having many glitches and limitations. Ethereum being the most popular smart contract platform has inherent limitations regarding block gas limit where sum of all block’s transactions gas limits can’t go over certain level. There are many use-cases where some contracts can be deployed directly on-chain (from another contract), because they have wide usage (for example ERC20 Token contracts).
If there are many variations of those contracts that can be deployed directly on-chain, deploying them from single contract is problematic since those variations cost gas, and it’s easy to exceed block gas limit. That’s where Abstract Factory pattern and separation of concerns comes in use.
2. About me
• Dejan Radić (29)
• MSc in Computer Science (University of Banja Luka)
• Participated on 20+ projects for international clients
as a developer
• Currently CTO at Oroundo (Culture & Tourism)
• Managing 15+ people on 3+ projects
• Attending conferences and meet-ups as a speaker
• Lately interested & passionate about blockchain
4. • Can smart contract create a new smart contract?
• Can they have the ability to create multiple kinds of
them?
• Is Ethereum platform limited in a way which prohibits
us to easily create such functionality?
• Use of abstract factory pattern instead of simple
factory changes the perspective
• Usage example: building an ICO/UTO/STO/TGE
platform on blockchain (token & crowdsale)
• Example: Creation of fungible (ERC20) and non-
fungible (ERC721) tokens
• UML class diagrams (draw.io)
• Examples in Solidity
• Solidity understanding level: Intermediate
Intro
5. On-chain contract deployments
• Yes, smart contract can create another smart contract by
using a keyword “new” (usually heap allocation)
• MyContract c1 = MyContract(someAddress);
• MyContract c2 = new MyContract(constructorParams);
• Getting the addresses (which are representing pointers or
references in Solidity) through address(c1) and
address(c2)
• Creating new contract consumes 21 000 gas immediately
• EtherScan source code verification !?
6. Simple factory
pattern approach
• Factory creates new
items based on
specified type
• Type is specified over
enum
• Both tokens
(bytecodes) are
deployed together
with factory
• Storage costs gas !?
7. Block gas limit
• Sum of all transaction gas limits in a block can
not exceed a certain value
• Block gas limit is different between the blocks
• New block gas limit is decided by algorithm
and voted by miners
• “Exceeds block gas limit” error shows if
transaction consumes more gas than available
• Values are different between Mainnet and
Ropsten (8m vs 4.7m)
10. Interfaces
contract IToken {
function name() public view returns (string memory name);
function symbol() public view returns (string memory symbol);
function balanceOf(address _owner) public view returns (uint);
}
contract ITokenFactory {
function createERC20(string memory name, string memory symbol, uint8 decimals) public returns (ERC20);
function createERC721(string memory name, string memory symbol) public returns (ERC721);
}
• Implementation in Solidity 0.5.1
• 0.5.0 version brought a few breaking changes
• Prefix “I” to emphasize it’s an interface
11. contract ERC20 is IToken {
string internal _name ;
string internal _symbol;
uint8 internal _decimals;
mapping(address => uint) public balances;
constructor (string memory name, string memory symbol, uint8 decimals) public {
_name = name;
_symbol = symbol;
_decimals = decimals;
}
function name() public view returns (string memory name) {
return _name;
}
function symbol() public view returns (string memory symbol) {
return _symbol;
}
function decimals() public view returns (uint8) {
return _decimals;
}
function balanceOf(address _owner) public view returns (uint) {
require(_owner != address(0));
return balances[_owner];
}
}
ERC20implementation
12. contract ERC721 is IToken {
string internal _name ;
string internal _symbol;
mapping(address => uint) public ownedTokensCount;
constructor (string memory name, string memory symbol) public {
_name = name;
_symbol = symbol;
}
function name() public view returns (string memory name) {
return _name;
}
function symbol() public view returns (string memory symbol) {
return _symbol;
}
function balanceOf(address _owner) public view returns (uint) {
return ownedTokensCount[_owner];
}
}
ERC721implementation
13. Concrete factory implementations
contract ERC20Factory is ITokenFactory {
function createERC20(string memory name, string memory symbol, uint8 decimals) public returns (ERC20) {
return new ERC20(name, symbol, decimals);
}
function createERC721(string memory name, string memory symbol) public returns (ERC721) {
require(false);
}
}
contract ERC721Factory is ITokenFactory {
function createERC20(string memory name, string memory symbol, uint8 decimals) public returns (ERC20) {
require(false);
}
function createERC721(string memory name, string memory symbol) public returns (ERC721) {
return new ERC721(name, symbol);
}
}
• Client has references (addresses) of all concrete factories
• All creating functions are implemented in all factories
• require(false) or revert() instead of return null
14. Custom contract deployment
function createCustomContract(bytes _code) public returns (address) {
address deployedAddress;
uint256 successIndication;
assembly {
deployedAddress := create(0, add(_code, 0x20), mload(_code))
successIndication := gt(extcodesize(deployedAddress),0)
}
require(successIndication > 0);
return deployedAddress;
}
• If required contract has more functionalities than the ones
available through factory, there is a way to deploy custom
contract over specified bytecode using assembly functions.
• Compilation over solc.js (through ĐApp)
• use of ERC-165 to check the interface of deployed contract
15. Conclusion
• Ethereum is still a novel technology
• Thinking outside of the box
• Divide and conquer technique lead to
separation of concerns
• Multiple separate deployments of concrete
factories overcome the inherent block size
limitations
• Application of design patterns, not just
because of standardization, but because of
different view of a problem
• Get a habit of drawing things, it can help with
change of perspective