Workshop Recap: Cheap contract deployment through Clones

Originally published at: https://blog.openzeppelin.com/workshop-recap-cheap-contract-deployment-through-clones/

Workshop Recap: Cheap contract deployment through Clones

The workshop was recorded on the 27th February 2021 and led by Hadrien Croubois – Smart Contract Engineer at OpenZeppelin.

The workshop covers the following:

  • Typical use cases: Uniswap pairs & Argent wallets
  • Walkthrough building a contract factory using the Clones library
  • Gas cost comparisons
  • Create vs Create2, when to use each one
  • Comparison with other proxies

You can watch the video, view the slides, try the code from the workshop.

Why Clones?

Clones (minimal proxies) as described in ERC1167, are very small, and cheap to deploy, smart-contracts that delegate all incoming calls to an implementation (template) contract containing the functionality. The address of this implementation contract is stored directly in the contract code, so no sload is required.

Using clones, significant gas savings can be made for deployments when more than one of a family of smart contracts will be deployed over time e.g. multiple ERC20, Uniswap pairs or smart contract wallets.

The Clones library in OpenZeppelin Contracts includes functions to deploy a proxy using either create (traditional deployment) or create2 (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the deterministic method. The Clones library has been available since OpenZeppelin Contracts 3.4.

Video

Slides

20210227 – Contracts clones workshop.pdf

Code for the workshop

The workshop code consists of the following examples of deploying using the Clones library, along with tests and gas usage reports showing the difference in deployment and usage gas costs between the standard case and using Clones:

  • ERC20
  • Uniswap pair
  • Argent wallet

The code can be found in the OpenZeppelin workshops mono-repository:
https://github.com/OpenZeppelin/workshops/tree/master/02-contracts-clone

See the instructions in the repository to run the workshop code.

Learn more

Deep dive into the Minimal Proxy: blog.openzeppelin.com/deep-dive-into-the-minimal-proxy-contract.

Learn more about OpenZeppelin Contracts: openzeppelin.com/contracts
See the documentation: docs.openzeppelin.com/contracts
Ask questions on the community forum: forum.openzeppelin.com

2 Likes

hi! this workshop is awesome!

I need help to write some tests on Truffle for my Contract Clone Factory.
I’m kind of a JS n00b.

My Contract is actually called Letter. Here’s how LetterFactory.sol looks like:

pragma solidity ^0.8.0;

import "./Letter.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";

contract LetterFactory {

    address immutable letterImplementation;

    constructor() {
        letterImplementation = address(new Letter());
    }

    function createLetter(string memory _titleMint, string memory _firstPageMint, string memory _authorMint)
    external
    returns (address) {
        address letterClone = Clones.clone(letterImplementation);
        Letter(letterClone).initLetter(_titleMint, _firstPageMint, _authorMint);
        return letterClone;
    }
}

Now I want to write some tests and verify the functionality of the deployed Clones.

Here’s how my Truffle test TestLetterFactory.js looks like so far:

const LetterFactory = artifacts.require("LetterFactory");

var accounts;

var alice;
var bob;
var carol;

contract('LetterFactory', (accs) => {
    accounts = accs;

    alice = accounts[0];
    bob = accounts[1];
    carol = accounts[2];
});

it('create Letters from LetterFactory', async() => {
    const letterFactory = await LetterFactory.deployed();

    const txA = await letterFactory.createLetter("titleA", "initPageA", "authorA", {from: alice});
    const txB = await letterFactory.createLetter("titleB", "initPageB", "authorB", {from: bob});
    const txC = await letterFactory.createLetter("titleC", "initPageC", "authorC", {from: carol});

    // ...
});

I want to get variables contractA, contractB and contractC representing each deployed Clone Instance.

I can’t figure out how to extract this information, since letterFactory.createLetter returns Transaction objects.

Any help would be much appreciated!

Hi, welcome! :wave:

Sorry, I am not sure how to do this in the truffle, I always use Hardhat to test, and with it, you can use contract.callStatic.functionName() to get the return value.