Using with Foundry

Foundry library for deploying and managing upgradeable contracts, which includes upgrade safety checks.

Installation

Run these commands:

forge install foundry-rs/forge-std
forge install OpenZeppelin/openzeppelin-foundry-upgrades
forge install OpenZeppelin/openzeppelin-contracts-upgradeable

Set the following in remappings.txt, replacing any previous definitions of these remappings:

@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
The above remappings mean that both @openzeppelin/contracts/ (including proxy contracts deployed by this library) and @openzeppelin/contracts-upgradeable/ come from your installation of the openzeppelin-contracts-upgradeable submodule and its subdirectories, which includes its own transitive copy of openzeppelin-contracts of the same release version number. This format is needed for Etherscan verification to work. Particularly, any copies of openzeppelin-contracts that you install separately are NOT used.

Windows installations

If you are using Windows, set the OPENZEPPELIN_BASH_PATH environment variable to the fully qualified path of the bash executable. For example, if you are using Git for Windows, add the following line in the .env file of your project (using forward slashes):

OPENZEPPELIN_BASH_PATH="C:/Program Files/Git/bin/bash"

Version Limitations

This library requires forge-std version 1.8.0 or higher.

This library currently only supports proxy contracts and upgrade interfaces from OpenZeppelin Contracts versions 5.0 or higher.

Before Running

This library uses the OpenZeppelin Upgrades CLI for upgrade safety checks, which are run by default during deployments and upgrades.

If you want to be able to run upgrade safety checks, the following are needed:

  1. Install Node.js.

  2. Configure your foundry.toml to enable ffi, ast, build info and storage layout:

[profile.default]
ffi = true
ast = true
build_info = true
extra_output = ["storageLayout"]
  1. If you are upgrading your contract from a previous version, add the @custom:oz-upgrades-from <reference> annotation to the new version of your contract according to Define Reference Contracts or specify the referenceContract option when calling the library’s functions.

  2. Run forge clean before running your Foundry script or tests, or include the --force option when running forge script or forge test.

If you do not want to run upgrade safety checks, you can skip the above steps and use the unsafeSkipAllChecks option when calling the library’s functions. Note that this is a dangerous option meant to be used as a last resort.

Output directory configuration

If your foundry.toml uses a non-default output directory, set the FOUNDRY_OUT environment variable to match your output directory. For example, if foundry.toml has:

[profile.default]
out = "my-output-dir"

Then set the following in the .env file of your project:

FOUNDRY_OUT=my-output-dir

Usage

Import the library in your Foundry scripts or tests:

import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol";

Also import the implementation contract that you want to validate, deploy, or upgrade to, for example:

import {MyToken} from "src/MyToken.sol";

Then call functions from Upgrades.sol to run validations, deployments, or upgrades.

Examples

Deploy a UUPS proxy:

address proxy = Upgrades.deployUUPSProxy(
    "MyContract.sol",
    abi.encodeCall(MyContract.initialize, ("arguments for the initialize function"))
);

Deploy a transparent proxy:

address proxy = Upgrades.deployTransparentProxy(
    "MyContract.sol",
    INITIAL_OWNER_ADDRESS_FOR_PROXY_ADMIN,
    abi.encodeCall(MyContract.initialize, ("arguments for the initialize function"))
);

Call your contract’s functions as normal, but remember to always use the proxy address:

MyContract instance = MyContract(proxy);
instance.myFunction();

Upgrade a transparent or UUPS proxy and call an arbitrary function (such as a reinitializer) during the upgrade process:

Upgrades.upgradeProxy(
    transparentProxy,
    "MyContractV2.sol",
    abi.encodeCall(MyContractV2.foo, ("arguments for foo"))
);

Upgrade a transparent or UUPS proxy without calling any additional function:

Upgrades.upgradeProxy(
    transparentProxy,
    "MyContractV2.sol",
    ""
);
When upgrading a proxy or beacon, ensure that the new contract either has its @custom:oz-upgrades-from <reference> annotation set to the current implementation contract used by the proxy or beacon, or set it with the referenceContract option, for example:
Options memory opts;
opts.referenceContract = "MyContractV1.sol";
Upgrades.upgradeProxy(proxy, "MyContractV2.sol", "", opts);
// or Upgrades.upgradeBeacon(beacon, "MyContractV2.sol", opts);

Deploy an upgradeable beacon:

address beacon = Upgrades.deployBeacon("MyContract.sol", INITIAL_OWNER_ADDRESS_FOR_BEACON);

Deploy a beacon proxy:

address proxy = Upgrades.deployBeaconProxy(
    beacon,
    abi.encodeCall(MyContract.initialize, ("arguments for the initialize function"))
);

Upgrade a beacon:

Upgrades.upgradeBeacon(beacon, "MyContractV2.sol");

Deploying and Verifying

Run your script with forge script to broadcast and deploy. See Foundry’s Solidity Scripting guide.

Include the --sender <ADDRESS> flag for the forge script command when performing upgrades, specifying an address that owns the proxy or proxy admin. Otherwise, OwnableUnauthorizedAccount errors will occur.
Include the --verify flag for the forge script command if you want to verify source code such as on Etherscan. This will verify your implementation contracts along with any proxy contracts as part of the deployment.

API

See Foundry Upgrades API for the full API documentation.