How to deploy minimal proxies using ProxyFactory in Truffle?

:memo:Details

I have deployed a Factory contract and a Cash contract, and used the Factory contract to create some proxies instance contracts of Cash contract, but how do I deploy these proxies instance contracts of Cash contract?

:1234: Code to reproduce

  1. I have a implementation/logic contract called Cash.sol. This is Initializable, Ownable. The Cash contract should be able to generate cash tokens of multiple currencies when ether is paid in. So, a minimal proxy of the Cash contract we deploy for USD should be able to generate a USD token, and a minimal proxy of the Cash contract we deploy for EUR should be able to generate a EUR token, and so forth. The only things that are parametrized for the minimal proxies is therefore a ‘currency name’.
        //initiliaze proxies. here, _owner is the Factory, name is the currency name/symbol
        function initialize(bytes32 _name, address _owner) public {
            Ownable.initialize(_owner);
            factory = Factory(_owner);
            name = _name;
            symbol = _name;
        }
  1. I have a Factory.sol which is a ProxyFactory. It creates proxies of the Cash (implementation) contract and stores the proxy addresses in an array.
            function createToken(address _target, bytes32 tokenName, bytes32 tokenType) external{
                    address _owner = msg.sender;            
                    bytes memory _payload = abi.encodeWithSignature("initialize(bytes32,address)", tokenName, _owner);

                    // Deploy proxy
                    address _via = deployMinimal(_target, _payload);
                    emit TokenCreated(_via, tokenName, tokenType);
                    if(tokenType == "Cash"){
                            token[_via] = via("ViaCash", tokenName);
                            tokens.push(_via);
                    }
            }
  1. I deploy the Factory and call and parameterize createToken() in the Factory with any number and names of currencies I want to support.
        deployer.deploy(Factory).then(async () => {
                const factory = await Factory.new();
                const cash = await Cash.new(); 
                await factory.createToken(cash.address, web3.utils.utf8ToHex("USD"), 
                web3.utils.utf8ToHex("Cash"));  await factory.createToken(cash.address, 
                web3.utils.utf8ToHex("EUR"), web3.utils.utf8ToHex("Cash"));});

Here is another topic link about the issue: 3411

1 Like

Hi @xxs,

You don’t need to use utf8ToHex to convert your strings.
I would create the proxies in a separate script rather than in the one migration.

I have created a simple example below, where I create the proxies in truffle console:

SimpleToken.sol

pragma solidity ^0.5.0;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol";

/**
 * @title SimpleToken
 * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the sender.
 * Note they can later distribute these tokens as they wish using `transfer` and other
 * `ERC20` functions.
 */
contract SimpleToken is Initializable, ERC20, ERC20Detailed {

    /**
     * @dev initialize that gives holder all of existing tokens.
     */
    function initialize(string memory name, string memory symbol, address holder) public initializer {
        ERC20Detailed.initialize(name, symbol, 18);
        _mint(holder, 1000000 * (10 ** uint256(decimals())));
    }
}

SimpleTokenFactory.sol

pragma solidity ^0.5.3;

import "@openzeppelin/upgrades/contracts/upgradeability/ProxyFactory.sol";

contract SimpleTokenFactory is ProxyFactory {
    address[] public tokens;

    event TokenCreated(address indexed tokenAddress);

    function createToken(address logic, string calldata name, string calldata symbol, address holder) external {

        bytes memory payload = abi.encodeWithSignature("initialize(string,string,address)", name, symbol, holder);

        // Deploy minimal proxy
        address token = deployMinimal(logic, payload);
        emit TokenCreated(token);

        tokens.push(token);
    }
}

2_deploy.js

I only deploy the logic contract and the factory.
Minimal proxies can be created via a script or the console

const SimpleToken = artifacts.require("SimpleToken");
const SimpleTokenFactory = artifacts.require("SimpleTokenFactory");

module.exports = async function (deployer) {
  await deployer.deploy(SimpleToken);
  await deployer.deploy(SimpleTokenFactory);
};

Deploy

$ npx truffle migrate

Compiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/SimpleToken.sol
> Compiling ./contracts/SimpleTokenFactory.sol
> Compiling @openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol
> Compiling @openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol
> Compiling @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol
> Compiling @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol
> Compiling @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol
> Compiling @openzeppelin/upgrades/contracts/Initializable.sol
> Compiling @openzeppelin/upgrades/contracts/cryptography/ECDSA.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/BaseAdminUpgradeabilityProxy.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/BaseUpgradeabilityProxy.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/InitializableAdminUpgradeabilityProxy.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/InitializableUpgradeabilityProxy.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/Proxy.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/ProxyFactory.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/UpgradeabilityProxy.sol
> Compiling @openzeppelin/upgrades/contracts/utils/Address.sol
> Artifacts written to /home/abcoathup/projects/forum/xxs/build/contracts
> Compiled successfully using:
   - solc: 0.5.16+commit.9c3226ce.Emscripten.clang



Starting migrations...
======================
> Network name:    'development'
> Network id:      1596177951625
> Block gas limit: 6721975 (0x6691b7)


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0xbdfc268046175ecac57a5c478995178dda88769035299d4d2b92a351c3f0a340
   > Blocks: 0            Seconds: 0
   > contract address:    0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab
   > block number:        1
   > block timestamp:     1596177963
   > account:             0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
   > balance:             99.9967165
   > gas used:            164175 (0x2814f)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.0032835 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:           0.0032835 ETH


2_deploy.js
===========

   Deploying 'SimpleToken'
   -----------------------
   > transaction hash:    0x2f449545e91f8f82878c54ed59b81de9a2d720a09d21d4547da2da2d2eb7ae11
   > Blocks: 0            Seconds: 0
   > contract address:    0xCfEB869F69431e42cdB54A4F4f105C19C080A601
   > block number:        3
   > block timestamp:     1596177963
   > account:             0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
   > balance:             99.96726942
   > gas used:            1430013 (0x15d1fd)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.02860026 ETH


   Deploying 'SimpleTokenFactory'
   ------------------------------
   > transaction hash:    0xd61d6304698eb95d8ef5ea817119f8e2fb9d4124ea184b42653dda06195a2c9b
   > Blocks: 0            Seconds: 0
   > contract address:    0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B
   > block number:        4
   > block timestamp:     1596177963
   > account:             0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
   > balance:             99.92872796
   > gas used:            1927073 (0x1d67a1)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.03854146 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.06714172 ETH


Summary
=======
> Total deployments:   3
> Final cost:          0.07042522 ETH

Create and interact with minimal proxies

$ npx truffle console
truffle(development)> logic = await SimpleToken.deployed()
truffle(development)> factory = await SimpleTokenFactory.deployed()
truffle(development)> await factory.createToken(logic.address, "Token A", "TKA", accounts[0])
truffle(development)> await factory.createToken(logic.address, "Token B", "TKB", accounts[0])
truffle(development)> tokenA = await SimpleToken.at(await factory.tokens(0))
truffle(development)> tokenB = await SimpleToken.at(await factory.tokens(1))
truffle(development)> await tokenA.name()
'Token A'
truffle(development)> await tokenB.name()
'Token B'

You also had a separate question about what does npx oz deploy kind `minimal do.
This creates minimal proxies using the OpenZeppelin CLI.

Please find below a deployment of the SimpleToken as a minimal proxy using the CLI.

$ npx oz deploy
✓ Compiling contracts with Truffle, using settings from truffle.js file
Truffle output:

Compiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/SimpleToken.sol
> Compiling ./contracts/SimpleTokenFactory.sol
> Compiling @openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol
> Compiling @openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol
> Compiling @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol
> Compiling @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol
> Compiling @openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol
> Compiling @openzeppelin/upgrades/contracts/Initializable.sol
> Compiling @openzeppelin/upgrades/contracts/cryptography/ECDSA.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/BaseAdminUpgradeabilityProxy.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/BaseUpgradeabilityProxy.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/InitializableAdminUpgradeabilityProxy.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/InitializableUpgradeabilityProxy.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/Proxy.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/ProxyFactory.sol
> Compiling @openzeppelin/upgrades/contracts/upgradeability/UpgradeabilityProxy.sol
> Compiling @openzeppelin/upgrades/contracts/utils/Address.sol
> Artifacts written to /home/abcoathup/projects/forum/xxs/build/contracts
> Compiled successfully using:
   - solc: 0.5.16+commit.9c3226ce.Emscripten.clang


? Choose the kind of deployment minimal
? Pick a network development
? Pick a contract to deploy SimpleToken
Minimal proxy support is still experimental.
✓ Added contract SimpleToken
✓ Contract SimpleToken deployed
All implementations have been deployed
? Call a function to initialize the instance after creating it? Yes
? Select which function * initialize(name: string, symbol: string, holder: address)
? name: string: My Token
? symbol: string: TKN
? holder: address: 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
✓ Instance created at 0x3408fb50BEeEF75145EC4a51Ba819cb7c71d2732
✓ Deployed ProxyFactory at 0x59d3631c86BbE35EF041872d502F218A39FBa150
To upgrade this instance run 'oz upgrade'
0x3408fb50BEeEF75145EC4a51Ba819cb7c71d2732

We can now interact with the token using call and send-tx

$ npx oz call
? Pick a network development
? Pick an instance SimpleToken at 0x3408fb50BEeEF75145EC4a51Ba819cb7c71d2732
? Select which function name()
✓ Method 'name()' returned: My Token
My Token
1 Like

Thank u very much for answering.

You mean actually npx oz delpoy>minimal is also used to creates minimal proxies using the OpenZeppelin CLI, the difference is it won’t use factory contract to create oproxies, it used the CLI to create the proxy of logic contract, right?

So in the above last example, you used the initialize function of the logic contract to create a proxy of the logic contract, i.e., My Token is a proxy contract of the SimpleToken, right?

1 Like

Yes. We can use the CLI to interactively create a minimal proxy and interact with it.
If you are creating a large amount of minimal proxies, then creating a factory inheriting from ProxyFactory, deploying the logic and factory contracts, then running a script to create the minimal proxies (like you were trying to do) is probably the best way forward.

Yes. I created a minimal proxy pointing to the logic contract and initialized it, all using the CLI.

Thanks very much. Another question about the Truffle:
When I inputted truffle console, it showed the error:

When I inputted truffle develop, it showed the error:

Why they happened and what should I do?

1 Like

Hi @xxs,

I have ganache-cli installed globally and running in a separate terminal.

I have a development network in truffle-config.js

    development: {
     host: "127.0.0.1",     // Localhost (default: none)
     port: 8545,            // Standard Ethereum port (default: none)
     network_id: "*",       // Any network (default: none)
    },

I am running on WSL2, node 10.

I am not sure why you are getting those errors with truffle. You may need to check with the truffle team.

@abcoathup Thanks.
Another question:
Usually by default, accounts[0] is the account that uses factory contract to create proxy contract of the logic contract, so does that mean accounts[0] is the proxyAdmin? so I should not use accounts[0] to interacte with the proxy contract?

And

? Choose the kind of deployment minimal
? Pick a network development
? Pick a contract to deploy SimpleToken
Minimal proxy support is still experimental.
✓ Added contract SimpleToken
✓ Contract SimpleToken deployed
All implementations have been deployed
? Call a function to initialize the instance after creating it? Yes
? Select which function * initialize(name: string, symbol: string, holder: address)
? name: string: My Token
? symbol: string: TKN
? holder: address: 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
✓ Instance created at 0x3408fb50BEeEF75145EC4a51Ba819cb7c71d2732
✓ Deployed ProxyFactory at 0x59d3631c86BbE35EF041872d502F218A39FBa150
To upgrade this instance run 'oz upgrade'
0x3408fb50BEeEF75145EC4a51Ba819cb7c71d2732

I konw that holder: address: 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1 is the address of the accounts[0].
But whose address is ✓ Deployed ProxyFactory at 0x59d3631c86BbE35EF041872d502F218A39FBa150 ? either the factory or the proxy address?
What’s the differencce between 0x59d3631c86BbE35EF041872d502F218A39FBa150 and 0x3408fb50BEeEF75145EC4a51Ba819cb7c71d2732?
Which is the address of the instance and which is the address of the roxy contract?
You have did some interactions with the instance of the logic contract, but if I want to interact with the proxy contract, what should I do?
Thanks very much!

1 Like

Hi @xxs,

Upgradeable contracts have an admin account to manage the upgrades.
Minimal proxies aren't upgradeable and don't have an admin account.
The ProxyAdmin contract can be used as the admin account of an upgradeable contract to allow any other account to interact with an upgradeable proxy.

It is only when you deploy a proxy contract without a proxy admin (which you can't do using the CLI) that you need to ensure that you interact with the contract from a non


The OpenZeppelin CLI deploys a ProxyFactory contract for deploying minimal proxies.

0x59... is the ProxyFactory contract
0x34... is the minimal proxy.

To interact with the minimal proxy you can use the OpenZeppelin CLI

3 posts were split to a new topic: Deploy ProxyFactory with Buidler

A post was split to a new topic: How to use Upgrades Plugins to deploy multiple minimal proxies

A post was split to a new topic: How to deploy minimal proxies with ProxyFactory deprecated?