Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial docs #142

Merged
merged 17 commits into from
Aug 31, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 0 additions & 117 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,123 +75,6 @@ it('works before and after upgrading', async function () {
});
```

## Usage


## How do the plugins work?
abcoathup marked this conversation as resolved.
Show resolved Hide resolved

Both plugins provide two main functions, `deployProxy` and `upgradeProxy`, which take care of managing upgradeable deployments of your contracts. In the case of `deployProxy`, this means:

1. Validate that the implementation is [upgrade safe](#what-does-it-mean-for-a-contract-to-be-upgrade-safe)

2. Deploy a [proxy admin](#what-is-a-proxy-admin) for your project

3. Deploy the [implementation contract](#what-is-an-implementation-contract)

4. Create and initialize the proxy contract

And when you call `upgradeProxy`:

1. Validate that the new implementation is [upgrade safe](#what-does-it-mean-for-a-contract-to-be-upgrade-safe) and is [compatible](#what-does-it-mean-for-an-implementation-to-be-compatible) with the previous one

2. Check if there is an [implementation contract](#what-is-an-implementation-contract) deployed with the same bytecode, and deploy one if not

3. Upgrade the proxy to use the new implementation contract

The plugins will keep track of all the implementation contracts you have deployed in an `.openzeppelin` folder in the project root, as well as the proxy admin. You will find one file per network there. It is advised that you commit to source control the files for all networks except the development ones (you may see them as `.openzeppelin/unknown-*.json`).

> Note: the format of the files within the `.openzeppelin` folder is not compatible with those of the [OpenZeppelin CLI](https://docs.openzeppelin.com/cli/2.8/). If you want to use these plugins for an existing OpenZeppelin CLI project, we will be sharing soon a guide on how to migrate.

## Managing ownership

All proxies define an _admin_ address which has the rights to upgrade them. By default, the admin is a [proxy admin contract](#what-is-a-proxy-admin) deployed behind the scenes. You can change the admin of a proxy by calling the `admin.changeAdminForProxy` function in the plugin. Keep in mind that the _admin_ of a proxy can only upgrade it, but not interact with the implementation contract. Read [here](https://docs.openzeppelin.com/upgrades/2.8/proxies#transparent-proxies-and-function-clashes) for more info on this restriction.

The proxy admin contract also defines an _owner_ address which has the rights to operate it. By default, this address is the externally owned account used during deployment. You can change the proxy admin owner by calling the `admin.transferProxyAdminOwnership` function in the plugin. Note that changing the proxy admin owner effectively transfers the power to upgrade any proxy in your whole project to the new owner, so use with care.

Once you have transferred the rights to upgrade a proxy to another address, you can still use your local setup to validate and deploy the implementation contract. The plugins include a `prepareUpgrade` function that will validate that the new implementation is upgrade-safe and compatible with the previous one, and deploy it using your local Ethereum account. You can then execute the upgrade itself from the admin address.

Refer to each plugin documentation for more details on the `admin` functions.

## FAQ

### 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`](https://docs.openzeppelin.com/upgrades/2.8/writing-upgradeable#initializers) base contract to have access to an `initializer` modifier that ensures the function is only called once.

```solidity
import "@openzeppelin/upgrades-core/contracts/Initializable.sol";
// Alternatively, if you are using @openzeppelin/contracts-ethereum-package:
// import "@openzeppelin/contracts-ethereum-package/contracts/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](https://docs.openzeppelin.com/upgrades/2.8/writing-upgradeable).

### 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. However, the plugins currently do not support validating custom types (enums or structs). To force a deployment where custom types are involved, set the `unsafeAllowCustomTypes` flag to true in the `deployProxy` or `upgradeProxy` call.

You can read more about how to make storage-compatible changes to an implementation contract [here](https://docs.openzeppelin.com/upgrades/2.8/writing-upgradeable#modifying-your-contracts).

### What is a proxy admin?

A [`ProxyAdmin`](packages/core/contracts/proxy/ProxyAdmin.sol) 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`](https://docs.openzeppelin.com/contracts/3.x/api/access#Ownable-transferOwnership-address-).

You can read more about this contract [here](https://docs.openzeppelin.com/cli/2.8/contracts-architecture#proxyadmin.sol).

### 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](https://docs.openzeppelin.com/upgrades/2.8/proxies).

### 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](https://docs.openzeppelin.com/upgrades/2.8/proxies).

### Why can't I use `immutable` variables?

Solidity 0.6.5 [introduced the `immutable` keyword]([https://github.com/ethereum/solidity/releases/tag/v0.6.5](https://github.com/ethereum/solidity/releases/tag/v0.6.5)) 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.

### Why can't I use external libraries?

At the moment the plugins do not support 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.

You can follow or contribute to [this issue in Github](https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/52).

### Why can't I use custom types like structs and enums?

At the moment the plugins do not support upgradeable contracts that implement or make use of custom types like structs or enums in their code or linked libraries. This is because of the additional complexity of checking for storage layout incompatibilities during an upgrade. (See ["What does it mean for an implementation to be compatible?"](#what-does-it-mean-for-an-implementation-to-be-compatible).)

In the mean time, we encourage users to either avoid using these kind of types or to manually check for [storage incompatibilities](https://docs.openzeppelin.com/upgrades/2.8/writing-upgradeable#modifying-your-contracts) and make use of the `unsafeAllowCustomTypes` flag available for the `deployProxy`, `upgradeProxy` and `prepareUpgrade` functions. If you're unsure about how to do this manual check, we'll be happy to help out with your situation if you [post in the forum](https://forum.openzeppelin.com).

You can follow or contribute to [this issue in Github](https://github.com/OpenZeppelin/openzeppelin-upgrades/issues/95).

### 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.

## Community

Join the [OpenZeppelin forum](https://forum.openzeppelin.com/) to ask questions or discuss about these plugins, smart contracts upgrades, or anything related to Ethereum development!
Expand Down
12 changes: 7 additions & 5 deletions docs/modules/ROOT/pages/network-files.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= Network Files

OpenZeppelin Upgrades keep track of all the implementation contracts you have deployed in an `.openzeppelin` folder in the project root, as well as the proxy admin. You will find one file per network there. It is advised that you commit to source control the files for all networks except the development ones (you may see them as `.openzeppelin/unknown-*.json`).
OpenZeppelin Upgrades keep track of all the contract versions you have deployed in an `.openzeppelin` folder in the project root, as well as the proxy admin. You will find one file per network there. It is advised that you commit to source control the files for all networks except the development ones (you may see them as `.openzeppelin/unknown-*.json`).

> Note: the format of the files within the `.openzeppelin` folder is not compatible with those of the xref:cli::index.adoc[OpenZeppelin CLI]. If you want to use these plugins for an existing OpenZeppelin CLI project, we will be sharing soon a guide on how to migrate.

Expand Down Expand Up @@ -43,16 +43,18 @@ OpenZeppelin Upgrades will generate a file for each of the networks you work on
For every logic contract, besides the deployment address, the following info is also tracked:
abcoathup marked this conversation as resolved.
Show resolved Hide resolved

* `types` keeps track of all the types used in the contract or its ancestors, from basic types like `uint256` to custom `struct` types
* `storage` tracks the storage layout of the linearized contract, referencing the types defined in the `types` section, and is used for verifying that any storage layout changes between subsequent versions are compatible
* `storage` tracks the storage layout of the linearized contract, referencing the types defined in the `types` section, and is used for verifying that any xref:faq.adoc#what-does-it-mean-for-an-implementation-to-be-compatible[storage layout changes between subsequent versions are compatible]

The naming of the file will be `<network_name>.json`, but note that `<network_name>` is not taken from the name of the network's entry in the Truffle or Buidler configuration file, but is instead inferred from the canonical network id associated to the entry.
The naming of the file will be `<network_name>.json`, but note that `<network_name>` is not taken from the name of the network's entry in the Truffle or Buidler configuration file, but is instead inferred from the chain id associated to the entry.

There is a limited set of public chains; Chains not on the list such as Ethereum Classic will have network files named `unknown-<chain_id>.json`.

[[configuration-files-in-version-control]]
== Configuration Files in Version Control

Public network files like `mainnet.json` or `ropsten.json` should be tracked in version control. These contain valuable information about your project's status in the corresponding network; the addresses of the contract implementations that have been deployed, the addresses of the proxies that have been deployed, etc. Such files should be identical for all the contributors of a project.
Public network files like `mainnet.json` or `ropsten.json` should be tracked in version control. These contain valuable information about your project's status in the corresponding network, like the addresses of the contract versions that have been deployed. Such files should be identical for all the contributors of a project.

However, local network files like `unknown-<network_id>.json` only represent a project's deployment in a temporary local network such as `ganache-cli` that are only relevant to a single contributor of the project and should not be tracked in version control.
However, local network files like `unknown-<chain_id>.json` only represent a project's deployment in a temporary local network such as `ganache-cli` that are only relevant to a single contributor of the project and should not be tracked in version control.

An example `.gitignore` file could contain the following entries:

Expand Down
14 changes: 7 additions & 7 deletions docs/modules/ROOT/pages/writing-upgradeable.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ contract MyContract is BaseContract {
}
----

[[use-upgradeable-packages]]
=== Using Upgradeable Packages
[[use-upgradeable-libraries]]
=== Using Upgradeable Smart Contract Libraries

Keep in mind that this restriction affects not only your contracts, but also the contracts you import from a library. Consider for example https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/token/ERC20/ERC20.sol[`ERC20`] from OpenZeppelin Contracts: the contract initializes the token's name, symbol and decimals in its constructor.

Expand Down Expand Up @@ -155,7 +155,7 @@ contract ERC20UpgradeSafe is Initializable, ContextUpgradeSafe, IERC20 {
}
----

Whether using OpenZeppelin Contracts or another Ethereum Package, always make sure that the package is set up to handle upgradeable contracts.
Whether using OpenZeppelin Contracts or another smart contract library, always make sure that the package is set up to handle upgradeable contracts.

[[avoid-initial-values-in-field-declarations]]
=== Avoiding Initial Values in Field Declarations
Expand All @@ -182,7 +182,7 @@ contract MyContract is Initializable {
}
----

Note that it still is fine to set constants here, because the compiler https://solidity.readthedocs.io/en/latest/contracts.html#constant-state-variables[does not reserve a storage slot for these variables], and every occurrence is replaced by the respective constant expression. So the following still works with OpenZeppelin Upgrades:
Note that it is still fine to define _constant_ state variables in this way, because the compiler https://solidity.readthedocs.io/en/latest/contracts.html#constant-state-variables[does not reserve a storage slot for these variables], and every occurrence is replaced by the respective constant expression. So the following still works with OpenZeppelin Upgrades:

[source,solidity]
----
Expand Down Expand Up @@ -216,7 +216,7 @@ contract MyContract is Initializable {
}
----

The easiest way around this issue is to simply accept an instance of that contract as a parameter, and inject it after creating it:
If you would like the `ERC20` instance to be upgradeable, the easiest way to achieve that is to simply accept an instance of that contract as a parameter, and inject it after creating it:

[source,solidity]
----
Expand Down Expand Up @@ -245,7 +245,7 @@ There is, however, an exception. If the direct call to the logic contract trigge

A similar effect can be achieved if the logic contract contains a `delegatecall` operation. If the contract can be made to `delegatecall` into a malicious contract that contains a `selfdestruct`, then the calling contract will be destroyed.

As such, it is strongly recommended to avoid any usage of either `selfdestruct` or `delegatecall` in your contracts. If you need to include them, make absolutely sure they cannot be called by an attacker on an uninitialized logic contract.
As such, it is not allowed to use either `selfdestruct` or `delegatecall` in your contracts.

[[modifying-your-contracts]]
== Modifying Your Contracts
Expand Down Expand Up @@ -391,4 +391,4 @@ contract Base {
}
----

Then the variable `base2` would be assigned the slot that `child` had in the previous version. A workaround for this is to declare unused variables on base contracts that you may want to extend in the future, as a means of "reserving" those slots. Note that this trick does not involve increased gas usage.
Then the variable `base2` would be assigned the slot that `child` had in the previous version. A workaround for this is to declare unused variables on base contracts that you may want to extend in the future, as a means of "reserving" those slots. Note that this trick does not involve increased gas usage.
Loading