-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
2,687 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
|
||
/** @type import('solidity-docgen/dist/config').UserConfig */ | ||
module.exports = { | ||
outputDir: 'docs/modules/api/pages', | ||
templates: 'docs/templates', | ||
exclude: ['internal'], | ||
pageExtension: '.adoc', | ||
pages: (_, file, config) => { | ||
// For each contract file, find the closest README.adoc and return its location as the output page path. | ||
const sourcesDir = path.resolve(config.root, config.sourcesDir); | ||
let dir = path.resolve(config.root, file.absolutePath); | ||
while (dir.startsWith(sourcesDir)) { | ||
dir = path.dirname(dir); | ||
if (fs.existsSync(path.join(dir, 'README.adoc'))) { | ||
return path.relative(sourcesDir, dir) + config.pageExtension; | ||
} | ||
} | ||
}, | ||
}; |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
= OpenZeppelin Defender integration | ||
|
||
OpenZeppelin Foundry Upgrades can be used for performing deployments through https://docs.openzeppelin.com/defender/[OpenZeppelin Defender], which allows for features such as gas pricing estimation, resubmissions, and automated bytecode and source code verification. | ||
|
||
WARNING: Defender deployments are **always** broadcast to a live network, regardless of whether you are using the `broadcast` cheatcode. | ||
The recommended pattern is to separate Defender scripts from scripts that rely on network forking and simulations, to avoid mixing simulation and live network data. | ||
|
||
== Installation | ||
|
||
See xref:foundry-upgrades#installion[Using with Foundry - Installation]. | ||
|
||
== Prerequisites | ||
1. Install https://nodejs.org/[Node.js]. | ||
|
||
2. Configure your `foundry.toml` to include build info and storage layout: | ||
[source,toml] | ||
---- | ||
[profile.default] | ||
build_info = true | ||
extra_output = ["storageLayout"] | ||
---- | ||
|
||
NOTE: Metadata must also be included in the compiler output, which it is by default. | ||
|
||
3. Include `--ffi` in your `forge script` or `forge test` command. | ||
|
||
4. Set the following environment variables in your `.env` file at your project root, using your Team API key and secret from OpenZeppelin Defender: | ||
[source] | ||
---- | ||
DEFENDER_KEY=<Your API key> | ||
DEFENDER_SECRET<Your API secret> | ||
---- | ||
|
||
== Usage | ||
|
||
=== Upgradeable Contracts | ||
|
||
If you are deploying upgradeable contracts, use the `Upgrades` library as described in xref:foundry-upgrades#installion[Using with Foundry - Installation] but set the option `defender.useDefenderDeploy = true` when calling functions to cause all deployments to occur through OpenZeppelin Defender. | ||
|
||
**Example 1 - Deploying a proxy**: | ||
To deploy a UUPS proxy, create a script called `Defender.s.sol` like the following: | ||
[source,solidity] | ||
---- | ||
pragma solidity ^0.8.20; | ||
import {Script} from "forge-std/Script.sol"; | ||
import {console} from "forge-std/console.sol"; | ||
import {Defender, ApprovalProcessResponse} from "openzeppelin-foundry-upgrades/Defender.sol"; | ||
import {Upgrades, Options} from "openzeppelin-foundry-upgrades/Upgrades.sol"; | ||
import {MyContract} from "../src/MyContract.sol"; | ||
contract DefenderScript is Script { | ||
function setUp() public {} | ||
function run() public { | ||
ApprovalProcessResponse memory upgradeApprovalProcess = Defender.getUpgradeApprovalProcess(); | ||
if (upgradeApprovalProcess.via == address(0)) { | ||
revert(string.concat("Upgrade approval process with id ", upgradeApprovalProcess.approvalProcessId, " has no assigned address")); | ||
} | ||
Options memory opts; | ||
opts.defender.useDefenderDeploy = true; | ||
address proxy = Upgrades.deployUUPSProxy( | ||
"MyContract.sol", | ||
abi.encodeCall(MyContract.initialize, ("Hello World", upgradeApprovalProcess.via)), | ||
opts | ||
); | ||
console.log("Deployed proxy to address", proxy); | ||
} | ||
} | ||
---- | ||
|
||
Then run the following command: | ||
[source,console] | ||
---- | ||
forge script <path to the script you created above> --ffi --rpc-url <RPC URL for the network you want to use> | ||
---- | ||
|
||
The above example assumes the implementation contract takes an initial owner address as an argument for its `initialize` function. The script retrieves the address associated with the upgrade approval process configured in Defender (such as a multisig address), and uses that address as the initial owner so that it can have upgrade rights for the proxy. | ||
|
||
This example calls the `Upgrades.deployUUPSProxy` function with the `defender.useDefenderDeploy` option to deploy both the implementation contract and a UUPS proxy to the connected network using Defender. The function waits for the deployments to complete, which may take a few minutes per contract, then returns with the deployed proxy address. While the function is waiting, you can monitor your deployment status in OpenZeppelin Defender's https://defender.openzeppelin.com/v2/#/deploy[Deploy module]. | ||
|
||
NOTE: If using an EOA or Safe to deploy, you must submit the pending deployments in Defender while the script is running. The script waits for each deployment to complete before it continues. | ||
|
||
**Example 2 - Proposing an upgrade to a proxy**: | ||
To propose an upgrade through Defender, create a script like the following: | ||
[source,solidity] | ||
---- | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
import {Script} from "forge-std/Script.sol"; | ||
import {console} from "forge-std/console.sol"; | ||
import {MyContractV2} from "../src/MyContractV2.sol"; | ||
import {ProposeUpgradeResponse, Defender, Options} from "openzeppelin-foundry-upgrades/Defender.sol"; | ||
contract DefenderScript is Script { | ||
function setUp() public {} | ||
function run() public { | ||
Options memory opts; | ||
ProposeUpgradeResponse memory response = Defender.proposeUpgrade( | ||
<MY_PROXY_ADDRESS>, | ||
"MyContractV2.sol", | ||
opts | ||
); | ||
console.log("Proposal id", response.proposalId); | ||
console.log("Url", response.url); | ||
} | ||
} | ||
---- | ||
|
||
Then run the script as in Example 1, and go the resulting URL to review and approve the upgrade proposal. | ||
|
||
=== Non-Upgradeable Contracts | ||
|
||
If you are deploying non-upgradeable contracts, import the `Defender` library from `Defender.sol` and use its functions to deploy contracts through OpenZeppelin Defender. | ||
|
||
**Example:** | ||
|
||
To deploy a non-upgradeable contract, create a script called `Defender.s.sol` like the following: | ||
[source,solidity] | ||
---- | ||
pragma solidity ^0.8.20; | ||
import {Script} from "forge-std/Script.sol"; | ||
import {console} from "forge-std/console.sol"; | ||
import {Defender} from "openzeppelin-foundry-upgrades/Defender.sol"; | ||
contract DefenderScript is Script { | ||
function setUp() public {} | ||
function run() public { | ||
address deployed = Defender.deployContract("MyContract.sol", abi.encode("arguments for the constructor")); | ||
console.log("Deployed contract to address", deployed); | ||
} | ||
} | ||
---- | ||
|
||
Then run the following command: | ||
[source,console] | ||
---- | ||
forge script <path to the script you created above> --ffi --rpc-url <RPC URL for the network you want to use> | ||
---- | ||
|
||
The above example calls the `Defender.deployContract` function to deploy the specified contract to the connected network using Defender. The function waits for the deployment to complete, which may take a few minutes, then returns with the deployed contract address. While the function is waiting, you can monitor your deployment status in OpenZeppelin Defender's https://defender.openzeppelin.com/v2/#/deploy[Deploy module]. | ||
|
||
NOTE: If using an EOA or Safe to deploy, you must submit the pending deployment in Defender while the script is running. The script waits for the deployment to complete before it continues. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
= Using with Foundry | ||
|
||
Foundry library for deploying and managing upgradeable contracts, which includes upgrade safety checks. | ||
|
||
== Installation | ||
|
||
Run these commands: | ||
[source,console] | ||
---- | ||
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: | ||
[source] | ||
---- | ||
@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/ | ||
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ | ||
---- | ||
|
||
NOTE: 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 https://gitforwindows.org/[Git for Windows], add the following line in the `.env` file of your project (using forward slashes): | ||
[source] | ||
---- | ||
OPENZEPPELIN_BASH_PATH="C:/Program Files/Git/bin/bash" | ||
---- | ||
|
||
== Version Limitations | ||
|
||
This library currently only supports proxy contracts and upgrade interfaces from OpenZeppelin Contracts versions 5.0 or higher. | ||
|
||
== Before Running | ||
|
||
This library uses the https://docs.openzeppelin.com/upgrades-plugins/1.x/api-core[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 https://nodejs.org/[Node.js]. | ||
|
||
2. Configure your `foundry.toml` to include build info and storage layout: | ||
[source,toml] | ||
---- | ||
[profile.default] | ||
build_info = true | ||
extra_output = ["storageLayout"] | ||
---- | ||
|
||
3. 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 https://docs.openzeppelin.com/upgrades-plugins/1.x/api-core#define-reference-contracts[Define Reference Contracts] or specify the `referenceContract` option when calling the library's functions. | ||
|
||
4. Run `forge clean` before running your Foundry script or tests. | ||
|
||
5. Include `--ffi` in your `forge script` or `forge test` command. | ||
|
||
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. | ||
|
||
== Usage | ||
|
||
Import the library in your Foundry scripts or tests: | ||
[source,solidity] | ||
---- | ||
import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; | ||
---- | ||
|
||
Then call functions from `Upgrades.sol` to run validations, deployments, or upgrades. | ||
|
||
=== Examples | ||
|
||
Deploy a UUPS proxy: | ||
[source,solidity] | ||
---- | ||
address proxy = Upgrades.deployUUPSProxy( | ||
"MyContract.sol", | ||
abi.encodeCall(MyContract.initialize, ("arguments for the initialize function")) | ||
); | ||
---- | ||
|
||
Deploy a transparent proxy: | ||
[source,solidity] | ||
---- | ||
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: | ||
[source,solidity] | ||
---- | ||
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: | ||
[source,solidity] | ||
---- | ||
Upgrades.upgradeProxy( | ||
transparentProxy, | ||
"MyContractV2.sol", | ||
abi.encodeCall(MyContractV2.foo, ("arguments for foo")) | ||
); | ||
---- | ||
|
||
Upgrade a transparent or UUPS proxy without calling any additional function: | ||
[source,solidity] | ||
---- | ||
Upgrades.upgradeProxy( | ||
transparentProxy, | ||
"MyContractV2.sol", | ||
"" | ||
); | ||
---- | ||
|
||
WARNING: 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: | ||
[source,solidity] | ||
---- | ||
Options memory opts; | ||
opts.referenceContract = "MyContractV1.sol"; | ||
Upgrades.upgradeProxy(proxy, "MyContractV2.sol", "", opts); | ||
// or Upgrades.upgradeBeacon(beacon, "MyContractV2.sol", opts); | ||
---- | ||
|
||
Deploy an upgradeable beacon: | ||
[source,solidity] | ||
---- | ||
address beacon = Upgrades.deployBeacon("MyContract.sol", INITIAL_OWNER_ADDRESS_FOR_BEACON); | ||
---- | ||
|
||
Deploy a beacon proxy: | ||
[source,solidity] | ||
---- | ||
address proxy = Upgrades.deployBeaconProxy( | ||
beacon, | ||
abi.encodeCall(MyContract.initialize, ("arguments for the initialize function")) | ||
); | ||
---- | ||
|
||
Upgrade a beacon: | ||
[source,solidity] | ||
---- | ||
Upgrades.upgradeBeacon(beacon, "MyContractV2.sol"); | ||
---- | ||
|
||
=== Deploying and Verifying | ||
|
||
Run your script with `forge script` to broadcast and deploy. See Foundry's https://book.getfoundry.sh/tutorials/solidity-scripting[Solidity Scripting] guide. | ||
|
||
IMPORTANT: 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. | ||
|
||
NOTE: 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 xref:api-foundry-upgrades.adoc[Foundry Upgrades API] for the full API documentation. |
Oops, something went wrong.