Skip to content

Commit

Permalink
Merge branch 'master' into initializers
Browse files Browse the repository at this point in the history
  • Loading branch information
ericglau authored Dec 10, 2024
2 parents 92e4c2c + 7bdb777 commit 2137d40
Show file tree
Hide file tree
Showing 24 changed files with 358 additions and 212 deletions.
8 changes: 8 additions & 0 deletions docs/modules/ROOT/pages/api-hardhat-upgrades.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ The following options are common to some functions.
* `relayerId`: (`string`) When using OpenZeppelin Defender deployments, the ID of the relayer to use for the deployment. Defaults to the relayer configured for your deployment environment on Defender.
* `salt`: (`string`) When using OpenZeppelin Defender deployments, if this is not set, deployments will be performed using the CREATE opcode. If this is set, deployments will be performed using the CREATE2 opcode with the provided salt. Note that deployments using a Safe are done using CREATE2 and require a salt. **Warning:** CREATE2 affects `msg.sender` behavior. See https://docs.openzeppelin.com/defender/tutorial/deploy#deploy-caveat[Caveats] for more information.
* `metadata`: (`{ commitHash?: string; tag?: string; [k: string]: any; }`) When using OpenZeppelin Defender deployments, you can use this to identify, tag, or classify deployments. See https://docs.openzeppelin.com/defender/module/deploy#metadata[Metadata].
* `proxyFactory`: (`ethers.ContractFactory`) Customizes the ethers contract factory to use for deploying the proxy, allowing a custom proxy contract to be deployed. See https://github.com/OpenZeppelin/openzeppelin-upgrades/blob/master/packages/plugin-hardhat/src/utils/factories.ts[factories.ts] for the default contract factory for each kind of proxy.
** *Since:* `@openzeppelin/hardhat-upgrades@3.7.0`
* `deployFunction`: (`(hre, opts, factory, ...args) => Promise<EthersOrDefenderDeployment>`) Customizes the function used to deploy the proxy. Can be used along with the `proxyFactory` option to override constructor parameters for custom proxy deployments. See https://github.com/OpenZeppelin/openzeppelin-upgrades/blob/master/packages/plugin-hardhat/src/utils/deploy.ts[deploy.ts] for the default deploy function.
** *Since:* `@openzeppelin/hardhat-upgrades@3.7.0`

Note that the options `unsafeAllow` can also be specified in a more granular way directly in the source code if using Solidity >=0.8.2. See xref:faq.adoc#how-can-i-disable-checks[How can I disable some of the checks?]

Expand Down Expand Up @@ -69,6 +73,8 @@ async function deployProxy(
txOverrides?: ethers.Overrides,
kind?: 'uups' | 'transparent',
useDefenderDeploy?: boolean,
proxyFactory?: ethers.ContractFactory,
deployFunction?: () => Promise<EthersOrDefenderDeployment>,
},
): Promise<ethers.Contract>
----
Expand Down Expand Up @@ -212,6 +218,8 @@ async function deployBeaconProxy(
initializer?: string | false,
txOverrides?: ethers.Overrides,
useDefenderDeploy?: boolean,
proxyFactory?: ethers.ContractFactory,
deployFunction?: () => Promise<EthersOrDefenderDeployment>,
},
): Promise<ethers.Contract>
----
Expand Down
28 changes: 28 additions & 0 deletions docs/modules/ROOT/pages/foundry/pages/foundry-upgrades.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,34 @@ Set the following in `remappings.txt`:

NOTE: Use `LegacyUpgrades.sol` instead of `Upgrades.sol` to upgrade existing deployments that were created with OpenZeppelin Contracts v4.

=== Optional: Alternative installation methods

==== NPM

Follow the steps above, but instead of running `forge install OpenZeppelin/openzeppelin-foundry-upgrades`, use this command instead:
[source,console]
----
npm install @openzeppelin/foundry-upgrades
----

Then add the following additional lines to `remappings.txt`, in addition to the ones described above:
[source,console]
----
openzeppelin-foundry-upgrades/=node_modules/@openzeppelin/foundry-upgrades/src/
solidity-stringutils/=node_modules/@openzeppelin/foundry-upgrades/lib/solidity-stringutils/
----

==== Soldeer

Follow the steps above, but instead of running `forge install OpenZeppelin/openzeppelin-foundry-upgrades`, use one of the install commands described in https://soldeer.xyz/project/openzeppelin-foundry-upgrades

Then add the following additional lines to `remappings.txt`, in addition to the ones described above (replace `0.3.6` with the version of the plugin that you installed):
[source,console]
----
openzeppelin-foundry-upgrades/=dependencies/openzeppelin-foundry-upgrades-0.3.6/src/
solidity-stringutils/=dependencies/openzeppelin-foundry-upgrades-0.3.6/lib/solidity-stringutils/
----

== Foundry Requirements

This library requires https://github.com/foundry-rs/forge-std[forge-std] version 1.8.0 or higher.
Expand Down
5 changes: 5 additions & 0 deletions packages/core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

- Detect issues in parent initializer calls. ([#1095](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1095))

## 1.41.0 (2024-11-25)

- Update Slang dependency to 0.18.3. ([#1102](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1102))
- Improves reliability of Hardhat compilation step for namespaced storage layout validations when using Solidity versions 0.8.27 and 0.8.28.

## 1.40.0 (2024-10-10)

- Fix Hardhat compile error when overriding interface functions with public constant variables. ([#1091](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1091))
Expand Down
4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openzeppelin/upgrades-core",
"version": "1.40.0",
"version": "1.41.0",
"description": "",
"repository": "https://github.com/OpenZeppelin/openzeppelin-upgrades/tree/master/packages/core",
"license": "MIT",
Expand Down Expand Up @@ -61,7 +61,7 @@
"typescript": "^5.0.0"
},
"dependencies": {
"@nomicfoundation/slang": "^0.17.0",
"@nomicfoundation/slang": "^0.18.3",
"cbor": "^9.0.0",
"chalk": "^4.1.0",
"compare-versions": "^6.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export {
export { ValidateUpgradeSafetyOptions, validateUpgradeSafety, ProjectReport, ReferenceContractNotFound } from './cli';

export { getUpgradeInterfaceVersion } from './upgrade-interface-version';
export { makeNamespacedInput } from './utils/make-namespaced';
export { makeNamespacedInput, trySanitizeNatSpec } from './utils/make-namespaced';
export { isNamespaceSupported } from './storage/namespace';
export { inferProxyAdmin } from './infer-proxy-admin';
export { assertUnreachable } from './utils/assert';
11 changes: 5 additions & 6 deletions packages/core/src/utils/make-namespaced.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
TASK_COMPILE_SOLIDITY_RUN_SOLCJS,
} from 'hardhat/builtin-tasks/task-names';

import { makeNamespacedInput } from './make-namespaced';
import { makeNamespacedInput, trySanitizeNatSpec } from './make-namespaced';
import { SolcBuild } from 'hardhat/types/builtin-tasks';
import { SolcInput, SolcOutput } from '../solc-api';
import { BuildInfo } from 'hardhat/types';
Expand Down Expand Up @@ -40,11 +40,10 @@ async function testMakeNamespaced(
// Inefficient, but we want to test that we don't actually modify the original input object
const origInput = JSON.parse(JSON.stringify(origBuildInfo.input));

const modifiedInput = makeNamespacedInput(
origBuildInfo.input,
origBuildInfo.output,
keepAllNatSpec ? undefined : origBuildInfo.solcVersion,
);
let modifiedInput = makeNamespacedInput(origBuildInfo.input, origBuildInfo.output, origBuildInfo.solcVersion);
if (!keepAllNatSpec) {
modifiedInput = await trySanitizeNatSpec(modifiedInput, origBuildInfo.solcVersion);
}

// Run hardhat compile on the modified input and make sure it has no errors
const modifiedOutput = await hardhatCompile(modifiedInput, solcVersion);
Expand Down
105 changes: 54 additions & 51 deletions packages/core/src/utils/make-namespaced.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ const OUTPUT_SELECTION = {
*
* @param input The original solc input.
* @param output The original solc output.
* @param solcVersion The version of the solc compiler that was originally used to compile the input.
* @param _solcVersion The version of the solc compiler that was originally used to compile the input. This argument is no longer used and is kept for backwards compatibility.
* @returns The modified solc input with storage layout that includes namespaced type information.
*/
export function makeNamespacedInput(input: SolcInput, output: SolcOutput, solcVersion?: string): SolcInput {
export function makeNamespacedInput(input: SolcInput, output: SolcOutput, _solcVersion?: string): SolcInput {
const modifiedSources: Record<string, { content?: string }> = {};

for (const [sourcePath] of Object.entries(input.sources)) {
Expand Down Expand Up @@ -163,74 +163,77 @@ export function makeNamespacedInput(input: SolcInput, output: SolcOutput, solcVe
}
}

const modifiedSource = tryRemoveNonStructNatSpec(getModifiedSource(orig, modifications), solcVersion);

modifiedSources[sourcePath] = { ...source, content: modifiedSource };
modifiedSources[sourcePath] = { ...source, content: getModifiedSource(orig, modifications) };
}

return { ...input, sources: modifiedSources, settings: { ...input.settings, outputSelection: OUTPUT_SELECTION } };
}

/**
* If we have the compiler version available and Slang is supported for the current platform and compiler version,
* use Slang to parse and remove all NatSpec comments that do not precede a struct definition and return the modified content.
* Attempts to remove all NatSpec comments that do not precede a struct definition from the input source contents.
* Directly modifies the input source contents, and also returns the modified input.
*
* If the solc version is not supported by the parser, the original content is kept.
*
* @param solcInput Solc input.
* @param solcVersion The version of the solc compiler that was originally used to compile the input.
* @returns The modified solc input with NatSpec comments removed where they do not precede a struct definition.
*/
export async function trySanitizeNatSpec(solcInput: SolcInput, solcVersion: string): Promise<SolcInput> {
for (const [sourcePath, source] of Object.entries(solcInput.sources)) {
if (source.content !== undefined) {
solcInput.sources[sourcePath].content = await tryRemoveNonStructNatSpec(source.content, solcVersion);
}
}
return solcInput;
}

/**
* If Slang is supported for the current compiler version, use Slang to parse and remove all NatSpec comments
* that do not precede a struct definition and return the modified content.
*
* Otherwise, return the original content.
*/
function tryRemoveNonStructNatSpec(origContent: string, solcVersion: string | undefined): string {
const natSpecRemovals: Modification[] = [];
async function tryRemoveNonStructNatSpec(origContent: string, solcVersion: string): Promise<string> {
if (solcVersion === undefined) {
return origContent;
}

if (solcVersion !== undefined && tryRequire('@nomicfoundation/slang') && slangSupportsVersion(solcVersion)) {
/* eslint-disable @typescript-eslint/no-var-requires */
const { Language } = require('@nomicfoundation/slang/language');
const { NonterminalKind, TerminalKind } = require('@nomicfoundation/slang/kinds');
const { TerminalNode } = require('@nomicfoundation/slang/cst');
const { isTrivia } = require('./slang/trivia');
/* eslint-enable @typescript-eslint/no-var-requires */
const { Parser } = await import('@nomicfoundation/slang/parser');
if (!Parser.supportedVersions().includes(solcVersion)) {
return origContent;
}

const { TerminalKind, TerminalKindExtensions } = await import('@nomicfoundation/slang/cst');

const language = new Language(solcVersion);
const parseOutput = language.parse(NonterminalKind.SourceUnit, origContent);
const cursor = parseOutput.createTreeCursor();
const parser = Parser.create(solcVersion);
const parseOutput = parser.parse(Parser.rootKind(), origContent);
const cursor = parseOutput.createTreeCursor();

const natSpecRemovals: Modification[] = [];

while (
cursor.goToNextTerminalWithKinds([TerminalKind.MultiLineNatSpecComment, TerminalKind.SingleLineNatSpecComment])
) {
// Lookahead to determine if the next non-trivia node is the struct keyword.
// If so, this NatSpec is part of the struct definition and should not be removed.
const lookaheadCursor = cursor.clone();
while (
cursor.goToNextTerminalWithKinds([TerminalKind.MultiLineNatSpecComment, TerminalKind.SingleLineNatSpecComment])
lookaheadCursor.goToNextTerminal() &&
lookaheadCursor.node.isTerminalNode() &&
TerminalKindExtensions.isTrivia(lookaheadCursor.node.kind)
) {
// Lookahead to determine if the next non-trivia node is the struct keyword.
// If so, this NatSpec is part of the struct definition and should not be removed.
const lookaheadCursor = cursor.clone();
while (lookaheadCursor.goToNextTerminal() && isTrivia(lookaheadCursor.node())) {
// skip over trivia nodes
}
if (lookaheadCursor.node().kind === TerminalKind.StructKeyword) {
continue;
}
// skip over trivia nodes
}

const triviaNode = cursor.node();
assert(triviaNode instanceof TerminalNode);
natSpecRemovals.push(makeDeleteRange(cursor.textRange.start.utf8, cursor.textRange.end.utf8));
if (lookaheadCursor.node.kind === TerminalKind.StructKeyword) {
continue;
}
return getModifiedSource(Buffer.from(origContent, 'utf8'), natSpecRemovals);
} else {
return origContent;
}
}

function tryRequire(id: string) {
try {
require(id);
return true;
} catch (e: any) {
// do nothing
natSpecRemovals.push(makeDeleteRange(cursor.textRange.start.utf8, cursor.textRange.end.utf8));
}
return false;
}

function slangSupportsVersion(solcVersion: string): boolean {
/* eslint-disable @typescript-eslint/no-var-requires */
const { Language } = require('@nomicfoundation/slang/language');
/* eslint-enable @typescript-eslint/no-var-requires */

return Language.supportedVersions().includes(solcVersion);
return getModifiedSource(Buffer.from(origContent, 'utf8'), natSpecRemovals);
}

interface Modification {
Expand Down
23 changes: 0 additions & 23 deletions packages/core/src/utils/slang/trivia.ts

This file was deleted.

9 changes: 9 additions & 0 deletions packages/plugin-hardhat/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 3.7.0 (2024-12-04)

- Add `proxyFactory` and `deployFunction` options which can be used to deploy a custom proxy contract. ([#1104](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1104))

## 3.6.0 (2024-11-25)

- Update Slang dependency to 0.18.3. ([#1102](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1102))
- Improves reliability of Hardhat compilation step for namespaced storage layout validations when using Solidity versions 0.8.27 and 0.8.28.

## 3.5.0 (2024-10-10)

- Support ignoring Hardhat compile errors when extracting detailed namespaced storage layouts for validations. ([#1090](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1090))
Expand Down
30 changes: 30 additions & 0 deletions packages/plugin-hardhat/contracts/AccessManagedProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Example of a custom proxy.
// This contract is for testing only.

import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {AccessManager} from "@openzeppelin/contracts/access/manager/AccessManager.sol";
import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol";
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";

contract AccessManagedProxy is ERC1967Proxy {
IAccessManager public immutable ACCESS_MANAGER;

constructor(address implementation, bytes memory _data, IAccessManager manager) payable ERC1967Proxy(implementation, _data) {
ACCESS_MANAGER = manager;
}

/**
* @dev Checks with the ACCESS_MANAGER if msg.sender is authorized to call the current call's function,
* and if so, delegates the current call to `implementation`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual override {
(bool immediate, ) = ACCESS_MANAGER.canCall(msg.sender, address(this), bytes4(msg.data[0:4]));
if (!immediate) revert IAccessManaged.AccessManagedUnauthorized(msg.sender);
super._delegate(implementation);
}
}
24 changes: 24 additions & 0 deletions packages/plugin-hardhat/contracts/CustomBeaconProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";

// Example of a custom beacon proxy.
// This contract is for testing only, it is not safe for use in production.

contract CustomBeaconProxy is BeaconProxy {
address private immutable _deployer;
// The beacon that will be used on calls by the deployer address
address private immutable _deployerBeacon;

constructor(address beacon, bytes memory data, address deployerBeacon) payable BeaconProxy(beacon, data) {
_deployer = msg.sender;
_deployerBeacon = deployerBeacon;
}

function _getBeacon() internal view override returns (address) {
if (msg.sender == _deployer) return _deployerBeacon;
return super._getBeacon();
}
}
4 changes: 2 additions & 2 deletions packages/plugin-hardhat/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openzeppelin/hardhat-upgrades",
"version": "3.5.0",
"version": "3.7.0",
"description": "",
"repository": "https://github.com/OpenZeppelin/openzeppelin-upgrades/tree/master/packages/plugin-hardhat",
"license": "MIT",
Expand Down Expand Up @@ -38,7 +38,7 @@
"@openzeppelin/defender-sdk-base-client": "^1.14.4",
"@openzeppelin/defender-sdk-deploy-client": "^1.14.4",
"@openzeppelin/defender-sdk-network-client": "^1.14.4",
"@openzeppelin/upgrades-core": "^1.40.0",
"@openzeppelin/upgrades-core": "^1.41.0",
"chalk": "^4.1.0",
"debug": "^4.1.1",
"ethereumjs-util": "^7.1.5",
Expand Down
Loading

0 comments on commit 2137d40

Please sign in to comment.