Frequently Asked Questions

Can I change Solidity compiler versions when upgrading?

Yes. The Solidity team guarantees that the compiler will preserve the storage layout across versions.

Why am I getting the error "Cannot call fallback function from the proxy admin"?

This is due to the Transparent Proxy Pattern. You shouldn’t get this error when using the OpenZeppelin Upgrades Plugins, since it uses the ProxyAdmin contract for managing your proxies.

However, if you are using OpenZeppelin Contracts proxies programmatically you could potentially run into such error. The solution is to always interact with your proxies from an account that is not the admin of the proxy, unless you want to specifically call the functions of the proxy itself.

What does it mean for a contract to be upgrade safe?

When deploying a proxy for a contract, there are some limitations to the contract code. In particular, the contract cannot have a constructor, and should not use the selfdestruct or delegatecall operations for security reasons.

As a replacement for the constructor, it is common to set up an initialize function to take care of the contract’s initialization. You can use the Initializable base contract to have access to an initializer modifier that ensures the function is only called once.

import "@openzeppelin/contracts/proxy/Initializable.sol";
// Alternatively, if you are using @openzeppelin/contracts-upgradeable:
// import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";

contract MyContract is Initializable {
  uint256 value;
  function initialize(uint256 initialValue) public initializer {
    value = initialValue;
  }
}

Both plugins will validate that the contract you are trying to deploy complies with these rules. You can read more about how to write upgrade safe contracts here.

What does it mean for an implementation to be compatible?

When upgrading a proxy from one implementation to another, the storage layout of both implementations must be compatible. This means that, even though you can completely change the code of the implementation, you cannot modify the existing contract state variables. The only operation allowed is to append new state variables after the ones already declared.

Both plugins will validate that the new implementation contract is compatible with the previous one.

You can read more about how to make storage-compatible changes to an implementation contract here.

What is a proxy admin?

A ProxyAdmin is a contract that acts as the owner of all your proxies. Only one per network gets deployed. When you start your project, the ProxyAdmin is owned by the deployer address, but you can transfer ownership of it by calling transferOwnership.

What is an implementation contract?

Upgradeable deployments require at least two contracts: a proxy and an implementation. The proxy contract is the instance you and your users will interact with, and the implementation is the contract that holds the code. If you call deployProxy several times for the same implementation contract, several proxies will be deployed, but only one implementation contract will be used.

When you upgrade a proxy to a new version, a new implementation contract is deployed if needed, and the proxy is set to use the new implementation contract. You can read more about the proxy upgrade pattern here.

What is a proxy?

A proxy is a contract that delegates all of its calls to a second contract, named an implementation contract. All state and funds are held in the proxy, but the code actually executed is that of the implementation. A proxy can be upgraded by its admin to use a different implementation contract.

You can read more about the proxy upgrade pattern here.

Why can’t I use immutable variables?

Solidity 0.6.5 introduced the immutable keyword to declare a variable that can be assigned only once during construction and can be read only after construction. It does so by calculating its value during contract creation and storing its value directly into the bytecode.

Notice that this behavior is incompatible with the way upgradeable contracts work for two reasons:

  1. Upgradeable contracts have no constructors but initializers, therefore they can’t handle immutable variables.

  2. Since the immutable variable value is stored in the bytecode its value would be shared among all proxies pointing to a given contract instead of each proxy’s storage.

In some cases immutable variables are upgrade safe. The plugins cannot currently detect these cases automatically so they will point it out as an error anyway. You can manually disable the check using the option unsafeAllow: ['state-variable-immutable'], or in Solidity >=0.8.2 placing the comment /// @custom:oz-upgrades-unsafe-allow state-variable-immutable before the variable declaration.

Why can’t I use external libraries?

At the moment the plugins only have partial support for upgradeable contracts linked to external libraries. This is because it’s not known at compile time what implementation is going to be linked thus making very difficult to guarantee the safety of the upgrade operation.

There are plans to add this functionality in the near future with certain constraints that make the issue easier to address like assuming that the external library’s source code is either present in the codebase or that it’s been deployed and mined so it can be fetched from the blockchain for analysis.

In the meantime you can deploy upgradeable contracts linked to external libraries by setting the unsafeAllowLinkedLibraries flag to true in the deployProxy or upgradeProxy calls, or including 'external-library-linking' in the unsafeAllow array. Keep in mind the plugins will not verify that the linked libraries are upgrade safe. This has to be done manually for now until the full support for external libraries is implemented.

You can follow or contribute to this issue in Github.

Can I use custom types like structs and enums?

Past versions of the plugins did not support upgradeable contracts that used custom types like structs or enums in their code or linked libraries. This is no longer the case for current versions of the plugins, and structs and enums will be automatically checked for compatibility when upgrading a contract.

Some users who have already deployed proxies with structs and/or enums and who need to upgrade those proxies may need to use the override flag unsafeAllowCustomTypes for their next upgrade, after which it will no longer be necessary. If the project contains the source code for the implementation currently in use by the proxy, the plugin will attempt to recover the metadata that it needs before the upgrade, falling back to the override flag if this is not possible.

How can I disable some checks?

Deployment and upgrade related functions come with an optional opts object, which includes an unsafeAllow option. This can be set to disable any check performed by the plugin. The list of checks that can individually be disabled is: * state-variable-assignment * state-variable-immutable * external-library-linking * struct-definition * enum-definition * constructor * delegatecall * selfdestruct

This function is a generalized version of unsafeAllowCustomTypes and unsafeAllowLinkedLibraries allowing any check to be manually disabled.

For example, in order to upgrade to an implementation that contains a delegate call, you would call:

await upgradeProxy(proxyAddress, implementationFactory, { unsafeAllow: ['delegatecall'] });

Additionally, it is possible to preciselly disable checks directly from the Solidity source code using natspec comments. This requires Solidity >=0.8.2.

contract SomeContract {
  function some_dangerous_function() public {
    ...
    /// @custom:oz-upgrades-unsafe-allow delegatecall
    (bool success, bytes memory returndata) = msg.sender.delegatecall("");
    ...
  }
}

This syntax can be used will the following errors: * /// @custom:oz-upgrades-unsafe-allow state-variable-immutable * /// @custom:oz-upgrades-unsafe-allow state-variable-assignment * /// @custom:oz-upgrades-unsafe-allow external-library-linking * /// @custom:oz-upgrades-unsafe-allow constructor * /// @custom:oz-upgrades-unsafe-allow delegatecall * /// @custom:oz-upgrades-unsafe-allow selfdestruct

In some cases you may want to allow multiple errors in a single line.

contract SomeOtherContract {
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
  uint256 immutable x = 1;
}

Why do I have to recompile all contracts for Truffle?

Truffle artifacts (the JSON files in build/contracts) contain the AST (abstract syntax tree) for each of your contracts. Our plugin uses this information to validate that your contracts are [upgrade safe](#what-does-it-mean-for-a-contract-to-be-upgrade-safe).

Truffle sometimes partially recompiles only the contracts that have changed. We will ask you to trigger a full recompilation either using truffle compile --all or deleting the build/contracts directory when this happens. The technical reason is that since Solidity does not produce deterministic ASTs, the plugins are unable to resolve references correctly if they are not from the same compiler run.