Can you deploy non-upgradeable smart contracts with zos?

I have (1) a protocol and (2) a dapp, with the latter being a wrapper around the former. I want the protocol to be non-upgradeable, while the dapp to remain upgradeable.

They are separated as individual packages in a monorepo. In the protocol package, I’m using openzeppelin-solidity, while for the dapp I’m using openzeppelin-eth and zos-lib.

There doesn’t seem to be any obvious issue with the aforementioned set-up, but I’m wondering if I can use zos in the following way?

zos create Dapp --non-upgradeable

If I could, that’d really helpful. I’d have ubiquitous scripts across the monorepo - for instance, running zos compile in both the protocol and the package instead of truffle compile --all in one of them.


Hi @PaulRBerg

There is an open issue to support creating non-upgradeable contracts:

The 2.4.0 release added experimental support for EIP1167 minimal proxies adding the --minimal flag to zos create

EIP1167 minimal proxies

We have added experimental support for EIP1167 minimal proxies. These proxies rely on the same delegatecall pattern as the usual ZeppelinOS proxies, but have two main differences:

  • They cannot be upgraded to a different version of the code
  • They are incredibly cheap to deploy: about 1/10th of the standard proxy!

These features make minimal proxies an ideal choice when you want to spawn many copies of the same contract, without worrying about maintaining them in the future. They also play nicely with EVM packages: since the package developer pays the deployment cost of the logic contracts, you only need to pay for the minimal proxies.

You can try deploying one of these proxies by adding the --minimal flag when running zos create . Remember that these proxies will not be upgradeable to a different version of the code: the reduced deployment gas fee comes at a cost!


Hi @PaulRBerg
Following up, do minimal proxies meet your needs?
Would appreciate your perspective for your use case.

I just finished reading the EIP-1167 spec on GitHub and it looks good enough for my needs. However, I want to make sure that I got this right:

small operational overhead - adds a single call cost to each call

Will users have to pay 42,000 instead of the base 21,000 gas cost?

1 Like

Hi @PaulRBerg

I deployed a simple Counter contract to test out the overhead of calling a minimal proxy and an upgradeable proxy. The overhead appears relatively small for my example (775 gas for a minimal proxy).

My results are in the same ballpark as what @ylv-io found using a previous version of ZeppelinOS. Gas overhead for proxied function call

You may want to test on one of your contracts.

Vanilla contract (deployed via Truffle)
First: 41,653; Subsequent: 26,653

Upgradeable Proxy (deployed via OpenZeppelin SDK)
First: 43,239; Subsequent: 28,239

Minimal Proxy (deployed via OpenZeppelin SDK)
First: 42,428; Subsequent: 27,428

Counter.sol (simple contract example)

pragma solidity ^0.5.0;

contract Counter {
  uint256 public value;
  function increase() public {
1 Like

Awesome analysis, @abcoathup! Thanks for looking into it and sharing your numbers! The 775 overhead makes sense, since the base cost for a CALL opcode is 700 gas (see Gcall in Appendix G of the yellow paper).

Nope. The 21000 base cost is that of sending a new transaction to the network. The overhead imposed by the proxy is an extra CALL opcode, which is in the ballpark of 700 gas, as Andy pointed out.


Thanks @abcoathup and @spalladino, greatly appreciate your analysis.

1 Like