├── .changeset └── config.json ├── .codecov.yml ├── .eslintrc.js ├── .gitattributes ├── .github ├── actions │ └── setup │ │ └── action.yml └── workflows │ ├── changeset.yml │ ├── checks.yml │ ├── publish.yml │ └── version.yml ├── .gitignore ├── .gitmodules ├── .node-version ├── .nycrc ├── .prettierrc.js ├── CONTRIBUTING.md ├── FUNDING.json ├── LICENSE ├── README.md ├── assets ├── banner.svg └── hardhat.svg ├── docs ├── antora.yml └── modules │ └── ROOT │ ├── nav.adoc │ └── pages │ ├── api-core.adoc │ ├── api-foundry-upgrades.adoc │ ├── api-hardhat-upgrades.adoc │ ├── api-truffle-upgrades.adoc │ ├── defender-deploy.adoc │ ├── faq.adoc │ ├── foundry-defender.adoc │ ├── foundry-upgrades.adoc │ ├── foundry │ ├── api │ │ └── pages │ │ │ ├── Defender.adoc │ │ │ ├── LegacyUpgrades.adoc │ │ │ ├── Options.adoc │ │ │ ├── Upgrades.adoc │ │ │ └── api-foundry-upgrades.adoc │ └── pages │ │ ├── foundry-defender.adoc │ │ └── foundry-upgrades.adoc │ ├── hardhat-upgrades.adoc │ ├── index.adoc │ ├── migrate-from-cli.adoc │ ├── network-files.adoc │ ├── proxies.adoc │ ├── truffle-upgrades.adoc │ └── writing-upgradeable.adoc ├── netlify.toml ├── package.json ├── packages ├── core │ ├── CHANGELOG.md │ ├── ava.config.js │ ├── contracts │ │ ├── Initializable.sol │ │ ├── import.sol │ │ └── test │ │ │ ├── CustomLayout.sol │ │ │ ├── FunctionSignatures.sol │ │ │ ├── ManifestMigrate.sol │ │ │ ├── Memory05.sol │ │ │ ├── Memory08.sol │ │ │ ├── Namespaced.sol │ │ │ ├── NamespacedConflicts.sol │ │ │ ├── NamespacedConflictsLayout.sol │ │ │ ├── NamespacedInLibrary.sol │ │ │ ├── NamespacedLayout.sol │ │ │ ├── NamespacedOutsideContract.sol │ │ │ ├── NamespacedToModify.sol │ │ │ ├── NamespacedToModify07.sol │ │ │ ├── NamespacedToModifyCustomLayout.sol │ │ │ ├── NamespacedToModifyImported.sol │ │ │ ├── NamespacedUDVT.sol │ │ │ ├── NamespacedUDVTLayout.sol │ │ │ ├── Proxiable.sol │ │ │ ├── RenamedRetyped.sol │ │ │ ├── RetypeFromContract.sol │ │ │ ├── Standalone.sol │ │ │ ├── Storage.sol │ │ │ ├── Storage088.sol │ │ │ ├── Storage089.sol │ │ │ ├── StorageRenamedRetyped.sol │ │ │ ├── Validations.sol │ │ │ ├── ValidationsFunctionPointers.sol │ │ │ ├── ValidationsImport.sol │ │ │ ├── ValidationsInitializer.sol │ │ │ ├── ValidationsNatspec.sol │ │ │ ├── ValidationsNatspecImport.sol │ │ │ ├── ValidationsSameNameSafe.sol │ │ │ ├── ValidationsSameNameUnsafe.sol │ │ │ ├── ValidationsUDVT.sol │ │ │ ├── Version.sol │ │ │ ├── cli │ │ │ ├── Annotation.sol │ │ │ ├── InvalidAnnotationArgsUpgrades.sol │ │ │ ├── InvalidAnnotationArgsUpgradesFrom.sol │ │ │ ├── SelfReferences.sol │ │ │ ├── Storage088.sol │ │ │ ├── Storage089.sol │ │ │ ├── Validate.sol │ │ │ ├── ValidateBuildInfoV1.sol │ │ │ ├── ValidateBuildInfoV2_Annotation_Bad.sol │ │ │ ├── ValidateBuildInfoV2_Annotation_Ok.sol │ │ │ ├── ValidateBuildInfoV2_Bad.sol │ │ │ ├── ValidateBuildInfoV2_Branch_Bad.sol │ │ │ ├── ValidateBuildInfoV2_Branch_Ok.sol │ │ │ ├── ValidateBuildInfoV2_Ok.sol │ │ │ └── excludes │ │ │ │ ├── Abstract1.sol │ │ │ │ ├── Abstract2.sol │ │ │ │ ├── AbstractUUPS.sol │ │ │ │ ├── ImportVersionsAndBeacon.sol │ │ │ │ ├── UsesAbstractUUPS.sol │ │ │ │ ├── V1.sol │ │ │ │ ├── V2Bad1.sol │ │ │ │ └── V2Bad2.sol │ │ │ └── ignore-errors │ │ │ ├── AllowChild.sol │ │ │ ├── AllowChildCallTransitive.sol │ │ │ ├── AllowChildInheritedTransitive.sol │ │ │ ├── AllowChildSelfReachable.sol │ │ │ ├── AllowParent.sol │ │ │ ├── AllowParentSelfReachable.sol │ │ │ ├── AllowReachable.sol │ │ │ ├── AllowReachableParent.sol │ │ │ ├── AllowReachableParentCall.sol │ │ │ ├── Constructors.sol │ │ │ ├── Modifiers.sol │ │ │ ├── RiskyFreeFunctions.sol │ │ │ ├── RiskyLibrary.sol │ │ │ ├── RiskyParentContract.sol │ │ │ ├── SafeContract.sol │ │ │ ├── SafeContractWithFreeFunctionCall.sol │ │ │ ├── SafeContractWithLibraryCall.sol │ │ │ ├── SafeContractWithLibraryImport.sol │ │ │ ├── SafeContractWithLibraryUsingFor.sol │ │ │ ├── SafeContractWithParentCall.sol │ │ │ ├── SafeContractWithTransitiveLibraryCall.sol │ │ │ ├── SafeRecursion.sol │ │ │ ├── TransitiveAllowReachable.sol │ │ │ ├── TransitiveRiskyLibrary.sol │ │ │ ├── TransitiveUnsafeParent.sol │ │ │ ├── UnsafeAllow.sol │ │ │ ├── UnsafeAllowParent.sol │ │ │ ├── UnsafeAllowReachableDifferentOpcode.sol │ │ │ ├── UnsafeAllowReachableParentDifferentOpcode.sol │ │ │ ├── UnsafeContract.sol │ │ │ ├── UnsafeContractWithFreeFunctionCall.sol │ │ │ ├── UnsafeContractWithInheritedParent.sol │ │ │ ├── UnsafeContractWithInheritedTransitiveParent.sol │ │ │ ├── UnsafeContractWithLibraryCall.sol │ │ │ ├── UnsafeContractWithLibraryUsingFor.sol │ │ │ ├── UnsafeContractWithParentCall.sol │ │ │ ├── UnsafeContractWithTransitiveLibraryCall.sol │ │ │ ├── UnsafeParentContract.sol │ │ │ └── UnsafeRecursion.sol │ ├── hardhat.config.js │ ├── hardhat │ │ └── separate-test-contracts.js │ ├── package.json │ ├── scripts │ │ ├── copy-build-info-v5.js │ │ ├── copy-legacy-artifacts.sh │ │ └── verify.js │ ├── src │ │ ├── add-proxy-to-manifest.ts │ │ ├── beacon.ts │ │ ├── call-optional-signature.ts │ │ ├── cli │ │ │ ├── cli.test.ts │ │ │ ├── cli.test.ts.md │ │ │ ├── cli.test.ts.snap │ │ │ ├── cli.ts │ │ │ ├── index.ts │ │ │ ├── validate.test.ts │ │ │ ├── validate.ts │ │ │ └── validate │ │ │ │ ├── build-info-file.test.ts │ │ │ │ ├── build-info-file.ts │ │ │ │ ├── contract-report.ts │ │ │ │ ├── default-exclude.ts │ │ │ │ ├── error.ts │ │ │ │ ├── find-contract.ts │ │ │ │ ├── project-report.test.ts │ │ │ │ ├── project-report.test.ts.md │ │ │ │ ├── project-report.test.ts.snap │ │ │ │ ├── project-report.ts │ │ │ │ ├── upgradeability-assessment.ts │ │ │ │ ├── validate-upgrade-safety.test.ts │ │ │ │ ├── validate-upgrade-safety.test.ts.md │ │ │ │ ├── validate-upgrade-safety.test.ts.snap │ │ │ │ ├── validate-upgrade-safety.ts │ │ │ │ ├── validations.test.ts │ │ │ │ ├── validations.test.ts.md │ │ │ │ ├── validations.test.ts.snap │ │ │ │ └── validations.ts │ │ ├── deployment.test.ts │ │ ├── deployment.ts │ │ ├── eip-1967-type.ts │ │ ├── eip-1967.ts │ │ ├── error.ts │ │ ├── impl-address.ts │ │ ├── impl-store.test.ts │ │ ├── impl-store.ts │ │ ├── index.ts │ │ ├── infer-proxy-admin.test.ts │ │ ├── infer-proxy-admin.ts │ │ ├── levenshtein.test.ts │ │ ├── levenshtein.ts │ │ ├── link-refs.test.ts │ │ ├── link-refs.ts │ │ ├── manifest-storage-layout.test.ts │ │ ├── manifest-storage-layout.ts │ │ ├── manifest.test.ts │ │ ├── manifest.ts │ │ ├── provider.test.ts │ │ ├── provider.ts │ │ ├── proxy-kind.ts │ │ ├── scripts │ │ │ ├── migratable-manifest.test.json │ │ │ ├── migrate-oz-cli-project.test.ts │ │ │ ├── migrate-oz-cli-project.test.ts.md │ │ │ ├── migrate-oz-cli-project.test.ts.snap │ │ │ ├── migrate-oz-cli-project.ts │ │ │ └── project-file.test.json │ │ ├── solc-api.ts │ │ ├── solidity-version.json │ │ ├── src-decoder.test.ts │ │ ├── src-decoder.ts │ │ ├── standalone.test.ts │ │ ├── standalone.ts │ │ ├── storage-0.8.test.ts │ │ ├── storage-0.8.test.ts.md │ │ ├── storage-0.8.test.ts.snap │ │ ├── storage-custom-layout.test.ts │ │ ├── storage-custom-layout.test.ts.md │ │ ├── storage-custom-layout.test.ts.snap │ │ ├── storage-memory-0.5.test.ts │ │ ├── storage-memory-0.5.test.ts.md │ │ ├── storage-memory-0.5.test.ts.snap │ │ ├── storage-namespaced-conflicts-layout.test.ts │ │ ├── storage-namespaced-conflicts-layout.test.ts.md │ │ ├── storage-namespaced-conflicts-layout.test.ts.snap │ │ ├── storage-namespaced-conflicts.test.ts │ │ ├── storage-namespaced-conflicts.test.ts.md │ │ ├── storage-namespaced-conflicts.test.ts.snap │ │ ├── storage-namespaced-layout.test.ts │ │ ├── storage-namespaced-layout.test.ts.md │ │ ├── storage-namespaced-layout.test.ts.snap │ │ ├── storage-namespaced-outside-contract.test.ts │ │ ├── storage-namespaced-solc-versions.test.ts │ │ ├── storage-namespaced-udvt.test.ts │ │ ├── storage-namespaced.test.ts │ │ ├── storage-namespaced.test.ts.md │ │ ├── storage-namespaced.test.ts.snap │ │ ├── storage.test.ts │ │ ├── storage.test.ts.md │ │ ├── storage.test.ts.snap │ │ ├── storage │ │ │ ├── compare.ts │ │ │ ├── compat.ts │ │ │ ├── extract.ts │ │ │ ├── gap.ts │ │ │ ├── index.ts │ │ │ ├── layout.ts │ │ │ ├── namespace.ts │ │ │ ├── report-gap.test.ts │ │ │ ├── report-gap.test.ts.md │ │ │ ├── report-gap.test.ts.snap │ │ │ ├── report-rename-retype.test.ts │ │ │ ├── report-rename-retype.test.ts.md │ │ │ ├── report-rename-retype.test.ts.snap │ │ │ ├── report-retype-from-contract.test.ts │ │ │ ├── report.test.ts │ │ │ ├── report.test.ts.md │ │ │ ├── report.test.ts.snap │ │ │ └── report.ts │ │ ├── stub-provider.ts │ │ ├── upgrade-interface-version.test.ts │ │ ├── upgrade-interface-version.ts │ │ ├── usage-error.ts │ │ ├── utils │ │ │ ├── address.ts │ │ │ ├── annotations.ts │ │ │ ├── assert.ts │ │ │ ├── contract-name.ts │ │ │ ├── curry.ts │ │ │ ├── debug.ts │ │ │ ├── deep-array.test.ts │ │ │ ├── deep-array.ts │ │ │ ├── erc7201.test.ts │ │ │ ├── erc7201.ts │ │ │ ├── execall.ts │ │ │ ├── function.test.ts │ │ │ ├── function.ts │ │ │ ├── indent.ts │ │ │ ├── integer-literals.test.ts │ │ │ ├── integer-literals.ts │ │ │ ├── is-nullish.test.ts │ │ │ ├── is-nullish.ts │ │ │ ├── is-value-type.ts │ │ │ ├── itemize.ts │ │ │ ├── log.ts │ │ │ ├── make-namespaced.test.ts │ │ │ ├── make-namespaced.test.ts.md │ │ │ ├── make-namespaced.test.ts.snap │ │ │ ├── make-namespaced.ts │ │ │ ├── make-non-enumerable.ts │ │ │ ├── map-values.ts │ │ │ ├── parse-type-id.test.ts │ │ │ ├── parse-type-id.test.ts.md │ │ │ ├── parse-type-id.test.ts.snap │ │ │ ├── parse-type-id.ts │ │ │ ├── pick.ts │ │ │ ├── stabilize-layout.ts │ │ │ └── type-id.ts │ │ ├── validate-ignore-errors.test.ts │ │ ├── validate-initializers.test.ts │ │ ├── validate.test.ts │ │ ├── validate │ │ │ ├── compat.ts │ │ │ ├── data.ts │ │ │ ├── error.ts │ │ │ ├── index.ts │ │ │ ├── overrides.ts │ │ │ ├── query.test.ts │ │ │ ├── query.ts │ │ │ ├── report.ts │ │ │ ├── run.test.ts │ │ │ ├── run.ts │ │ │ └── run │ │ │ │ └── initializer.ts │ │ ├── version.test.ts │ │ └── version.ts │ └── tsconfig.json ├── plugin-defender-hardhat │ ├── CHANGELOG.md │ ├── README.md │ └── assets │ │ ├── approve-upgrade.png │ │ └── verified-deploy.png ├── plugin-hardhat │ ├── CHANGELOG.md │ ├── README.md │ ├── ava.config.js │ ├── contracts │ │ ├── AccessManagedProxy.sol │ │ ├── Action.sol │ │ ├── ActionV2.sol │ │ ├── Adder.sol │ │ ├── AdderV2.sol │ │ ├── Beacon.sol │ │ ├── Constructor.sol │ │ ├── CustomBeaconProxy.sol │ │ ├── CustomProxy.sol │ │ ├── DeployContract.sol │ │ ├── DeployOverload.sol │ │ ├── ExternalLibraries.sol │ │ ├── GapV1.sol │ │ ├── GapV2.sol │ │ ├── Greeter.sol │ │ ├── GreeterDefender.sol │ │ ├── GreeterFallback.sol │ │ ├── GreeterProxiable40Fallback.sol │ │ ├── GreeterProxiable40FallbackString.sol │ │ ├── GreeterTransparent40Fallback.sol │ │ ├── GreeterTransparent40FallbackString.sol │ │ ├── GreeterV2.sol │ │ ├── GreeterV3.sol │ │ ├── HasOwner.sol │ │ ├── Initializers.sol │ │ ├── Invalid.sol │ │ ├── InvalidGreeter.sol │ │ ├── MakeNamespaced0516.sol │ │ ├── MultiPragma.sol │ │ ├── MultipleExternalLibraries.sol │ │ ├── Namespaced.sol │ │ ├── NoLicense.sol │ │ ├── Portfolio.sol │ │ ├── PortfolioV2.sol │ │ ├── Token.sol │ │ ├── TokenV2.sol │ │ ├── TokenV3.sol │ │ ├── Unlicensed.sol │ │ ├── UnrecognizedLicense.sol │ │ ├── WithLicense.sol │ │ └── utils │ │ │ ├── Proxiable.sol │ │ │ └── Proxiable40.sol │ ├── hardhat.config.js │ ├── package.json │ ├── scripts │ │ └── test.sh │ ├── src │ │ ├── admin.ts │ │ ├── defender │ │ │ ├── client.ts │ │ │ ├── deploy.ts │ │ │ ├── get-approval-process.ts │ │ │ ├── propose-upgrade-with-approval.ts │ │ │ └── utils.ts │ │ ├── deploy-beacon-proxy.ts │ │ ├── deploy-beacon.ts │ │ ├── deploy-contract.ts │ │ ├── deploy-implementation.ts │ │ ├── deploy-proxy.ts │ │ ├── force-import.ts │ │ ├── index.ts │ │ ├── prepare-upgrade.ts │ │ ├── scripts │ │ │ └── migrate-oz-cli-project.ts │ │ ├── type-extensions.ts │ │ ├── upgrade-beacon.ts │ │ ├── upgrade-proxy.ts │ │ ├── utils │ │ │ ├── attach-abi.ts │ │ │ ├── contract-instance.ts │ │ │ ├── contract-types.ts │ │ │ ├── debug.ts │ │ │ ├── deploy-impl.ts │ │ │ ├── deploy.ts │ │ │ ├── ethers.ts │ │ │ ├── etherscan-api.ts │ │ │ ├── factories.ts │ │ │ ├── index.ts │ │ │ ├── initial-owner.ts │ │ │ ├── initializer-data.ts │ │ │ ├── is-full-solc-output.ts │ │ │ ├── options.ts │ │ │ ├── simulate-deploy.ts │ │ │ ├── validate-impl.ts │ │ │ └── validations.ts │ │ ├── validate-implementation.ts │ │ ├── validate-upgrade.ts │ │ └── verify-proxy.ts │ ├── test │ │ ├── beacon-000-externally-deployed.js │ │ ├── beacon-001-externally-upgraded.js │ │ ├── beacon-constructor.js │ │ ├── beacon-custom-beacon-proxy.js │ │ ├── beacon-deploy-overload.js │ │ ├── beacon-deploy-validation.js │ │ ├── beacon-happy-path-with-addresses.js │ │ ├── beacon-happy-path-with-enums.js │ │ ├── beacon-happy-path-with-library.js │ │ ├── beacon-happy-path-with-structs.js │ │ ├── beacon-happy-path.js │ │ ├── beacon-initial-owner.js │ │ ├── beacon-initializers.js │ │ ├── beacon-linked-libraries.js │ │ ├── beacon-upgrade-block-proxy.js │ │ ├── beacon-upgrade-storage.js │ │ ├── beacon-upgrade-validation.js │ │ ├── beacon-with-call.js │ │ ├── constructor.js │ │ ├── defender-contract-instance.js │ │ ├── defender-deploy-contract.js │ │ ├── defender-deploy-implementation.js │ │ ├── defender-deploy-proxy.js │ │ ├── defender-deploy.js │ │ ├── defender-get-approval-process.js │ │ ├── defender-hardhat-setup.js │ │ ├── defender-utils.js │ │ ├── implementation-functions.js │ │ ├── import-v4.js │ │ ├── import-v5.js │ │ ├── import-with-deploy-v4.js │ │ ├── import-with-deploy-v5.js │ │ ├── infer-proxy-kind.js │ │ ├── namespaced.js │ │ ├── namespaced.js.md │ │ ├── namespaced.js.snap │ │ ├── prepare-upgrade-txresponse.js │ │ ├── propose-upgrade-with-approval-beacon.js │ │ ├── propose-upgrade-with-approval-transparent.js │ │ ├── propose-upgrade-with-approval-unsafeAllow.js │ │ ├── propose-upgrade-with-approval-use-deployed.js │ │ ├── propose-upgrade-with-approval-uups.js │ │ ├── redeploy-implementation-always.js │ │ ├── redeploy-implementation-never.js │ │ ├── redeploy-implementation-onchange.js │ │ ├── solidity-overrides-storage.js │ │ ├── timeout.js │ │ ├── transparent-admin-initial-owner.js │ │ ├── transparent-admin-unknown-upgrade-interface.js │ │ ├── transparent-deploy-overload.js │ │ ├── transparent-deploy-validation.js │ │ ├── transparent-happy-path-with-call.js │ │ ├── transparent-happy-path-with-enums.js │ │ ├── transparent-happy-path-with-library.js │ │ ├── transparent-happy-path-with-structs.js │ │ ├── transparent-happy-path.js │ │ ├── transparent-initializers.js │ │ ├── transparent-linked-libraries.js │ │ ├── transparent-multi-compiler.js │ │ ├── transparent-transfer-admin-ownership-happy-path.js │ │ ├── transparent-transfer-admin-ownership-signer.js │ │ ├── transparent-transfer-admin-ownership-wrong-signer.js │ │ ├── transparent-upgrade-storage.js │ │ ├── transparent-upgrade-validation.js │ │ ├── transparent-v4-change-admin-different-address.js │ │ ├── transparent-v4-change-admin-happy-path.js │ │ ├── transparent-v4-transfer-admin-ownership-multiple.js │ │ ├── transparent-v5-with-v4-manifest-admin.js │ │ ├── tx-overrides.js │ │ ├── use-deployed-implementation.js │ │ ├── uups-custom-proxy.js │ │ ├── uups-deploy-overload.js │ │ ├── uups-deploy-validation.js │ │ ├── uups-happy-path-v4.js │ │ ├── uups-happy-path-with-call-v4.js │ │ ├── uups-happy-path-with-call.js │ │ ├── uups-happy-path-with-enums.js │ │ ├── uups-happy-path-with-library.js │ │ ├── uups-happy-path-with-structs.js │ │ ├── uups-happy-path.js │ │ ├── uups-initial-owner.js │ │ ├── uups-initializers.js │ │ ├── uups-linked-libraries.js │ │ ├── uups-multi-compiler.js │ │ ├── uups-unknown-upgrade-interface.js │ │ ├── uups-upgrade-storage.js │ │ └── uups-upgrade-validation.js │ └── tsconfig.json └── plugin-truffle │ ├── CHANGELOG.md │ └── README.md ├── renovate.json ├── scripts ├── check-diff-docs.sh ├── prepare-foundry-docs.sh └── release │ ├── format-changelog.js │ ├── publish.sh │ └── version.sh ├── tsconfig.base.json ├── tsconfig.json └── yarn.lock /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { 6 | "repo": "OpenZeppelin/openzeppelin-upgrades" 7 | } 8 | ], 9 | "commit": false, 10 | "fixed": [], 11 | "linked": [], 12 | "access": "public", 13 | "baseBranch": "master", 14 | "updateInternalDependencies": "patch", 15 | "ignore": [] 16 | } 17 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: off 2 | github_checks: 3 | annotations: false 4 | coverage: 5 | status: 6 | project: 7 | default: 8 | threshold: 1% 9 | patch: 10 | default: 11 | only_pulls: true 12 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | ecmaVersion: 2020, 5 | }, 6 | extends: ['eslint:recommended', 'plugin:prettier/recommended'], 7 | env: { 8 | node: true, 9 | es2022: true, 10 | }, 11 | plugins: ['unicorn'], 12 | rules: { 13 | curly: 'warn', 14 | 'prettier/prettier': 'warn', 15 | 'unicorn/no-array-reduce': 'warn', 16 | 'no-plusplus': ['warn', { allowForLoopAfterthoughts: true }], 17 | }, 18 | ignorePatterns: ['submodules'], 19 | overrides: [ 20 | { 21 | files: ['*.ts'], 22 | parser: '@typescript-eslint/parser', 23 | plugins: ['@typescript-eslint'], 24 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], 25 | rules: { 26 | '@typescript-eslint/ban-ts-comment': 'off', 27 | '@typescript-eslint/no-explicit-any': 'off', 28 | '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], 29 | }, 30 | }, 31 | { 32 | files: ['ava.config.js'], 33 | parserOptions: { 34 | sourceType: 'module', 35 | }, 36 | }, 37 | { 38 | files: ['packages/plugin-truffle/**'], 39 | globals: { 40 | artifacts: 'readonly', 41 | contract: 'readonly', 42 | it: 'readonly', 43 | }, 44 | }, 45 | ], 46 | }; 47 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | 3 | runs: 4 | using: composite 5 | steps: 6 | - uses: actions/setup-node@v4 7 | with: 8 | node-version: 20.x 9 | cache: yarn 10 | 11 | - name: Install dependencies 12 | run: yarn --frozen-lockfile --prefer-offline 13 | shell: bash 14 | -------------------------------------------------------------------------------- /.github/workflows/changeset.yml: -------------------------------------------------------------------------------- 1 | name: Changeset 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | types: 8 | - opened 9 | - synchronize 10 | - labeled 11 | - unlabeled 12 | 13 | concurrency: 14 | group: changeset-${{ github.ref }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | check: 19 | runs-on: ubuntu-latest 20 | if: ${{ !contains(github.event.pull_request.labels.*.name, 'ignore-changeset') }} 21 | steps: 22 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 23 | with: 24 | fetch-depth: 0 # Include history so Changesets finds merge-base 25 | - name: Set up environment 26 | uses: ./.github/actions/setup 27 | - name: Check changeset 28 | run: npx changeset status --since=origin/${{ github.base_ref }} 29 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Packages 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | concurrency: version-or-publish-${{ github.ref }} 7 | 8 | jobs: 9 | publish: 10 | name: Publish Packages 11 | permissions: 12 | contents: write 13 | pull-requests: write 14 | runs-on: ubuntu-latest 15 | environment: publish 16 | steps: 17 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 18 | with: 19 | fetch-depth: 0 # To get all tags 20 | ref: ${{ github.ref }} 21 | - name: Set up environment 22 | uses: ./.github/actions/setup 23 | - name: Create Prepare Release PR or Publish 24 | id: changesets 25 | uses: changesets/action@e0145edc7d9d8679003495b11f87bd8ef63c0cba # v1.5.3 26 | with: 27 | title: Prepare Release 28 | commit: Prepare Release 29 | version: npm run version 30 | publish: npm run publish 31 | commitMode: github-api 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 35 | - name: Check changesets status 36 | if: steps.changesets.outputs.hasChangesets == 'true' 37 | run: | 38 | echo "Changesets found. Merge Prepare Release PR before publishing." 39 | exit 1 40 | - name: Check publish status 41 | if: steps.changesets.outputs.published == 'false' 42 | run: | 43 | echo "Publish failed. Check the logs for more details." 44 | exit 1 45 | -------------------------------------------------------------------------------- /.github/workflows/version.yml: -------------------------------------------------------------------------------- 1 | name: Version Packages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths-ignore: 8 | - 'docs/**' 9 | 10 | concurrency: version-or-publish-${{ github.ref }} 11 | 12 | jobs: 13 | version: 14 | name: Prepare Release PR 15 | permissions: 16 | contents: write 17 | pull-requests: write 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 21 | with: 22 | fetch-depth: 0 # To get all tags 23 | ref: ${{ github.ref }} 24 | - name: Set up environment 25 | uses: ./.github/actions/setup 26 | - name: Create Prepare Release PR 27 | uses: changesets/action@e0145edc7d9d8679003495b11f87bd8ef63c0cba # v1.5.3 28 | with: 29 | title: Prepare Release 30 | commit: Prepare Release 31 | version: npm run version 32 | commitMode: github-api 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.tsbuildinfo 3 | dist 4 | cache 5 | artifacts 6 | build 7 | .openzeppelin 8 | *.tgz 9 | .nyc_output 10 | coverage 11 | .env 12 | /examples 13 | package-lock.json 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "submodules/openzeppelin-foundry-upgrades"] 2 | path = submodules/openzeppelin-foundry-upgrades 3 | url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades 4 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "reporter": [ 3 | "text", 4 | "lcov" 5 | ], 6 | "all": true, 7 | "include": [ 8 | "packages/*/{src,dist}/**" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | arrowParens: 'avoid', 4 | trailingComma: 'all', 5 | printWidth: 120, 6 | bracketSpacing: true, 7 | }; 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to OpenZeppelin Upgrades 2 | ======= 3 | 4 | Contributions to OpenZeppelin Upgrades are welcome. Please review the information below to test out and contribute your changes. 5 | 6 | ## Building and testing the project 7 | 8 | ### Prerequisites 9 | The following prerequisites are required to build the project locally: 10 | - [Node.js](https://nodejs.org/) 11 | - [Yarn](https://yarnpkg.com/getting-started/install) 12 | 13 | After the prerequisites are installed, the commands below can be run from this project's root directory. 14 | 15 | ### Installing dependencies 16 | ```yarn install``` 17 | 18 | The dependencies must be installed at least once before running the tests or linter. 19 | 20 | ### Running tests 21 | ```yarn test``` 22 | 23 | Ensure that all tests pass. If you are adding new functionality, include testcases as appropriate. 24 | 25 | ### Running linter 26 | ```yarn lint``` 27 | 28 | If linting errors or warnings occur, run `yarn lint --fix` to attempt to auto-fix issues. If there are remaining issues that cannot be auto-fixed, manually address them and re-run the command to ensure it passes. 29 | 30 | ## Creating Pull Requests (PRs) 31 | 32 | As a contributor, we ask that you fork this repository, work on your own fork and then submit pull requests. The pull requests will be reviewed and eventually merged into the main repo. See ["Fork-a-Repo"](https://help.github.com/articles/fork-a-repo/) for how this works. 33 | 34 | ### Adding Changesets 35 | If your PR modifies code under `packages`, you will need to add a changeset to summarize the changes. The PR's `Changeset` GitHub check will give an error if this condition is not satisfied. 36 | - To add a changeset: from the root directory, run `yarn changeset` -------------------------------------------------------------------------------- /FUNDING.json: -------------------------------------------------------------------------------- 1 | { 2 | "drips": { 3 | "ethereum": { 4 | "ownedBy": "0xAeb37910f93486C85A1F8F994b67E8187554d664" 5 | } 6 | }, 7 | "opRetro": { 8 | "projectId": "0x939241afa4c4b9e1dda6b8250baa8f04fa8b0debce738cfd324c0b18f9926d25" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-2025 Zeppelin Group Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /docs/antora.yml: -------------------------------------------------------------------------------- 1 | name: upgrades-plugins 2 | title: Upgrades Plugins 3 | version: ~ 4 | nav: 5 | - modules/ROOT/nav.adoc 6 | -------------------------------------------------------------------------------- /docs/modules/ROOT/nav.adoc: -------------------------------------------------------------------------------- 1 | * xref:index.adoc[Overview] 2 | * xref:hardhat-upgrades.adoc[Using with Hardhat] 3 | ** xref:defender-deploy.adoc[OpenZeppelin Defender integration] 4 | ** xref:network-files.adoc[Network Files] 5 | * xref:foundry-upgrades.adoc[Using with Foundry] 6 | ** xref:foundry-defender.adoc[OpenZeppelin Defender integration] 7 | * xref:writing-upgradeable.adoc[Writing Upgradeable Contracts] 8 | * xref:proxies.adoc[Proxy Upgrade Pattern] 9 | * xref:faq.adoc[Frequently Asked Questions] 10 | 11 | .API Reference 12 | * xref:api-hardhat-upgrades.adoc[Hardhat Upgrades API] 13 | * xref:api-foundry-upgrades.adoc[Foundry Upgrades API] 14 | * xref:api-core.adoc[Upgrades Core & CLI] 15 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/api-foundry-upgrades.adoc: -------------------------------------------------------------------------------- 1 | include::foundry/api/pages/api-foundry-upgrades.adoc[] -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/foundry-defender.adoc: -------------------------------------------------------------------------------- 1 | include::foundry/pages/foundry-defender.adoc[] -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/foundry-upgrades.adoc: -------------------------------------------------------------------------------- 1 | include::foundry/pages/foundry-upgrades.adoc[] -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | command = "npm run docs" 3 | publish = "build/site" 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openzeppelin-upgrades", 3 | "private": true, 4 | "license": "MIT", 5 | "scripts": { 6 | "docs": "bash scripts/prepare-foundry-docs.sh && bash scripts/check-diff-docs.sh && oz-docs", 7 | "docs:foundry:ci": "bash scripts/prepare-foundry-docs.sh && oz-docs", 8 | "docs:watch": "oz-docs watch", 9 | "prepare": "wsrun -ms prepare && tsc -b", 10 | "lint": "yarn lint:path .", 11 | "lint:path": "eslint --ignore-path .gitignore --max-warnings 0", 12 | "test": "wsrun -ms test", 13 | "coverage": "nyc yarn test", 14 | "version": "bash scripts/release/version.sh", 15 | "publish": "bash scripts/release/publish.sh" 16 | }, 17 | "devDependencies": { 18 | "@openzeppelin/docs-utils": "^0.1.0", 19 | "@typescript-eslint/eslint-plugin": "^7.0.0", 20 | "@typescript-eslint/parser": "^7.0.0", 21 | "eslint": "^8.0.0", 22 | "eslint-config-prettier": "^9.0.0", 23 | "eslint-plugin-prettier": "^5.0.0", 24 | "eslint-plugin-unicorn": "^51.0.0", 25 | "ethers": "^6.8.1", 26 | "nyc": "^17.0.0", 27 | "prettier": "^3.0.0", 28 | "typescript": "^5.0.0", 29 | "wsrun": "^5.2.4", 30 | "@changesets/cli": "^2.29.3", 31 | "@changesets/changelog-github": "^0.5.1" 32 | }, 33 | "workspaces": { 34 | "packages": [ 35 | "packages/*" 36 | ] 37 | } 38 | } -------------------------------------------------------------------------------- /packages/core/ava.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | workerThreads: false, // required because of chdir in tests 3 | watchMode: { 4 | ignoreChanges: ['**/*.{ts,map,tsbuildinfo}', 'artifacts', 'cache'], 5 | }, 6 | typescript: { 7 | rewritePaths: { 'src/': 'dist/' }, 8 | compile: false, 9 | }, 10 | environmentVariables: { 11 | FORCE_COLOR: '0', 12 | }, 13 | timeout: '30s', 14 | }; 15 | -------------------------------------------------------------------------------- /packages/core/contracts/import.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; 5 | import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; 6 | import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 7 | import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 8 | import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; 9 | -------------------------------------------------------------------------------- /packages/core/contracts/test/FunctionSignatures.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.8; 3 | 4 | pragma experimental ABIEncoderV2; 5 | 6 | contract ContractFunctionSignatures { 7 | function f01() public {} 8 | function f02(uint x) public {} 9 | function f03(string calldata a) public {} 10 | function f04(string calldata a, string calldata b) public {} 11 | function f05(uint[] calldata xs) public {} 12 | function f06(uint[5] calldata xs) public {} 13 | 14 | enum Enum { 15 | A, 16 | B, 17 | C 18 | } 19 | 20 | function f07(Enum e) public {} 21 | 22 | struct S1 { 23 | uint m1; 24 | uint m2; 25 | } 26 | 27 | function f08(S1 calldata) public {} 28 | 29 | struct S2 { 30 | string m1; 31 | uint8 m2; 32 | S1 m3; 33 | } 34 | 35 | function f09(S2 calldata) public {} 36 | 37 | function f10(bytes calldata) public {} 38 | 39 | function f11(bytes32) public {} 40 | 41 | function f12(ContractFunctionSignatures) public {} 42 | 43 | function f13(function(address,uint256,bytes32) external) public {} 44 | 45 | function f14(function(address,uint256,bytes32) external returns (bool)) public {} 46 | 47 | function f15(function(Enum,S1 memory) external returns (bool)) public {} 48 | 49 | function f16(function(Enum,S1 memory) external returns (bool),Enum,S1 memory) public {} 50 | } 51 | 52 | library LibraryFunctionSignatures { 53 | struct S { 54 | address a; 55 | } 56 | 57 | function lf01(S storage s) external {} 58 | 59 | function lf02(mapping (uint => uint) storage m) external {} 60 | 61 | function lf03(uint[] storage s) external {} 62 | } 63 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ManifestMigrate.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.8; 3 | 4 | contract ManifestMigrateLayout { 5 | struct T { 6 | bool b; 7 | } 8 | 9 | struct S { 10 | uint x; 11 | string s; 12 | T t; 13 | } 14 | 15 | enum E { 16 | E1, 17 | E2 18 | } 19 | 20 | uint x1; 21 | T t1; 22 | S s1; 23 | string s2; 24 | bool b1; 25 | E e1; 26 | } 27 | 28 | contract ManifestMigrateUnique is ManifestMigrateLayout { 29 | function useAll() external { 30 | x1 = 5; 31 | t1 = T({ b: true }); 32 | s1 = S({ x: 6, s: "ok", t: t1 }); 33 | s2 = "no"; 34 | b1 = false; 35 | e1 = E.E2; 36 | } 37 | } 38 | 39 | contract ManifestMigrateUnambiguous0 is ManifestMigrateLayout { 40 | function useAll() external { 41 | x1 = 10; 42 | t1 = T({ b: false }); 43 | s1 = S({ x: 6, s: "string", t: t1 }); 44 | s2 = "ok"; 45 | b1 = false; 46 | e1 = E.E1; 47 | } 48 | } 49 | 50 | // These two are expected to have the same bytecode modulo metadata. 51 | contract ManifestMigrateUnambiguous1 is ManifestMigrateUnambiguous0 { } 52 | contract ManifestMigrateUnambiguous2 is ManifestMigrateUnambiguous0 { } 53 | 54 | // These two are expected to have the same bytecode modulo metadata and similar 55 | // layout, but different types (see struct D members); 56 | contract ManifestMigrateAmbiguous1 is ManifestMigrateUnique { 57 | struct D { 58 | uint w; 59 | } 60 | D d1; 61 | function test() external { 62 | d1.w += 1; 63 | } 64 | } 65 | contract ManifestMigrateAmbiguous2 is ManifestMigrateUnique { 66 | struct D { 67 | uint z; 68 | } 69 | D d1; 70 | function test() external { 71 | d1.z += 1; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /packages/core/contracts/test/Memory05.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.5.16; 3 | 4 | contract Memory05 { 5 | mapping(string => address) a; 6 | mapping(bytes => address) b; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/contracts/test/Memory08.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | 4 | contract Memory08 { 5 | mapping(string => address) a; 6 | mapping(bytes => address) b; 7 | } 8 | 9 | contract Memory08Bad { 10 | mapping(bytes => address) a; 11 | mapping(string => address) b; 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/contracts/test/NamespacedConflicts.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | contract DuplicateNamespace { 5 | function foo() public pure returns (uint256) { 6 | return 0; 7 | } 8 | 9 | /// @custom:storage-location erc7201:conflicting 10 | struct Conflicting1 { 11 | uint256 b; 12 | } 13 | 14 | /// @custom:storage-location erc7201:conflicting 15 | struct Conflicting2 { 16 | uint256 c; 17 | } 18 | 19 | function foo2() public pure returns (uint256) { 20 | return 0; 21 | } 22 | } 23 | 24 | contract Parent { 25 | function foo5() public pure returns (uint256) { 26 | return 0; 27 | } 28 | 29 | /// @custom:storage-location erc7201:conflicting 30 | struct Conflicting0 { 31 | uint256 a; 32 | } 33 | 34 | function foo6() public pure returns (uint256) { 35 | return 0; 36 | } 37 | } 38 | 39 | contract ConflictsWithParent is Parent { 40 | function foo3() public pure returns (uint256) { 41 | return 0; 42 | } 43 | 44 | /// @custom:storage-location erc7201:conflicting 45 | struct Conflicting { 46 | uint256 a; 47 | } 48 | 49 | function foo4() public pure returns (uint256) { 50 | return 0; 51 | } 52 | } 53 | 54 | contract ConflictsInBothParents is DuplicateNamespace, ConflictsWithParent { 55 | uint256 a; 56 | } 57 | 58 | contract InheritsDuplicate is DuplicateNamespace { 59 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/NamespacedConflictsLayout.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | contract DuplicateNamespace { 5 | /// @custom:storage-location erc7201:conflicting 6 | struct Conflicting1 { 7 | uint256 b; 8 | } Conflicting1 $Conflicting1; 9 | 10 | /// @custom:storage-location erc7201:conflicting 11 | struct Conflicting2 { 12 | uint256 c; 13 | } Conflicting2 $Conflicting2; 14 | } 15 | 16 | contract Parent { 17 | /// @custom:storage-location erc7201:conflicting 18 | struct Conflicting0 { 19 | uint256 a; 20 | } Conflicting0 $Conflicting0; 21 | } 22 | 23 | contract ConflictsWithParent is Parent { 24 | /// @custom:storage-location erc7201:conflicting 25 | struct Conflicting { 26 | uint256 a; 27 | } Conflicting $Conflicting; 28 | } 29 | 30 | contract ConflictsInBothParents is DuplicateNamespace, ConflictsWithParent { 31 | uint256 a; 32 | } 33 | 34 | contract InheritsDuplicate is DuplicateNamespace { 35 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/NamespacedInLibrary.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | /// This is not considered a namespace according to ERC-7201 because the namespaced struct is outside of a contract. 5 | library LibraryWithNamespace { 6 | /// @custom:storage-location erc7201:example.main 7 | struct MainStorage { 8 | uint256 x; 9 | uint256 y; 10 | } 11 | } 12 | 13 | contract UsesLibraryWithNamespace { 14 | // keccak256(abi.encode(uint256(keccak256("example.main")) - 1)) & ~bytes32(uint256(0xff)); 15 | bytes32 private constant MAIN_STORAGE_LOCATION = 16 | 0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500; 17 | 18 | function _getMainStorage() private pure returns (LibraryWithNamespace.MainStorage storage $) { 19 | assembly { 20 | $.slot := MAIN_STORAGE_LOCATION 21 | } 22 | } 23 | 24 | function _getXTimesY() internal view returns (uint256) { 25 | LibraryWithNamespace.MainStorage storage $ = _getMainStorage(); 26 | return $.x * $.y; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/contracts/test/NamespacedLayout.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | contract Example { 5 | MainStorage $MainStorage; 6 | 7 | /// @custom:storage-location erc7201:example.main 8 | struct MainStorage { 9 | uint256 x; 10 | uint256 y; 11 | } 12 | 13 | // keccak256(abi.encode(uint256(keccak256("example.main")) - 1)) & ~bytes32(uint256(0xff)); 14 | bytes32 private constant MAIN_STORAGE_LOCATION = 15 | 0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500; 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/contracts/test/NamespacedOutsideContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | // This is not valid according to ERC-7201 because the namespaced struct is outside of a contract. 5 | 6 | /// @custom:storage-location erc7201:example.main 7 | struct MainStorage { 8 | uint256 x; 9 | uint256 y; 10 | } 11 | 12 | contract Example { 13 | // keccak256(abi.encode(uint256(keccak256("example.main")) - 1)) & ~bytes32(uint256(0xff)); 14 | bytes32 private constant MAIN_STORAGE_LOCATION = 15 | 0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500; 16 | 17 | function _getMainStorage() private pure returns (MainStorage storage $) { 18 | assembly { 19 | $.slot := MAIN_STORAGE_LOCATION 20 | } 21 | } 22 | 23 | function _getXTimesY() internal view returns (uint256) { 24 | MainStorage storage $ = _getMainStorage(); 25 | return $.x * $.y; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/contracts/test/NamespacedToModify07.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.7.6; 3 | 4 | contract HasFunction { 5 | constructor(uint) {} 6 | function foo() pure public returns (uint) {} 7 | } 8 | 9 | contract UsingFunction is HasFunction(1) { 10 | uint x = foo(); 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/contracts/test/NamespacedToModifyCustomLayout.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.29; 3 | 4 | abstract contract Base { 5 | } 6 | 7 | contract ExampleCustomLayout layout at 0x123 is Base { 8 | /// @custom:storage-location erc7201:example.main 9 | struct MainStorage { 10 | uint256 x; 11 | uint256 z; 12 | } 13 | } 14 | 15 | contract ExampleCustomLayout_DifferentSpecifierOrder is Base layout at 0x123 { 16 | /// @custom:storage-location erc7201:example.main 17 | struct MainStorage { 18 | uint256 x; 19 | uint256 z; 20 | } 21 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/NamespacedToModifyImported.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {CONSTANT_USING_SELECTOR, plusTwo, plusThree, CustomErrorOutsideContract} from "./NamespacedToModify.sol"; 5 | 6 | contract Example {} 7 | -------------------------------------------------------------------------------- /packages/core/contracts/test/Proxiable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.2; 3 | 4 | // These contracts are for testing only, they are not safe for use in production. 5 | 6 | abstract contract BrokenProxiable { 7 | string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; 8 | 9 | // NOT SAFE FOR PRODUCTION USE. 10 | // This does NOT actually perform any upgrade, but is only for tests to check that this function exists. 11 | function upgradeToAndCall(address newImplementation, bytes calldata data) external { 12 | _authorizeUpgrade(newImplementation); 13 | } 14 | 15 | function _authorizeUpgrade(address newImplementation) internal { 16 | _beforeUpgrade(newImplementation); 17 | } 18 | 19 | function _beforeUpgrade(address newImplementation) internal virtual; 20 | } 21 | 22 | contract ChildOfProxiable is BrokenProxiable { 23 | function _beforeUpgrade(address newImplementation) internal virtual override {} 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/contracts/test/RenamedRetyped.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.2; 3 | 4 | contract RenameV1 { 5 | uint x; 6 | } 7 | 8 | contract RenameV2 { 9 | /// @custom:oz-renamed-from x 10 | uint y; 11 | } 12 | 13 | contract RetypeV1 { 14 | bool x; 15 | } 16 | 17 | contract RetypeV2 { 18 | /// @dev a retyped variable 19 | /// @custom:oz-retyped-from bool 20 | uint8 x; 21 | } 22 | 23 | contract WronglyReportedRetypeV3 { 24 | /// @custom:oz-retyped-from address 25 | uint8 x; 26 | } 27 | 28 | contract MissmatchingTypeRetypeV4 { 29 | /// @custom:oz-retyped-from bool 30 | bytes32 x; 31 | } 32 | 33 | contract ConfusingRetypeV1 { 34 | address y; 35 | bool x; 36 | } 37 | 38 | contract ConfusingRetypeV2 { 39 | address y; 40 | /// @custom:oz-retyped-from address 41 | uint8 x; 42 | } 43 | 44 | contract NonHardcodedRetypeV1 { 45 | address a; 46 | } 47 | 48 | contract NonHardcodedRetypeV2 { 49 | /// @custom:oz-retyped-from address 50 | bytes20 a; 51 | } 52 | 53 | contract LayoutChangeV1 { 54 | bool a; 55 | bool b; 56 | } 57 | 58 | contract LayoutChangeV2 { 59 | /// @custom:oz-retyped-from bool 60 | uint16 a; 61 | 62 | /// @custom:oz-retyped-from bool 63 | uint8 b; 64 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/Standalone.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.2; 3 | 4 | contract StandaloneV1 { 5 | uint a; 6 | string b; 7 | 8 | function extremelyUnsafe(address target, bytes memory data) public { 9 | (bool ok, ) = target.delegatecall(data); 10 | require(ok); 11 | } 12 | } 13 | 14 | contract StandaloneV2Good { 15 | uint a; 16 | string b; 17 | uint c; 18 | } 19 | 20 | contract StandaloneV2Bad { 21 | uint x; 22 | uint a; 23 | string b; 24 | } 25 | 26 | contract StandaloneRenameV1 { 27 | uint x; 28 | } 29 | 30 | contract StandaloneRenameV2 { 31 | /// @custom:oz-renamed-from x 32 | uint y; 33 | } 34 | 35 | contract StandaloneRenameV3 is StandaloneRenameV2 { 36 | uint z; 37 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/Storage088.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.8; 3 | 4 | contract Storage088 { 5 | type MyUserValueType is uint128; 6 | MyUserValueType my_user_value; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/contracts/test/Storage089.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | 4 | contract Storage089 { 5 | type MyUserValueType is uint128; 6 | MyUserValueType my_user_value; 7 | } 8 | 9 | contract Storage089_V2 { 10 | type MyUserValueType is uint128; 11 | MyUserValueType my_user_value; 12 | 13 | uint x; 14 | } 15 | 16 | contract Storage089_V3 { 17 | type MyUserValueType is uint8; 18 | MyUserValueType my_user_value; 19 | 20 | uint x; 21 | } 22 | 23 | contract Storage089_MappingUVDTKey_V1 { 24 | type MyUserValueType is bool; 25 | 26 | mapping (MyUserValueType => uint) m1; 27 | mapping (MyUserValueType => uint) m2; 28 | mapping (uint8 => uint) m3; 29 | } 30 | 31 | contract Storage089_MappingUVDTKey_V2_Ok { 32 | type MyUserValueType is uint8; 33 | 34 | mapping (MyUserValueType => uint) m1; 35 | mapping (MyUserValueType => uint) m2; 36 | mapping (uint8 => uint) m3; 37 | } 38 | 39 | contract Storage089_MappingUVDTKey_V2_Bad { 40 | type MyUserValueType is uint16; 41 | 42 | mapping (MyUserValueType => uint) m1; 43 | mapping (MyUserValueType => uint) m2; 44 | mapping (uint8 => uint) m3; 45 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/StorageRenamedRetyped.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | 4 | contract StorageRenamedRetyped { 5 | /// @custom:oz-renamed-from b 6 | uint a; 7 | 8 | /// @custom:oz-retyped-from bool 9 | uint8 b; 10 | 11 | /// @custom:oz-renamed-from b 12 | /// @custom:oz-retyped-from bool 13 | uint8 c; 14 | 15 | /// @custom:oz-retyped-from bool 16 | /// @custom:oz-renamed-from b 17 | uint8 d; 18 | } 19 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ValidationsImport.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.8; 3 | 4 | contract ImportedHasStateVariableAssignment { 5 | uint x = 1; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ValidationsNatspecImport.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.2; 3 | 4 | /// @custom:oz-upgrades-unsafe-allow state-variable-assignment 5 | contract ImportedHasStateVariableAssignmentNatspec1 { 6 | uint x = 1; 7 | } 8 | 9 | contract ImportedHasStateVariableAssignmentNatspec2 { 10 | /// @custom:oz-upgrades-unsafe-allow state-variable-assignment 11 | uint x = 1; 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ValidationsSameNameSafe.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.8; 3 | 4 | // This helps the tests by hinting Hardhat to compile the two files together. 5 | import './Storage.sol'; 6 | 7 | contract SameName { 8 | function d() public { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ValidationsSameNameUnsafe.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.8; 3 | 4 | // This helps the tests by hinting Hardhat to compile the two files together. 5 | import './Storage.sol'; 6 | 7 | contract SameName { 8 | function d() public { 9 | (bool s, ) = msg.sender.delegatecall(""); 10 | s; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ValidationsUDVT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | contract ValidationsUDVT { 5 | type MyUserValueType is uint128; 6 | MyUserValueType my_user_value; 7 | function foo(MyUserValueType v) external { 8 | my_user_value = v; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/Version.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.6.8; 3 | 4 | contract Greeter { 5 | // one line version 6 | function greet() public pure returns (string memory) { return "Hi!"; } 7 | } 8 | 9 | contract GreeterDifferentFormatting { 10 | function greet() public pure returns (string memory) { 11 | // expanded version 12 | return "Hi!"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/Annotation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.8; 3 | 4 | /// @custom:oz-upgrades 5 | contract Annotation { 6 | uint128 public x; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/InvalidAnnotationArgsUpgrades.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | /** 5 | * @custom:oz-upgrades Storage088 6 | */ 7 | contract InvalidAnnotationArgsUpgrades { 8 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/InvalidAnnotationArgsUpgradesFrom.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | /** 5 | * @custom:oz-upgrades-from 6 | */ 7 | contract InvalidAnnotationArgsUpgradesFrom { 8 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/SelfReferences.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.8; 3 | 4 | /// @custom:oz-upgrades-from SelfReference 5 | contract SelfReference { 6 | uint public x; 7 | } 8 | 9 | /// @custom:oz-upgrades-from contracts/test/cli/SelfReferences.sol:SelfReferenceFullyQualified 10 | contract SelfReferenceFullyQualified { 11 | uint public x; 12 | } 13 | 14 | contract NoAnnotation { 15 | uint public x; 16 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/Storage088.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.8; 3 | 4 | contract Storage088 { 5 | type MyUserValueType is uint128; 6 | MyUserValueType my_user_value; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/Storage089.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.9; 3 | 4 | /** 5 | * @custom:oz-upgrades-from Storage088 6 | */ 7 | contract Storage089 { 8 | type MyUserValueType is uint128; 9 | MyUserValueType my_user_value; 10 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/ValidateBuildInfoV1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | contract MyContract { 5 | uint256 public x; 6 | uint256 public y; 7 | uint256[48] private __gap; 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/ValidateBuildInfoV2_Annotation_Bad.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | /// @custom:oz-upgrades-from build-info-v1:MyContract 5 | contract MyContract { 6 | uint256 public x; 7 | uint256[49] private __gap; 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/ValidateBuildInfoV2_Annotation_Ok.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | /// @custom:oz-upgrades-from build-info-v1:MyContract 5 | contract MyContract { 6 | uint256 public x; 7 | uint256 public y; 8 | uint256 public z; 9 | uint256[47] private __gap; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/ValidateBuildInfoV2_Bad.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | contract MyContract { 5 | uint256 public x; 6 | uint256[49] private __gap; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/ValidateBuildInfoV2_Branch_Bad.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | /// @custom:oz-upgrades-from build-info-v2:MyContract 5 | contract MyContract { 6 | uint256 public x; 7 | uint256 public y; 8 | uint256 public zBranch; 9 | uint256[47] private __gap; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/ValidateBuildInfoV2_Branch_Ok.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | /// @custom:oz-upgrades-from build-info-v1:MyContract 5 | contract MyContract { 6 | uint256 public x; 7 | uint256 public y; 8 | uint256 public zBranch; 9 | uint256[47] private __gap; 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/ValidateBuildInfoV2_Ok.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | contract MyContract { 5 | uint256 public x; 6 | uint256 public y; 7 | uint256 public z; 8 | uint256[47] private __gap; 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/excludes/Abstract1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | abstract contract Abstract1 { 5 | uint256 public immutable x; 6 | 7 | constructor(uint256 _x) { 8 | x = _x; 9 | } 10 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/excludes/Abstract2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | abstract contract Abstract2 { 5 | uint256 public immutable y; 6 | 7 | constructor(uint256 _y) { 8 | y = _y; 9 | } 10 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/excludes/AbstractUUPS.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 5 | import {Abstract1} from "./Abstract1.sol"; 6 | import {Abstract2} from "./Abstract2.sol"; 7 | 8 | abstract contract AbstractUUPS is UUPSUpgradeable, Abstract1, Abstract2 { 9 | uint256 public immutable z; 10 | 11 | constructor(uint256 _x, uint256 _y, uint256 _z) Abstract1(_x) Abstract2(_y) { 12 | z = _z; 13 | } 14 | 15 | function initialize() initializer public { 16 | __UUPSUpgradeable_init(); 17 | } 18 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/excludes/ImportVersionsAndBeacon.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./V1.sol"; 5 | import "./V2Bad1.sol"; 6 | import "./V2Bad2.sol"; 7 | import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; 8 | 9 | abstract contract Dummy { 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/excludes/UsesAbstractUUPS.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | // This contract is for testing only, it is not safe for use in production. 5 | 6 | import {AbstractUUPS} from "./AbstractUUPS.sol"; 7 | 8 | contract UsesAbstractUUPS is AbstractUUPS { 9 | constructor(uint256 _x, uint256 _y, uint256 _z) AbstractUUPS(x, y, z) { 10 | } 11 | 12 | function _authorizeUpgrade(address newImplementation) internal pure override { 13 | revert("Upgrade disabled"); 14 | } 15 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/excludes/V1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | contract V1 { 5 | uint256 public x; 6 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/excludes/V2Bad1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | /// @custom:oz-upgrades-from V1 5 | contract V2Bad1 { 6 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/cli/excludes/V2Bad2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | /// @custom:oz-upgrades-from V1 5 | contract V2Bad2 { 6 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/AllowChild.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./AllowParent.sol"; 5 | 6 | contract AllowChild is AllowParent { 7 | function shouldBeAllowed(bytes memory data) public { 8 | internalDelegateCall(data); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/AllowChildCallTransitive.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./AllowReachableParentCall.sol"; 5 | 6 | contract AllowChildCallTransitive is AllowReachableParentCall { 7 | function myfunction(bytes memory data) internal { 8 | allowed(data); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/AllowChildInheritedTransitive.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TransitiveAllowReachable.sol"; 5 | 6 | contract AllowChildInheritedTransitive is TransitiveAllowReachable { 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/AllowChildSelfReachable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./AllowParentSelfReachable.sol"; 5 | 6 | contract AllowChildSelfReachable is AllowParentSelfReachable { 7 | function shouldBeAllowed(bytes memory data) public { 8 | internalDelegateCall(data); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/AllowParent.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | /** 5 | * allow causes the delegatecall to be ignored for all functions in this contract 6 | * 7 | * @custom:oz-upgrades-unsafe-allow delegatecall 8 | */ 9 | contract AllowParent { 10 | function internalDelegateCall( 11 | bytes memory data 12 | ) internal returns (bytes memory) { 13 | (, bytes memory returndata) = address(this).delegatecall(data); 14 | return returndata; 15 | } 16 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/AllowParentSelfReachable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | /** 5 | * allow-reachable causes the delegatecall to be ignored for all functions in this contract, including its own lexical scope 6 | * 7 | * @custom:oz-upgrades-unsafe-allow-reachable delegatecall 8 | */ 9 | contract AllowParentSelfReachable { 10 | function internalDelegateCall( 11 | bytes memory data 12 | ) internal returns (bytes memory) { 13 | (, bytes memory returndata) = address(this).delegatecall(data); 14 | return returndata; 15 | } 16 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/AllowReachable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TransitiveRiskyLibrary.sol"; 5 | 6 | // allow-reachable causes the delegatecall in a transitive function to be ignored 7 | contract AllowReachable { 8 | /// @custom:oz-upgrades-unsafe-allow-reachable delegatecall 9 | function unsafe(bytes memory data) public { 10 | TransitiveRiskyLibrary.internalDelegateCall(address(this), data); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/AllowReachableParent.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TransitiveUnsafeParent.sol"; 5 | 6 | /** 7 | * allow-reachable causes the delegatecall in a parent function to be ignored 8 | * 9 | * this is actually unsafe but only allowed here for testing purposes! 10 | * 11 | * @custom:oz-upgrades-unsafe-allow-reachable delegatecall 12 | */ 13 | contract AllowReachableParent is TransitiveUnsafeParent { 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/AllowReachableParentCall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyParentContract.sol"; 5 | 6 | /** 7 | * allow-reachable causes the delegatecall to be ignored for all reachable functions from this contract 8 | * 9 | * @custom:oz-upgrades-unsafe-allow-reachable delegatecall 10 | */ 11 | contract AllowReachableParentCall is RiskyParentContract { 12 | function allowed(bytes memory data) internal { 13 | internalDelegateCall(address(this), data); 14 | } 15 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/Modifiers.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | abstract contract UnsafeParentModifier { 5 | modifier unsafe(bytes memory data) { 6 | _; 7 | (bool s, ) = msg.sender.delegatecall(data); 8 | s; 9 | } 10 | } 11 | 12 | // TODO: do not throw an error in this case 13 | // contract ModifierNotUsed is UnsafeParentModifier { 14 | // function foo() public {} 15 | // } 16 | 17 | contract ModifierUsed is UnsafeParentModifier { 18 | function foo() public unsafe('') {} 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/RiskyFreeFunctions.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | function isContract(address account) view returns (bool) { 5 | return account.code.length > 0; 6 | } 7 | 8 | function freeDelegateCall( 9 | address target, 10 | bytes memory data 11 | ) returns (bytes memory) { 12 | (, bytes memory returndata) = target.delegatecall(data); 13 | return returndata; 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/RiskyLibrary.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | library RiskyLibrary { 5 | function isContract(address account) internal view returns (bool) { 6 | return account.code.length > 0; 7 | } 8 | 9 | function internalDelegateCall( 10 | address target, 11 | bytes memory data 12 | ) internal returns (bytes memory) { 13 | (, bytes memory returndata) = target.delegatecall(data); 14 | return returndata; 15 | } 16 | 17 | function privateDelegateCall( 18 | address target, 19 | bytes memory data 20 | ) private returns (bytes memory) { 21 | (, bytes memory returndata) = target.delegatecall(data); 22 | return returndata; 23 | } 24 | 25 | function publicDelegateCall( 26 | address target, 27 | bytes memory data 28 | ) public returns (bytes memory) { 29 | (, bytes memory returndata) = target.delegatecall(data); 30 | return returndata; 31 | } 32 | 33 | function externalDelegateCall( 34 | address target, 35 | bytes memory data 36 | ) external returns (bytes memory) { 37 | (, bytes memory returndata) = target.delegatecall(data); 38 | return returndata; 39 | } 40 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/RiskyParentContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | contract RiskyParentContract { 5 | function isContract(address account) internal view returns (bool) { 6 | return account.code.length > 0; 7 | } 8 | 9 | function internalDelegateCall( 10 | address target, 11 | bytes memory data 12 | ) internal returns (bytes memory) { 13 | (, bytes memory returndata) = target.delegatecall(data); 14 | return returndata; 15 | } 16 | 17 | function privateDelegateCall( 18 | address target, 19 | bytes memory data 20 | ) private returns (bytes memory) { 21 | (, bytes memory returndata) = target.delegatecall(data); 22 | return returndata; 23 | } 24 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/SafeContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | contract SafeContract { 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/SafeContractWithFreeFunctionCall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyFreeFunctions.sol" as RiskyFreeFunctions; 5 | 6 | contract SafeContractWithFreeFunctionCall { 7 | function safe() public view returns (bool) { 8 | return RiskyFreeFunctions.isContract(address(this)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/SafeContractWithLibraryCall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyLibrary.sol"; 5 | 6 | contract SafeContractWithLibraryCall { 7 | function safe() public view returns (bool) { 8 | return RiskyLibrary.isContract(address(this)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/SafeContractWithLibraryImport.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyLibrary.sol"; 5 | 6 | contract SafeContractWithLibraryImport { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/SafeContractWithLibraryUsingFor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyLibrary.sol"; 5 | 6 | contract SafeContractWithLibraryUsingFor { 7 | using RiskyLibrary for address; 8 | 9 | function safe() public view returns (bool) { 10 | return address(this).isContract(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/SafeContractWithParentCall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyParentContract.sol"; 5 | 6 | contract SafeContractWithParentCall is RiskyParentContract { 7 | function safe() public view returns (bool) { 8 | return isContract(address(this)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/SafeContractWithTransitiveLibraryCall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TransitiveRiskyLibrary.sol"; 5 | 6 | contract SafeContractWithTransitiveLibraryCall { 7 | function safe() public view returns (bool) { 8 | return TransitiveRiskyLibrary.isContract(address(this)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/SafeRecursion.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyParentContract.sol"; 5 | 6 | contract SafeRecursion is RiskyParentContract { 7 | function safe(uint256 i) public view returns (bool) { 8 | if (++i == 10) { 9 | return isContract(address(this)); 10 | } else { 11 | return safe(i); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/TransitiveAllowReachable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyLibrary.sol"; 5 | 6 | /** 7 | * allow delegatecalls on all of this contract's functions and reachable code 8 | * 9 | * @custom:oz-upgrades-unsafe-allow-reachable delegatecall 10 | */ 11 | contract TransitiveAllowReachable { 12 | function internalDelegateCall( 13 | bytes memory data 14 | ) external returns (bytes memory) { 15 | return RiskyLibrary.internalDelegateCall(address(this), data); 16 | } 17 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/TransitiveRiskyLibrary.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyLibrary.sol"; 5 | 6 | library TransitiveRiskyLibrary { 7 | function isContract(address account) internal view returns (bool) { 8 | return RiskyLibrary.isContract(account); 9 | } 10 | 11 | function internalDelegateCall( 12 | address target, 13 | bytes memory data 14 | ) internal returns (bytes memory) { 15 | return RiskyLibrary.internalDelegateCall(target, data); 16 | } 17 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/TransitiveUnsafeParent.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./UnsafeParentContract.sol"; 5 | 6 | contract TransitiveUnsafeParent is UnsafeParentContract { 7 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeAllow.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TransitiveRiskyLibrary.sol"; 5 | 6 | // allow has no effect because the delegatecall is in a transitive function 7 | contract UnsafeAllow { 8 | /// @custom:oz-upgrades-unsafe-allow delegatecall 9 | function unsafe(address target, bytes memory data) public { 10 | TransitiveRiskyLibrary.internalDelegateCall(target, data); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeAllowParent.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TransitiveUnsafeParent.sol"; 5 | 6 | /** 7 | * allow has no effect because the delegatecall is in a parent function 8 | * 9 | * @custom:oz-upgrades-unsafe-allow delegatecall 10 | */ 11 | contract UnsafeAllowParent is TransitiveUnsafeParent { 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeAllowReachableDifferentOpcode.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TransitiveRiskyLibrary.sol"; 5 | 6 | // allow-reachable has no effect because the transitive function has a different opcode 7 | contract UnsafeAllowReachableDifferentOpcode { 8 | /// @custom:oz-upgrades-unsafe-allow-reachable selfdestruct 9 | function unsafe(bytes memory data) public { 10 | TransitiveRiskyLibrary.internalDelegateCall(address(this), data); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeAllowReachableParentDifferentOpcode.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TransitiveUnsafeParent.sol"; 5 | 6 | /** 7 | * allow-reachable has no effect because the parent function has a different opcode 8 | * 9 | * @custom:oz-upgrades-unsafe-allow-reachable selfdestruct 10 | */ 11 | contract UnsafeAllowReachableParentDifferentOpcode is TransitiveUnsafeParent { 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | contract UnsafeContract { 5 | function externalDelegateCall( 6 | address target, 7 | bytes memory data 8 | ) external returns (bytes memory) { 9 | (, bytes memory returndata) = target.delegatecall(data); 10 | return returndata; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeContractWithFreeFunctionCall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyFreeFunctions.sol" as RiskyFreeFunctions; 5 | 6 | contract UnsafeContractWithFreeFunctionCall { 7 | function unsafe(address target, bytes memory data) public { 8 | RiskyFreeFunctions.freeDelegateCall(target, data); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeContractWithInheritedParent.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./UnsafeParentContract.sol"; 5 | 6 | contract UnsafeContractWithInheritedParent is UnsafeParentContract { 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeContractWithInheritedTransitiveParent.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TransitiveUnsafeParent.sol"; 5 | 6 | contract UnsafeContractWithInheritedTransitiveParent is TransitiveUnsafeParent { 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeContractWithLibraryCall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyLibrary.sol"; 5 | 6 | contract UnsafeContractWithLibraryCall { 7 | function unsafe(address target, bytes memory data) public { 8 | RiskyLibrary.internalDelegateCall(target, data); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeContractWithLibraryUsingFor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyLibrary.sol"; 5 | 6 | contract UnsafeContractWithLibraryUsingFor { 7 | using RiskyLibrary for address; 8 | 9 | function unsafe(address target, bytes memory data) public { 10 | target.internalDelegateCall(data); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeContractWithParentCall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyParentContract.sol"; 5 | 6 | contract UnsafeContractWithParentCall is RiskyParentContract { 7 | function unsafe(address target, bytes memory data) public { 8 | internalDelegateCall(target, data); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeContractWithTransitiveLibraryCall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./TransitiveRiskyLibrary.sol"; 5 | 6 | contract UnsafeContractWithTransitiveLibraryCall { 7 | function unsafe(address target, bytes memory data) public { 8 | TransitiveRiskyLibrary.internalDelegateCall(target, data); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeParentContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | contract UnsafeParentContract { 5 | function isContract(address account) internal view returns (bool) { 6 | return account.code.length > 0; 7 | } 8 | 9 | function internalDelegateCall( 10 | address target, 11 | bytes memory data 12 | ) internal returns (bytes memory) { 13 | (, bytes memory returndata) = target.delegatecall(data); 14 | return returndata; 15 | } 16 | 17 | function privateDelegateCall( 18 | address target, 19 | bytes memory data 20 | ) private returns (bytes memory) { 21 | (, bytes memory returndata) = target.delegatecall(data); 22 | return returndata; 23 | } 24 | 25 | // unsafe! 26 | function publicDelegateCall( 27 | address target, 28 | bytes memory data 29 | ) public returns (bytes memory) { 30 | (, bytes memory returndata) = target.delegatecall(data); 31 | return returndata; 32 | } 33 | 34 | // unsafe! 35 | function externalDelegateCall( 36 | address target, 37 | bytes memory data 38 | ) external returns (bytes memory) { 39 | (, bytes memory returndata) = target.delegatecall(data); 40 | return returndata; 41 | } 42 | } -------------------------------------------------------------------------------- /packages/core/contracts/test/ignore-errors/UnsafeRecursion.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "./RiskyParentContract.sol"; 5 | 6 | contract UnsafeRecursion is RiskyParentContract { 7 | function unsafe(uint256 i, address target, bytes memory data) public returns (bytes memory) { 8 | if (++i == 10) { 9 | return internalDelegateCall(target, data); 10 | } else { 11 | return unsafe(i, target, data); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/add-proxy-to-manifest.ts: -------------------------------------------------------------------------------- 1 | import { logWarning, Manifest, ProxyDeployment } from '.'; 2 | 3 | export async function addProxyToManifest(kind: ProxyDeployment['kind'], address: string, manifest: Manifest) { 4 | await manifest.addProxy({ kind, address }); 5 | 6 | if (kind !== 'transparent' && (await manifest.getAdmin())) { 7 | logWarning(`A proxy admin was previously deployed on this network`, [ 8 | `This is not natively used with the current kind of proxy ('${kind}').`, 9 | `Changes to the admin will have no effect on this new proxy.`, 10 | ]); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/beacon.ts: -------------------------------------------------------------------------------- 1 | import { InvalidBeacon, getImplementationAddressFromBeacon } from './impl-address'; 2 | import { EthereumProvider } from './provider'; 3 | 4 | /** 5 | * Checks if the address looks like a beacon. 6 | * 7 | * @returns true if the address has an implementation() function that returns an address, false otherwise. 8 | */ 9 | export async function isBeacon(provider: EthereumProvider, beaconAddress: string): Promise { 10 | try { 11 | await getImplementationAddressFromBeacon(provider, beaconAddress); 12 | return true; 13 | } catch (e: any) { 14 | if (e instanceof InvalidBeacon) { 15 | return false; 16 | } else { 17 | throw e; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/core/src/call-optional-signature.ts: -------------------------------------------------------------------------------- 1 | import { keccak256 } from 'ethereumjs-util'; 2 | import { call, EthereumProvider } from './provider'; 3 | 4 | export async function callOptionalSignature(provider: EthereumProvider, address: string, signature: string) { 5 | const data = '0x' + keccak256(Buffer.from(signature)).toString('hex').slice(0, 8); 6 | try { 7 | return await call(provider, address, data); 8 | } catch (e: any) { 9 | if ( 10 | e.message.includes('function selector was not recognized') || 11 | e.message.includes('invalid opcode') || 12 | e.message.includes('revert') || 13 | e.message.includes('execution error') 14 | ) { 15 | return undefined; 16 | } else { 17 | throw e; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/core/src/cli/cli.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/cli/cli.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/cli/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { main } from './validate'; 4 | 5 | const run = async () => { 6 | await main(process.argv.slice(2)); 7 | }; 8 | 9 | void run(); 10 | -------------------------------------------------------------------------------- /packages/core/src/cli/index.ts: -------------------------------------------------------------------------------- 1 | export { ValidateUpgradeSafetyOptions, validateUpgradeSafety } from './validate/validate-upgrade-safety'; 2 | export { ProjectReport } from './validate/project-report'; 3 | export { ReferenceContractNotFound } from './validate/find-contract'; 4 | -------------------------------------------------------------------------------- /packages/core/src/cli/validate/default-exclude.ts: -------------------------------------------------------------------------------- 1 | const UPGRADEABLE_BEACON = '**/contracts/proxy/beacon/UpgradeableBeacon.sol' as const; 2 | 3 | export const defaultExclude: string[] = [UPGRADEABLE_BEACON] as const; 4 | -------------------------------------------------------------------------------- /packages/core/src/cli/validate/error.ts: -------------------------------------------------------------------------------- 1 | import { UpgradesError } from '../..'; 2 | 3 | export class ValidateCommandError extends UpgradesError {} 4 | -------------------------------------------------------------------------------- /packages/core/src/cli/validate/project-report.test.ts.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `src/cli/validate/project-report.test.ts` 2 | 3 | The actual snapshot is saved in `project-report.test.ts.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## get project report - errors - console 8 | 9 | > Snapshot 1 10 | 11 | ` ✘ mypath/MyContract.sol:MyContract␊ 12 | ␊ 13 | MyContract.sol:10: Implementation is missing a public \`upgradeTo(address)\` or \`upgradeToAndCall(address,bytes)\` function␊ 14 | Inherit UUPSUpgradeable to include one or both of these functions in your contract␊ 15 | @openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol␊ 16 | https://zpl.in/upgrades/error-008␊ 17 | ␊ 18 | MyContract.sol:20: Use of delegatecall is not allowed␊ 19 | https://zpl.in/upgrades/error-002␊ 20 | ␊ 21 | ✘ MyContract2 (upgrades from MyContract)␊ 22 | ␊ 23 | file.sol:1: Replaced \`x2\` with \`renamed\` of incompatible type␊ 24 | ␊ 25 | FAILED (2 upgradeable contracts detected, 0 passed, 2 failed)` 26 | 27 | ## get project report - some passed 28 | 29 | > Snapshot 1 30 | 31 | ` ✘ mypath/MyContract.sol:MyContract1␊ 32 | ␊ 33 | MyContract.sol:10: Implementation is missing a public \`upgradeTo(address)\` or \`upgradeToAndCall(address,bytes)\` function␊ 34 | Inherit UUPSUpgradeable to include one or both of these functions in your contract␊ 35 | @openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol␊ 36 | https://zpl.in/upgrades/error-008␊ 37 | ␊ 38 | ✔ mypath/MyContract.sol:MyContract2␊ 39 | ␊ 40 | FAILED (2 upgradeable contracts detected, 1 passed, 1 failed)` 41 | -------------------------------------------------------------------------------- /packages/core/src/cli/validate/project-report.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/cli/validate/project-report.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/cli/validate/project-report.ts: -------------------------------------------------------------------------------- 1 | import { UpgradeableContractReport } from './contract-report'; 2 | import { Report } from '../../standalone'; 3 | 4 | export class ProjectReport implements Report { 5 | constructor( 6 | readonly upgradeableContractReports: UpgradeableContractReport[], 7 | readonly specifiedContract?: boolean, 8 | ) {} 9 | 10 | get ok(): boolean { 11 | return this.upgradeableContractReports.every(r => r.ok); 12 | } 13 | 14 | explain(color = true): string { 15 | if (this.numTotal === 0) { 16 | return 'No upgradeable contracts detected.'; 17 | } else { 18 | const lines = this.upgradeableContractReports.map(r => r.explain(color)); 19 | const status = this.ok ? 'SUCCESS' : 'FAILED'; 20 | 21 | if (this.specifiedContract) { 22 | lines.push(`${status}`); 23 | } else { 24 | const numFailed = this.numTotal - this.numPassed; 25 | const plural = this.numTotal === 1 ? '' : 's'; 26 | lines.push( 27 | `${status} (${this.numTotal} upgradeable contract${plural} detected, ${this.numPassed} passed, ${numFailed} failed)`, 28 | ); 29 | } 30 | return lines.join('\n\n'); 31 | } 32 | } 33 | 34 | /** 35 | * Number of contracts that passed upgrade safety checks. 36 | */ 37 | get numPassed(): number { 38 | return this.upgradeableContractReports.filter(r => r.ok).length; 39 | } 40 | 41 | /** 42 | * Total number of upgradeable contracts detected. 43 | */ 44 | get numTotal(): number { 45 | return this.upgradeableContractReports.length; 46 | } 47 | } 48 | 49 | export function getProjectReport( 50 | upgradeableContractReports: UpgradeableContractReport[], 51 | specifiedContract?: boolean, 52 | ): ProjectReport { 53 | return new ProjectReport(upgradeableContractReports, specifiedContract); 54 | } 55 | -------------------------------------------------------------------------------- /packages/core/src/cli/validate/validate-upgrade-safety.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/cli/validate/validate-upgrade-safety.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/cli/validate/validations.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/cli/validate/validations.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/eip-1967-type.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EIP1967BeaconNotFound, 3 | EIP1967ImplementationNotFound, 4 | getAdminAddress, 5 | getBeaconAddress, 6 | getImplementationAddress, 7 | isEmptySlot, 8 | } from './eip-1967'; 9 | import { EthereumProvider } from './provider'; 10 | 11 | export async function isTransparentOrUUPSProxy(provider: EthereumProvider, address: string): Promise { 12 | try { 13 | await getImplementationAddress(provider, address); 14 | // if an exception was not encountered above, then this address is a transparent/uups proxy 15 | return true; 16 | } catch (e: any) { 17 | if (e instanceof EIP1967ImplementationNotFound) { 18 | return false; 19 | } else { 20 | throw e; 21 | } 22 | } 23 | } 24 | 25 | export async function isTransparentProxy(provider: EthereumProvider, address: string): Promise { 26 | const adminAddress = await getAdminAddress(provider, address); 27 | return !isEmptySlot(adminAddress); 28 | } 29 | 30 | export async function isBeaconProxy(provider: EthereumProvider, address: string): Promise { 31 | try { 32 | await getBeaconAddress(provider, address); 33 | // if an exception was not encountered above, then this address is a beacon proxy 34 | return true; 35 | } catch (e: any) { 36 | if (e instanceof EIP1967BeaconNotFound) { 37 | return false; 38 | } else { 39 | throw e; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/core/src/error.ts: -------------------------------------------------------------------------------- 1 | import util from 'util'; 2 | import chalk from 'chalk'; 3 | 4 | interface ErrorDescriptor { 5 | msg: (e: E) => string; 6 | hint?: (e: E) => string | undefined; 7 | link?: string; 8 | } 9 | 10 | export type ErrorDescriptions = { 11 | [K in E['kind']]: ErrorDescriptor; 12 | }; 13 | 14 | function noDetails() { 15 | return ''; 16 | } 17 | 18 | export class UpgradesError extends Error { 19 | constructor(message: string, details = noDetails) { 20 | super(message + '\n\n' + details()); 21 | } 22 | 23 | [util.inspect.custom](): string { 24 | return chalk.red.bold('Error:') + ' ' + this.message; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/core/src/infer-proxy-admin.ts: -------------------------------------------------------------------------------- 1 | import { callOptionalSignature } from './call-optional-signature'; 2 | import { EthereumProvider } from './provider'; 3 | import { parseAddress } from './utils/address'; 4 | 5 | /** 6 | * Infers whether the address might be a ProxyAdmin contract, by checking if it has an owner() function that returns an address. 7 | * @param provider Ethereum provider 8 | * @param possibleContractAddress The address to check 9 | * @returns true if the address might be a ProxyAdmin contract, false otherwise 10 | */ 11 | export async function inferProxyAdmin(provider: EthereumProvider, possibleContractAddress: string): Promise { 12 | const owner = await callOptionalSignature(provider, possibleContractAddress, 'owner()'); 13 | return owner !== undefined && parseAddress(owner) !== undefined; 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/link-refs.ts: -------------------------------------------------------------------------------- 1 | import { SolcBytecode } from './solc-api'; 2 | 3 | export interface LinkReference { 4 | src: string; 5 | name: string; 6 | length: number; 7 | start: number; 8 | placeholder: string; 9 | } 10 | 11 | export function extractLinkReferences(bytecode: SolcBytecode): LinkReference[] { 12 | const linkRefs: LinkReference[] = []; 13 | const { linkReferences } = bytecode; 14 | for (const source of Object.keys(linkReferences)) { 15 | for (const name of Object.keys(linkReferences[source])) { 16 | for (const { length, start } of linkReferences[source][name]) { 17 | const placeholder = bytecode.object.substr(start * 2, length * 2); 18 | linkRefs.push({ 19 | src: source, 20 | name, 21 | length, 22 | start, 23 | placeholder, 24 | }); 25 | } 26 | } 27 | } 28 | return linkRefs; 29 | } 30 | 31 | export function unlinkBytecode(bytecode: string, linkReferences: LinkReference[]): string { 32 | let unlinkedBytecode: string = bytecode.replace(/^0x/, ''); 33 | for (const linkRef of linkReferences) { 34 | const { length, start, placeholder } = linkRef; 35 | if ((start + length) * 2 <= unlinkedBytecode.length) { 36 | unlinkedBytecode = 37 | unlinkedBytecode.substr(0, start * 2) + placeholder + unlinkedBytecode.substr((start + length) * 2); 38 | } 39 | } 40 | return '0x' + unlinkedBytecode; 41 | } 42 | -------------------------------------------------------------------------------- /packages/core/src/provider.test.ts: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import { EthereumProvider, getTransactionReceipt } from './provider'; 3 | 4 | const hash = '0x1234'; 5 | 6 | function makeProviderReturning(result: unknown): EthereumProvider { 7 | return { send: (_method: string, _params: unknown[]) => Promise.resolve(result) } as EthereumProvider; 8 | } 9 | 10 | test('getTransactionReceipt returns null', async t => { 11 | const provider = makeProviderReturning(null); 12 | t.is(await getTransactionReceipt(provider, hash), null); 13 | }); 14 | 15 | test('getTransactionReceipt status returns 0x0', async t => { 16 | const provider = makeProviderReturning({ status: '0x0' }); 17 | const receipt = await getTransactionReceipt(provider, hash); 18 | t.is(receipt?.status, '0x0'); 19 | }); 20 | 21 | test('getTransactionReceipt status returns 0x1', async t => { 22 | const provider = makeProviderReturning({ status: '0x1' }); 23 | const receipt = await getTransactionReceipt(provider, hash); 24 | t.is(receipt?.status, '0x1'); 25 | }); 26 | 27 | test('getTransactionReceipt status normalizes to 0x0', async t => { 28 | const provider = makeProviderReturning({ status: '0x000000000000000000000000' }); 29 | const receipt = await getTransactionReceipt(provider, hash); 30 | t.is(receipt?.status, '0x0'); 31 | }); 32 | 33 | test('getTransactionReceipt status normalizes to 0x1', async t => { 34 | const provider = makeProviderReturning({ status: '0x000000000000000000000001' }); 35 | const receipt = await getTransactionReceipt(provider, hash); 36 | t.is(receipt?.status, '0x1'); 37 | }); 38 | 39 | test('getTransactionReceipt status returns empty hex', async t => { 40 | const provider = makeProviderReturning({ status: '0x' }); 41 | const receipt = await getTransactionReceipt(provider, hash); 42 | t.is(receipt?.status, '0x'); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/core/src/scripts/migrate-oz-cli-project.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/scripts/migrate-oz-cli-project.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/scripts/project-file.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifestVersion": "2.2", 3 | "contracts": { 4 | "Box": "Box" 5 | }, 6 | "dependencies": {}, 7 | "name": "my-project", 8 | "version": "1.0.0", 9 | "compiler": { 10 | "compilerSettings": { 11 | "optimizer": { 12 | "enabled": false, 13 | "runs": "200" 14 | } 15 | }, 16 | "typechain": { 17 | "enabled": false 18 | }, 19 | "manager": "openzeppelin", 20 | "solcVersion": "0.6.12", 21 | "artifactsDir": "build/contracts", 22 | "contractsDir": "contracts" 23 | }, 24 | "telemetryOptIn": false 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/solc-api.ts: -------------------------------------------------------------------------------- 1 | import type { SourceUnit } from 'solidity-ast'; 2 | import { StorageItem, TypeItem } from './storage'; 3 | 4 | export interface SolcOutput { 5 | contracts: { 6 | [file in string]: { 7 | [contract in string]: { 8 | evm: { 9 | bytecode: SolcBytecode; 10 | methodIdentifiers?: Record; 11 | }; 12 | storageLayout?: { 13 | storage: StorageItem[]; 14 | types: Record; 15 | }; 16 | }; 17 | }; 18 | }; 19 | sources: { 20 | [file in string]: { 21 | ast: SourceUnit; 22 | id: number; 23 | }; 24 | }; 25 | errors?: { 26 | severity: 'error' | 'warning'; 27 | formattedMessage: string; 28 | sourceLocation?: { 29 | file: string; 30 | }; 31 | }[]; 32 | } 33 | 34 | export interface SolcInput { 35 | sources: { 36 | [source in string]: { 37 | content?: string; 38 | }; 39 | }; 40 | settings?: { 41 | outputSelection?: { 42 | [file in string]: { 43 | [contract in string]: string[]; 44 | }; 45 | }; 46 | }; 47 | } 48 | 49 | export type SolcLinkReferences = { 50 | [file in string]: { 51 | [library in string]: { 52 | length: number; 53 | start: number; 54 | }[]; 55 | }; 56 | }; 57 | 58 | export interface SolcBytecode { 59 | linkReferences: SolcLinkReferences; 60 | object: string; 61 | } 62 | -------------------------------------------------------------------------------- /packages/core/src/solidity-version.json: -------------------------------------------------------------------------------- 1 | "0.8.29" -------------------------------------------------------------------------------- /packages/core/src/src-decoder.test.ts: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import { SolcInput, SolcOutput } from './solc-api'; 4 | import { solcInputOutputDecoder } from './src-decoder'; 5 | 6 | test('solcInputOutputDecoder', t => { 7 | const solcInput: SolcInput = { 8 | sources: { 9 | 'a.sol': { content: '\n\n' }, 10 | 'b.sol': { content: '\n\n' }, 11 | }, 12 | }; 13 | 14 | const solcOutput: SolcOutput = { 15 | contracts: {}, 16 | sources: { 17 | 'a.sol': { id: 0, ast: undefined as any }, 18 | 'b.sol': { id: 1, ast: undefined as any }, 19 | }, 20 | }; 21 | 22 | const decodeSrc = solcInputOutputDecoder(solcInput, solcOutput); 23 | 24 | t.is(decodeSrc({ src: '0:0:0' }), 'a.sol:1'); 25 | t.is(decodeSrc({ src: '1:0:0' }), 'a.sol:2'); 26 | t.is(decodeSrc({ src: '1:0:1' }), 'b.sol:2'); 27 | t.is(decodeSrc({ src: '2:0:1' }), 'b.sol:3'); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/core/src/src-decoder.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { SolcOutput, SolcInput } from './solc-api'; 3 | 4 | export type SrcDecoder = (node: { src: string }) => string; 5 | 6 | interface Source { 7 | name: string; 8 | content: string; 9 | } 10 | 11 | export function solcInputOutputDecoder(solcInput: SolcInput, solcOutput: SolcOutput, basePath = '.'): SrcDecoder { 12 | const sources: Record = {}; 13 | 14 | function getSource(sourceId: number): Source { 15 | if (sourceId in sources) { 16 | return sources[sourceId]; 17 | } else { 18 | const sourcePath = Object.entries(solcOutput.sources).find(([, { id }]) => sourceId === id)?.[0]; 19 | if (sourcePath === undefined) { 20 | throw new Error(`Source file not available`); 21 | } 22 | const content = solcInput.sources[sourcePath]?.content; 23 | const name = path.relative(basePath, sourcePath); 24 | if (content === undefined) { 25 | throw new Error(`Content for ${name} not available`); 26 | } 27 | return (sources[sourceId] = { name, content }); 28 | } 29 | } 30 | 31 | return ({ src }) => { 32 | const [begin, , sourceId] = src.split(':').map(Number); 33 | const { name, content } = getSource(sourceId); 34 | const line = content.substr(0, begin).split('\n').length; 35 | return name + ':' + line; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /packages/core/src/standalone.ts: -------------------------------------------------------------------------------- 1 | import { SolcOutput, SolcInput } from './solc-api'; 2 | import { solcInputOutputDecoder } from './src-decoder'; 3 | import { 4 | getContractVersion, 5 | getErrors, 6 | getStorageLayout, 7 | UpgradeableContractErrorReport, 8 | validate, 9 | ValidationOptions, 10 | withValidationDefaults, 11 | } from './validate'; 12 | import { getStorageUpgradeReport, StorageLayout } from './storage'; 13 | import { Version } from './version'; 14 | import { ValidationError } from './validate/run'; 15 | 16 | export interface Report { 17 | ok: boolean; 18 | explain(color?: boolean): string; 19 | } 20 | 21 | /** 22 | * @deprecated Use `validateUpgradeSafety` instead. 23 | */ 24 | export class UpgradeableContract { 25 | readonly version: Version; 26 | readonly errors: ValidationError[]; 27 | readonly layout: StorageLayout; 28 | 29 | constructor( 30 | readonly name: string, 31 | solcInput: SolcInput, 32 | solcOutput: SolcOutput, 33 | opts: ValidationOptions = {}, 34 | solcVersion?: string, 35 | ) { 36 | const decodeSrc = solcInputOutputDecoder(solcInput, solcOutput); 37 | const validation = validate(solcOutput, decodeSrc, solcVersion, solcInput); 38 | this.version = getContractVersion(validation, name); 39 | this.errors = getErrors(validation, this.version, withValidationDefaults(opts)); 40 | this.layout = getStorageLayout(validation, this.version); 41 | } 42 | 43 | getErrorReport() { 44 | return new UpgradeableContractErrorReport(this.errors); 45 | } 46 | 47 | getStorageUpgradeReport(newVersion: UpgradeableContract, opts: ValidationOptions = {}) { 48 | return getStorageUpgradeReport(this.layout, newVersion.layout, withValidationDefaults(opts)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/core/src/storage-0.8.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/storage-0.8.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/storage-custom-layout.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/storage-custom-layout.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/storage-memory-0.5.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/storage-memory-0.5.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/storage-namespaced-conflicts-layout.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/storage-namespaced-conflicts-layout.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/storage-namespaced-conflicts.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/storage-namespaced-conflicts.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/storage-namespaced-layout.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/storage-namespaced-layout.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/storage-namespaced-outside-contract.test.ts: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import { artifacts } from 'hardhat'; 3 | 4 | import { validate } from './validate'; 5 | import { solcInputOutputDecoder } from './src-decoder'; 6 | 7 | test('namespace outside contract - error', async t => { 8 | const contract = 'contracts/test/NamespacedOutsideContract.sol:Example'; 9 | 10 | const { solcOutput, decodeSrc } = await getOutputAndDecoder(contract); 11 | 12 | const error = t.throws(() => validate(solcOutput, decodeSrc)); 13 | t.assert( 14 | error?.message.includes( 15 | 'contracts/test/NamespacedOutsideContract.sol:7: Namespace struct MainStorage is defined outside of a contract', 16 | ), 17 | error?.message, 18 | ); 19 | }); 20 | 21 | test('namespace in library - warning', async t => { 22 | const contract = 'contracts/test/NamespacedInLibrary.sol:UsesLibraryWithNamespace'; 23 | 24 | const { solcOutput, decodeSrc } = await getOutputAndDecoder(contract); 25 | validate(solcOutput, decodeSrc); 26 | 27 | t.pass(); 28 | }); 29 | 30 | async function getOutputAndDecoder(contract: string) { 31 | const buildInfo = await artifacts.getBuildInfo(contract); 32 | if (buildInfo === undefined) { 33 | throw new Error(`Build info not found for contract ${contract}`); 34 | } 35 | const solcOutput = buildInfo.output; 36 | const solcInput = buildInfo.input; 37 | const decodeSrc = solcInputOutputDecoder(solcInput, solcOutput); 38 | return { solcOutput, decodeSrc }; 39 | } 40 | -------------------------------------------------------------------------------- /packages/core/src/storage-namespaced.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/storage-namespaced.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/storage.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/storage.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/storage/compat.ts: -------------------------------------------------------------------------------- 1 | export { StorageItem, StorageLayout, TypeItem, TypeItemMembers, StructMember } from './layout'; 2 | export { extractStorageLayout } from './extract'; 3 | export { decodeTypeIdentifier, stabilizeTypeIdentifier } from '../utils/type-id'; 4 | -------------------------------------------------------------------------------- /packages/core/src/storage/gap.ts: -------------------------------------------------------------------------------- 1 | import { StorageField, storageFieldEnd } from './compare'; 2 | 3 | /** 4 | * Returns true if the field represents a storage gap. 5 | * 6 | * @param field the storage field 7 | * @returns true if field is a gap, otherwise false 8 | */ 9 | export function isGap(field: StorageField): boolean { 10 | return field.type.head === 't_array' && (field.label === '__gap' || field.label.startsWith('__gap_')); 11 | } 12 | 13 | /** 14 | * Returns true if original storage field is a gap and the updated storage field 15 | * ends at the exact same position as the gap. 16 | * 17 | * @param original the original storage field 18 | * @param updated the updated storage field 19 | * @returns true if original is a gap and original and updated end at the same position, otherwise false 20 | */ 21 | export function endMatchesGap(original: StorageField, updated: StorageField) { 22 | const originalEnd = storageFieldEnd(original); 23 | const updatedEnd = storageFieldEnd(updated); 24 | 25 | return isGap(original) && originalEnd !== undefined && updatedEnd !== undefined && originalEnd === updatedEnd; 26 | } 27 | -------------------------------------------------------------------------------- /packages/core/src/storage/report-gap.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/storage/report-gap.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/storage/report-rename-retype.test.ts.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `src/storage/report-rename-retype.test.ts` 2 | 3 | The actual snapshot is saved in `report-rename-retype.test.ts.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## successful rename 8 | 9 | > Snapshot 1 10 | 11 | '' 12 | 13 | ## successful retype 14 | 15 | > Snapshot 1 16 | 17 | '' 18 | 19 | ## wrongly reported retype 20 | 21 | > Snapshot 1 22 | 23 | `file.sol:1: Upgraded \`x\` to an incompatible type␊ 24 | - Bad upgrade from bool to uint8` 25 | 26 | ## rightly reported retype but incompatible new type 27 | 28 | > Snapshot 1 29 | 30 | `file.sol:1: Layout changed for \`x\` (bool -> bytes32)␊ 31 | - Number of bytes changed from 1 to 32` 32 | 33 | ## confusing bad retype 34 | 35 | > Snapshot 1 36 | 37 | `file.sol:1: Upgraded \`x\` to an incompatible type␊ 38 | - Bad upgrade from bool to uint8` 39 | 40 | ## non-hardcoded retype 41 | 42 | > Snapshot 1 43 | 44 | '' 45 | 46 | ## retype with layout change 47 | 48 | > Snapshot 1 49 | 50 | `file.sol:1: Layout changed for \`a\` (bool -> uint16)␊ 51 | - Number of bytes changed from 1 to 2` 52 | -------------------------------------------------------------------------------- /packages/core/src/storage/report-rename-retype.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/storage/report-rename-retype.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/storage/report.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/storage/report.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/upgrade-interface-version.ts: -------------------------------------------------------------------------------- 1 | import debug from './utils/debug'; 2 | import { callOptionalSignature } from './call-optional-signature'; 3 | import { EthereumProvider } from './provider'; 4 | 5 | export async function getUpgradeInterfaceVersion( 6 | provider: EthereumProvider, 7 | address: string, 8 | log = debug, 9 | ): Promise { 10 | const encodedVersion = await callOptionalSignature(provider, address, 'UPGRADE_INTERFACE_VERSION()'); 11 | if (encodedVersion !== undefined) { 12 | // Encoded string 13 | const buf = Buffer.from(encodedVersion.replace(/^0x/, ''), 'hex'); 14 | 15 | // The first 32 bytes represent the offset, which should be 32 for a string 16 | const offset = parseInt(buf.slice(0, 32).toString('hex'), 16); 17 | if (offset !== 32) { 18 | // Log as debug and return undefined if the interface version is not a string. 19 | // Do not throw an error because this could be caused by a fallback function. 20 | log(`Unexpected type for UPGRADE_INTERFACE_VERSION at address ${address}. Expected a string`); 21 | return undefined; 22 | } 23 | 24 | // The next 32 bytes represent the length of the string 25 | const length = parseInt(buf.slice(32, 64).toString('hex'), 16); 26 | 27 | // The rest is the string itself 28 | return buf.slice(64, 64 + length).toString('utf8'); 29 | } else { 30 | return undefined; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/core/src/utils/address.ts: -------------------------------------------------------------------------------- 1 | import { toChecksumAddress } from 'ethereumjs-util'; 2 | 3 | /** 4 | * Parses an address from a hex string which may come from storage or a returned address via eth_call. 5 | * 6 | * @param addressString The address hex string. 7 | * @returns The parsed checksum address, or undefined if the input string is not an address. 8 | */ 9 | export function parseAddress(addressString: string): string | undefined { 10 | const buf = Buffer.from(addressString.replace(/^0x/, ''), 'hex'); 11 | if (!buf.slice(0, 12).equals(Buffer.alloc(12, 0)) || buf.length !== 32) { 12 | return undefined; 13 | } 14 | const address = '0x' + buf.toString('hex', 12, 32); // grab the last 20 bytes 15 | return toChecksumAddress(address); 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/utils/assert.ts: -------------------------------------------------------------------------------- 1 | export function assertUnreachable(_: never): never { 2 | assert(false); 3 | } 4 | 5 | export function assert(p: unknown): asserts p { 6 | if (!p) { 7 | throw new Error('An unexpected condition occurred. Please report this at https://zpl.in/upgrades/report'); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/utils/contract-name.ts: -------------------------------------------------------------------------------- 1 | export function getFullyQualifiedName(source: string, contractName: string) { 2 | return `${source}:${contractName}`; 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/src/utils/curry.ts: -------------------------------------------------------------------------------- 1 | export interface Curried { 2 | (a: A): (b: B) => T; 3 | (a: A, b: B): T; 4 | } 5 | 6 | export function curry2(fn: (a: A, b: B) => T): Curried { 7 | function curried(a: A): (b: B) => T; 8 | function curried(a: A, b: B): T; 9 | function curried(a: A, ...b: [] | [B]): T | ((b: B) => T) { 10 | if (b.length === 0) { 11 | return b => fn(a, b); 12 | } else { 13 | return fn(a, ...b); 14 | } 15 | } 16 | return curried; 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/utils/debug.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | 3 | export default debug('@openzeppelin:upgrades:core'); 4 | -------------------------------------------------------------------------------- /packages/core/src/utils/deep-array.test.ts: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import { deepEqual } from './deep-array'; 4 | 5 | test('depth 0', t => { 6 | t.true(deepEqual('a', 'a')); 7 | t.false(deepEqual('a', 'b')); 8 | t.false(deepEqual('a', ['a'])); 9 | t.false(deepEqual(['a'], 'a')); 10 | }); 11 | 12 | test('depth 1', t => { 13 | t.true(deepEqual(['a', 'b'], ['a', 'b'])); 14 | t.false(deepEqual(['a'], ['a', 'b'])); 15 | t.false(deepEqual(['a', 'b'], ['a', 'c'])); 16 | }); 17 | 18 | test('depth 2', t => { 19 | t.true(deepEqual([['a'], ['b']], [['a'], ['b']])); 20 | t.false(deepEqual([['a'], ['b']], [['a'], ['c']])); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/core/src/utils/deep-array.ts: -------------------------------------------------------------------------------- 1 | export type DeepArray = T | DeepArray[]; 2 | 3 | export function deepEqual(a: DeepArray, b: DeepArray): boolean { 4 | if (Array.isArray(a) && Array.isArray(b)) { 5 | return a.length === b.length && a.every((v, i) => deepEqual(v, b[i])); 6 | } else { 7 | return a === b; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/utils/erc7201.test.ts: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import { calculateERC7201StorageLocation } from './erc7201'; 4 | 5 | test('calculateERC7201StorageLocation', t => { 6 | t.is( 7 | calculateERC7201StorageLocation('example.main'), 8 | '0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500', 9 | ); 10 | }); 11 | -------------------------------------------------------------------------------- /packages/core/src/utils/erc7201.ts: -------------------------------------------------------------------------------- 1 | import { keccak256 } from 'ethereumjs-util'; 2 | 3 | export const ERC7201_FORMULA_PREFIX = 'erc7201:'; 4 | 5 | /** 6 | * Returns the ERC-7201 storage location hash for a given namespace id using formula: 7 | * keccak256(abi.encode(uint256(keccak256(bytes(id))) - 1)) & ~bytes32(uint256(0xff)) 8 | * 9 | * @param id The namespace id, not including the 'erc7201:' prefix 10 | * @returns The ERC-7201 storage location hash as a hex string 11 | */ 12 | export function calculateERC7201StorageLocation(id: string): string { 13 | const firstHash = keccak256(Buffer.from(id)); 14 | const minusOne = BigInt(`0x${firstHash.toString('hex')}`) - 1n; 15 | const minusOneBuffer = Buffer.from(minusOne.toString(16), 'hex'); 16 | 17 | const secondHash = keccak256(minusOneBuffer); 18 | 19 | const mask = BigInt('0xff'); 20 | const masked = BigInt(`0x${secondHash.toString('hex')}`) & ~mask; 21 | 22 | const padded = masked.toString(16).padStart(64, '0'); 23 | 24 | return `0x${padded}`; 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/utils/execall.ts: -------------------------------------------------------------------------------- 1 | export function* execall(re: RegExp, text: string) { 2 | re = new RegExp(re, re.flags + (re.sticky ? '' : 'y')); 3 | while (true) { 4 | const match = re.exec(text); 5 | if (match && match[0] !== '') { 6 | yield match; 7 | } else { 8 | break; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/utils/function.test.ts: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import { artifacts } from 'hardhat'; 3 | import { FunctionDefinition } from 'solidity-ast'; 4 | import { findAll, astDereferencer } from 'solidity-ast/utils'; 5 | 6 | import { getFunctionSignature } from './function'; 7 | import { SolcOutput } from '../solc-api'; 8 | 9 | testContract('ContractFunctionSignatures'); 10 | testContract('LibraryFunctionSignatures'); 11 | 12 | function testContract(contractName: string) { 13 | test(contractName, async t => { 14 | const fileName = 'contracts/test/FunctionSignatures.sol'; 15 | const buildInfo = await artifacts.getBuildInfo(`${fileName}:${contractName}`); 16 | if (buildInfo === undefined) { 17 | throw new Error('Build info not found'); 18 | } 19 | 20 | const solcOutput: SolcOutput = buildInfo.output; 21 | 22 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 23 | const signatures = Object.keys(solcOutput.contracts[fileName][contractName].evm.methodIdentifiers!); 24 | 25 | const functions: Record = {}; 26 | for (const def of findAll('FunctionDefinition', solcOutput.sources[fileName].ast)) { 27 | functions[def.name] = def; 28 | } 29 | const deref = astDereferencer(solcOutput); 30 | 31 | for (const signature of signatures) { 32 | const name = signature.replace(/\(.*/, ''); 33 | t.is(getFunctionSignature(functions[name], deref), signature); 34 | } 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /packages/core/src/utils/indent.ts: -------------------------------------------------------------------------------- 1 | export function indent(text: string, amount: number, amount0 = amount): string { 2 | return text.replace(/^/gm, (_, i) => ' '.repeat(i === 0 ? amount0 : amount)); 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/src/utils/integer-literals.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js'; 2 | 3 | /** 4 | * Converts a Solidity unsigned integer literal according to https://docs.soliditylang.org/en/latest/types.html#rational-literals 5 | * to a 64-byte padded hex string. 6 | * 7 | * Accepts decimal, hexadecimal, and scientific notation formats. 8 | * 9 | * If the input is null or undefined, returns '0x0000000000000000000000000000000000000000000000000000000000000000'. 10 | * 11 | * Assumes the input is a valid uint256-compatible literal for Solidity, and only performs minimal validation. 12 | * 13 | * @param literal Integer literal to convert 14 | * @returns Hex string with 0x prefix, padded to 64 bytes with leading zeroes 15 | */ 16 | export function normalizeUint256Literal(literal: string | null | undefined): string { 17 | if (!literal) { 18 | return '0x0000000000000000000000000000000000000000000000000000000000000000'; 19 | } 20 | const lowercaseNoUnderscores = literal.replace(/_/g, '').toLowerCase(); 21 | 22 | const parsed = new BigNumber(lowercaseNoUnderscores); 23 | if (!parsed.isInteger()) { 24 | throw new Error(`Invalid integer literal: ${literal}`); 25 | } 26 | const calculatedValueAsHex = parsed.toString(16); 27 | const padded = calculatedValueAsHex.padStart(64, '0'); 28 | return `0x${padded}`; 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/src/utils/is-nullish.test.ts: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import { isNullish } from './is-nullish'; 4 | 5 | test('null', t => { 6 | t.true(isNullish(null)); 7 | }); 8 | 9 | test('undefined', t => { 10 | t.true(isNullish(undefined)); 11 | }); 12 | 13 | test('number 5', t => { 14 | t.false(isNullish(5)); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/core/src/utils/is-nullish.ts: -------------------------------------------------------------------------------- 1 | export function isNullish(value: unknown): value is null | undefined { 2 | return value === null || value === undefined; 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/src/utils/is-value-type.ts: -------------------------------------------------------------------------------- 1 | import { ParsedTypeDetailed } from '../storage/layout'; 2 | 3 | export function isValueType(type: ParsedTypeDetailed): boolean { 4 | return type.args === undefined || ['t_contract', 't_enum', 't_userDefinedValueType'].includes(type.head); 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/utils/itemize.ts: -------------------------------------------------------------------------------- 1 | // itemize returns a markdown-like list of the items 2 | 3 | // itemize['a\nb', 'c'] = 4 | // - a 5 | // b 6 | // - c 7 | 8 | import { indent } from './indent'; 9 | 10 | export function itemize(...items: string[]): string { 11 | return itemizeWith('-', ...items); 12 | } 13 | 14 | export function itemizeWith(bullet: string, ...items: string[]): string { 15 | return items.map(item => bullet + indent(item, 2, 1)).join('\n'); 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/utils/log.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | import { indent } from './indent'; 4 | 5 | let silenced = false; 6 | 7 | function log(prefix: string, title: string, lines: string[]): void { 8 | if (silenced) { 9 | return; 10 | } 11 | 12 | const parts = [chalk.yellow.bold(prefix + ':') + ' ' + title + '\n']; 13 | 14 | if (lines.length > 0) { 15 | parts.push(lines.map(l => indent(l, 4) + '\n').join('')); 16 | } 17 | 18 | console.error(parts.join('\n')); 19 | } 20 | 21 | export function logNote(title: string, lines: string[] = []): void { 22 | log('Note', title, lines); 23 | } 24 | 25 | export function logWarning(title: string, lines: string[] = []): void { 26 | log('Warning', title, lines); 27 | } 28 | 29 | export function silenceWarnings(): void { 30 | logWarning(`All subsequent Upgrades warnings will be silenced.`, [ 31 | `Make sure you have manually checked all uses of unsafe flags.`, 32 | ]); 33 | silenced = true; 34 | } 35 | -------------------------------------------------------------------------------- /packages/core/src/utils/make-namespaced.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/utils/make-namespaced.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/utils/make-non-enumerable.ts: -------------------------------------------------------------------------------- 1 | export function makeNonEnumerable(obj: O, key: keyof O): void { 2 | Object.defineProperty(obj, key, { enumerable: false }); 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/src/utils/map-values.ts: -------------------------------------------------------------------------------- 1 | export function mapValues(obj: Record, fn: (value: V) => W): Record { 2 | const res: Partial> = {}; 3 | for (const k in obj) { 4 | res[k] = fn(obj[k]); 5 | } 6 | return res as Record; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/utils/parse-type-id.test.ts: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import { parseTypeId } from './parse-type-id'; 4 | 5 | const fixtures = [ 6 | 't_uint256', 7 | 't_enum(MyEnum)', 8 | 't_struct(MyComplexStruct)storage', 9 | 't_array(t_uint256)3_storage', 10 | 't_mapping(t_uint256,t_uint256)', 11 | 't_mapping(unknown,t_uint256)', 12 | 't_mapping(t_uint256,t_array(t_bool)dyn_storage)', 13 | 't_mapping(t_uint256,t_mapping(t_string_memory_ptr,t_address))', 14 | 't_function_internal_nonpayable(t_uint256)returns()', 15 | 't_array(t_function_internal_nonpayable(t_uint256)returns(t_address))10_storage', 16 | ]; 17 | 18 | for (const f of fixtures) { 19 | test(f, t => { 20 | t.snapshot(parseTypeId(f)); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/src/utils/parse-type-id.test.ts.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/core/src/utils/parse-type-id.test.ts.snap -------------------------------------------------------------------------------- /packages/core/src/utils/pick.ts: -------------------------------------------------------------------------------- 1 | export function pick(obj: T, keys: K[]): Pick { 2 | const res: Partial> = {}; 3 | for (const k of keys) { 4 | res[k] = obj[k]; 5 | } 6 | return res as Pick; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/utils/stabilize-layout.ts: -------------------------------------------------------------------------------- 1 | import { StorageLayout } from '../storage'; 2 | import { isEnumMembers } from '../storage/layout'; 3 | import { stabilizeTypeIdentifier } from './type-id'; 4 | 5 | export function stabilizeStorageLayout(layout: StorageLayout) { 6 | return { 7 | storage: layout.storage.map(s => ({ ...s, type: stabilizeTypeIdentifier(s.type) })), 8 | types: Object.entries(layout.types).map(([type, item]) => { 9 | const members = 10 | item.members && 11 | (isEnumMembers(item.members) 12 | ? item.members 13 | : item.members.map(m => ({ ...m, type: stabilizeTypeIdentifier(m.type) }))); 14 | return [stabilizeTypeIdentifier(type), { ...item, members }]; 15 | }), 16 | namespaces: layout.namespaces 17 | ? Object.entries(layout.namespaces).map(([ns, items]) => { 18 | return [ns, items.map(item => ({ ...item, type: stabilizeTypeIdentifier(item.type) }))]; 19 | }) 20 | : undefined, 21 | baseSlot: layout.baseSlot, 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /packages/core/src/validate/compat.ts: -------------------------------------------------------------------------------- 1 | // Legacy interface for backwards compatibility 2 | 3 | import { ContractValidation, ValidationRunData } from './run'; 4 | 5 | export type RunValidation = ValidationRunData; 6 | export type ValidationLog = RunValidation[]; 7 | export type Validation = RunValidation; 8 | 9 | export type ValidationResult = ContractValidation; 10 | -------------------------------------------------------------------------------- /packages/core/src/validate/data.ts: -------------------------------------------------------------------------------- 1 | import * as versions from 'compare-versions'; 2 | 3 | import { ValidationRunData } from './run'; 4 | 5 | type ValidationDataV1 = ValidationRunData; 6 | 7 | type ValidationDataV2 = ValidationRunData[]; 8 | 9 | const currentMajor = '3'; 10 | const currentVersion = '3.4'; 11 | 12 | interface ValidationDataV3 { 13 | version: '3' | '3.1' | '3.2' | '3.3' | '3.4'; 14 | log: ValidationRunData[]; 15 | } 16 | 17 | export type ValidationDataCurrent = ValidationDataV3; 18 | 19 | export type ValidationData = ValidationDataV1 | ValidationDataV2 | ValidationDataV3; 20 | 21 | export function normalizeValidationData(data: ValidationData): ValidationDataCurrent { 22 | if (isCurrentValidationData(data, false)) { 23 | return data; 24 | } else if (Array.isArray(data)) { 25 | return { version: currentVersion, log: data }; 26 | } else { 27 | return { version: currentVersion, log: [data] }; 28 | } 29 | } 30 | 31 | export function isCurrentValidationData(data: ValidationData, exact = true): data is ValidationDataCurrent { 32 | if (Array.isArray(data)) { 33 | return false; 34 | } else if (!('version' in data)) { 35 | return false; 36 | } else if (typeof data.version === 'string' && versions.validate(data.version)) { 37 | if (exact) { 38 | return data.version === currentVersion; 39 | } else { 40 | return versions.compare(data.version, `${currentMajor}.*`, '='); 41 | } 42 | } else { 43 | throw new Error('Unknown version or malformed validation data'); 44 | } 45 | } 46 | 47 | export function concatRunData( 48 | newRunData: ValidationRunData, 49 | previousData?: ValidationDataCurrent, 50 | ): ValidationDataCurrent { 51 | return { 52 | version: currentVersion, 53 | log: [newRunData].concat(previousData?.log ?? []), 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /packages/core/src/validate/error.ts: -------------------------------------------------------------------------------- 1 | import { ValidationError } from './run'; 2 | import { UpgradesError } from '../error'; 3 | import { UpgradeableContractErrorReport } from './report'; 4 | 5 | export class ValidationErrors extends UpgradesError { 6 | constructor( 7 | contractName: string, 8 | readonly errors: ValidationError[], 9 | ) { 10 | super(`Contract \`${contractName}\` is not upgrade safe`, () => 11 | new UpgradeableContractErrorReport(errors).explain(), 12 | ); 13 | } 14 | } 15 | 16 | export class ContractSourceNotFoundError extends UpgradesError { 17 | constructor() { 18 | super('The requested contract was not found. Make sure the source code is available for compilation'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/core/src/validate/index.ts: -------------------------------------------------------------------------------- 1 | export { validate, ValidationRunData, ContractValidation } from './run'; 2 | export { ProxyKindOption, StandaloneValidationOptions, ValidationOptions, withValidationDefaults } from './overrides'; 3 | export { ValidationErrors, ContractSourceNotFoundError } from './error'; 4 | export { RunValidation, ValidationLog, Validation, ValidationResult } from './compat'; 5 | export { ValidationData, ValidationDataCurrent, isCurrentValidationData, concatRunData } from './data'; 6 | export { 7 | getContractVersion, 8 | getContractNameAndRunValidation, 9 | getStorageLayout, 10 | assertUpgradeSafe, 11 | getUnlinkedBytecode, 12 | getErrors, 13 | isUpgradeSafe, 14 | inferProxyKind, 15 | inferInitializable, 16 | } from './query'; 17 | export { UpgradeableContractErrorReport } from './report'; 18 | 19 | // Backwards compatibility 20 | export { silenceWarnings } from '../utils/log'; 21 | -------------------------------------------------------------------------------- /packages/core/src/validate/query.test.ts: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import { ContractValidation, ValidationRunData } from './run'; 4 | import { getUnlinkedBytecode } from './query'; 5 | import { getVersion } from '../version'; 6 | 7 | test('getUnlinkedBytecode', t => { 8 | const unlinkedBytecode = '0x12__$5ae0c2211b657f8a7ca51e0b14f2a8333d$__78'; 9 | const linkedBytecode = '0x12456456456456456456456456456456456456456478'; 10 | 11 | const validation: Record> = { 12 | B: { 13 | version: getVersion(unlinkedBytecode), 14 | linkReferences: [ 15 | { 16 | src: '', 17 | name: '', 18 | start: 50, 19 | length: 20, 20 | placeholder: '__$5ae0c2211b657f8a7ca51e0b14f2a8333d$__', 21 | }, 22 | { 23 | src: '', 24 | name: '', 25 | start: 30, 26 | length: 20, 27 | placeholder: '__$5ae0c2211b657f8a7ca51e0b14f2a8333d$__', 28 | }, 29 | ], 30 | }, 31 | A: { 32 | version: getVersion(unlinkedBytecode), 33 | linkReferences: [ 34 | { 35 | src: '', 36 | name: '', 37 | start: 1, 38 | length: 20, 39 | placeholder: '__$5ae0c2211b657f8a7ca51e0b14f2a8333d$__', 40 | }, 41 | ], 42 | }, 43 | }; 44 | 45 | const recovered = getUnlinkedBytecode(validation as ValidationRunData, linkedBytecode); 46 | 47 | t.is(recovered, unlinkedBytecode); 48 | }); 49 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "outDir": "dist", 6 | "rootDir": "src", 7 | "resolveJsonModule": true, 8 | }, 9 | "include": [ 10 | "src/**/*.ts", 11 | "src/**/*.json" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/plugin-defender-hardhat/assets/approve-upgrade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/plugin-defender-hardhat/assets/approve-upgrade.png -------------------------------------------------------------------------------- /packages/plugin-defender-hardhat/assets/verified-deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/plugin-defender-hardhat/assets/verified-deploy.png -------------------------------------------------------------------------------- /packages/plugin-hardhat/ava.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | workerThreads: false, 3 | files: ['test/*.js'], 4 | watchMode: { 5 | ignoreChanges: ['**/*.ts', '.openzeppelin'], 6 | }, 7 | serial: true, 8 | failWithoutAssertions: false, 9 | snapshotDir: '.', 10 | environmentVariables: { 11 | FORCE_COLOR: '0', 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/AccessManagedProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | // Example of a custom proxy. 5 | // This contract is for testing only. 6 | 7 | import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 8 | import {AccessManager} from "@openzeppelin/contracts/access/manager/AccessManager.sol"; 9 | import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol"; 10 | import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol"; 11 | 12 | contract AccessManagedProxy is ERC1967Proxy { 13 | IAccessManager public immutable ACCESS_MANAGER; 14 | 15 | constructor(address implementation, bytes memory _data, IAccessManager manager) payable ERC1967Proxy(implementation, _data) { 16 | ACCESS_MANAGER = manager; 17 | } 18 | 19 | /** 20 | * @dev Checks with the ACCESS_MANAGER if msg.sender is authorized to call the current call's function, 21 | * and if so, delegates the current call to `implementation`. 22 | * 23 | * This function does not return to its internal call site, it will return directly to the external caller. 24 | */ 25 | function _delegate(address implementation) internal virtual override { 26 | (bool immediate, ) = ACCESS_MANAGER.canCall(msg.sender, address(this), bytes4(msg.data[0:4])); 27 | if (!immediate) revert IAccessManaged.AccessManagedUnauthorized(msg.sender); 28 | super._delegate(implementation); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/Action.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | contract Action { 4 | enum ActionType { UP, DOWN } 5 | event ActionEvent(ActionType actionType); 6 | 7 | ActionType action; 8 | 9 | function log() public { 10 | emit ActionEvent(action); 11 | } 12 | 13 | } 14 | 15 | import "./utils/Proxiable.sol"; 16 | contract ActionProxiable is Action, Proxiable {} 17 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/ActionV2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | contract ActionV2 { 4 | enum ActionType { UP, DOWN, LEFT, RIGHT } 5 | event ActionEvent(ActionType actionType); 6 | 7 | ActionType action; 8 | 9 | function log() public { 10 | emit ActionEvent(action); 11 | } 12 | 13 | } 14 | 15 | contract ActionV2Bad { 16 | enum ActionType { UP, LEFT, RIGHT } 17 | event ActionEvent(ActionType actionType); 18 | 19 | ActionType action; 20 | 21 | function log() public { 22 | emit ActionEvent(action); 23 | } 24 | } 25 | 26 | import "./utils/Proxiable.sol"; 27 | contract ActionV2Proxiable is ActionV2, Proxiable {} 28 | contract ActionV2BadProxiable is ActionV2Bad, Proxiable {} 29 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/Adder.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | contract Adder { 4 | uint n; 5 | 6 | function initialize() public view { 7 | } 8 | 9 | function add(uint x) public returns (uint) { 10 | n = n + x; 11 | return n; 12 | } 13 | 14 | } 15 | 16 | import "./utils/Proxiable.sol"; 17 | contract AdderProxiable is Adder, Proxiable {} 18 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/AdderV2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | library SafeAdd { 4 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 5 | uint256 c = a + b; 6 | require(c >= a, "SafeMath: addition overflow"); 7 | 8 | return c; 9 | } 10 | } 11 | 12 | contract AdderV2 { 13 | using SafeAdd for uint; 14 | 15 | uint n; 16 | 17 | function initialize() public view { 18 | } 19 | 20 | function add(uint x) public returns (uint) { 21 | n = n.add(x); 22 | return n; 23 | } 24 | 25 | } 26 | 27 | import "./utils/Proxiable.sol"; 28 | contract AdderV2Proxiable is AdderV2, Proxiable {} 29 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/Beacon.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | // This contract is for testing only, it is not safe for use in production. 4 | 5 | contract Beacon { 6 | address private _implementation; 7 | 8 | constructor(address implementation_) { 9 | _setImplementation(implementation_); 10 | } 11 | 12 | function implementation() public view virtual returns (address) { 13 | return _implementation; 14 | } 15 | 16 | function upgradeTo(address newImplementation) public virtual { 17 | _setImplementation(newImplementation); 18 | } 19 | 20 | function _setImplementation(address newImplementation) private { 21 | _implementation = newImplementation; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/Constructor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract WithConstructor { 5 | /// @custom:oz-upgrades-unsafe-allow state-variable-immutable 6 | uint256 public immutable value; 7 | 8 | /// @custom:oz-upgrades-unsafe-allow constructor 9 | constructor(uint256 args) { 10 | value = args; 11 | } 12 | } 13 | 14 | contract WithConstructorArray { 15 | uint256[] public x; 16 | 17 | /// @custom:oz-upgrades-unsafe-allow constructor 18 | constructor(uint256[] memory args) { 19 | x = args; 20 | } 21 | } -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/CustomBeaconProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; 6 | 7 | // Example of a custom beacon proxy. 8 | // This contract is for testing only, it is not safe for use in production. 9 | 10 | contract CustomBeaconProxy is BeaconProxy { 11 | address private immutable _deployer; 12 | // The beacon that will be used on calls by the deployer address 13 | address private immutable _deployerBeacon; 14 | 15 | constructor(address beacon, bytes memory data, address deployerBeacon) payable BeaconProxy(beacon, data) { 16 | _deployer = msg.sender; 17 | _deployerBeacon = deployerBeacon; 18 | } 19 | 20 | function _getBeacon() internal view override returns (address) { 21 | if (msg.sender == _deployer) return _deployerBeacon; 22 | return super._getBeacon(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/DeployContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {Initializable as Initializable1} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; 5 | import {Initializable as Initializable2} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 6 | import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 7 | 8 | contract NonUpgradeable { 9 | } 10 | 11 | contract IsInitializable is Initializable1 { 12 | } 13 | 14 | contract IsInitializableUpgradeable is Initializable2 { 15 | } 16 | 17 | // Insecure contract 18 | contract IsUUPS is UUPSUpgradeable { 19 | function _authorizeUpgrade(address newImplementation) 20 | internal 21 | override 22 | {} 23 | } 24 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/DeployOverload.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | contract DeployOverload { 4 | uint public value; 5 | 6 | function customInitialize() public { 7 | value = 42; 8 | } 9 | 10 | } 11 | 12 | import "./utils/Proxiable.sol"; 13 | contract DeployOverloadProxiable is DeployOverload, Proxiable {} 14 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/ExternalLibraries.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | library SafeMath { 4 | function add(uint256 a, uint256 b) external pure returns (uint256) { 5 | uint256 c = a + b; 6 | require(c >= a, "SafeMath: addition overflow"); 7 | 8 | return c; 9 | } 10 | 11 | function sub(uint256 a, uint256 b) external pure returns (uint256) { 12 | require(b <= a, "SafeMath: subtraction overflow"); 13 | uint256 c = a - b; 14 | 15 | return c; 16 | } 17 | 18 | function version() external pure returns (string memory) { 19 | return "V1"; 20 | } 21 | } 22 | 23 | library SafeMathV2 { 24 | function add(uint256 a, uint256 b) external pure returns (uint256) { 25 | uint256 c = a + b; 26 | require(c >= a, "SafeMath: addition overflow"); 27 | 28 | return c; 29 | } 30 | 31 | function sub(uint256 a, uint256 b) external pure returns (uint256) { 32 | require(b <= a, "SafeMath: subtraction overflow"); 33 | uint256 c = a - b; 34 | 35 | return c; 36 | } 37 | 38 | function version() external pure returns (string memory) { 39 | return "V2"; 40 | } 41 | } 42 | 43 | library SafePercent { 44 | function getPercent(uint256 a, uint256 pct) external pure returns (uint256) { 45 | uint256 c = div(mul(a,pct),100); 46 | 47 | return c; 48 | } 49 | 50 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 51 | uint256 c = a * b; 52 | require(c / a == b, "SafePercent: multiplication overflow"); 53 | 54 | return c; 55 | } 56 | 57 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 58 | require(b > 0, "SafePercent: division by zero"); 59 | uint256 c = a / b; 60 | 61 | return c; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/GapV1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | contract GapV1 { 5 | string greeting; 6 | uint256[49] private __gap; 7 | } 8 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/GapV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | contract GapV2 { 5 | string greeting; 6 | uint256 new1; 7 | uint256 new2; 8 | uint256[47] private __gap; 9 | } 10 | 11 | contract GapV2_Bad { 12 | string greeting; 13 | uint256 new1; 14 | uint256 new2; 15 | uint256[48] private __gap; 16 | } 17 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/Greeter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >= 0.4.22 <0.8.0; 2 | 3 | contract Greeter { 4 | 5 | string greeting; 6 | 7 | function initialize(string memory _greeting) public { 8 | greeting = _greeting; 9 | } 10 | 11 | function greet() public view returns (string memory) { 12 | return greeting; 13 | } 14 | 15 | function setGreeting(string memory _greeting) public { 16 | greeting = _greeting; 17 | } 18 | 19 | } 20 | 21 | import "./utils/Proxiable.sol"; 22 | contract GreeterProxiable is Greeter, Proxiable {} 23 | 24 | import "./utils/Proxiable40.sol"; 25 | contract GreeterProxiable40 is Greeter, Proxiable40 {} -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/GreeterDefender.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >= 0.4.22 <0.8.0; 2 | 3 | contract GreeterDefender { 4 | function version() external pure returns (uint256) { 5 | return 1; 6 | } 7 | } 8 | 9 | contract GreeterDefenderV2 { 10 | function version() external pure returns (uint256) { 11 | return 2; 12 | } 13 | } 14 | 15 | contract GreeterDefenderV2Bad { 16 | function x() public { 17 | (bool ok, ) = address(this).delegatecall(""); 18 | require(ok); 19 | } 20 | } 21 | 22 | contract GreeterDefenderV3 { 23 | uint256 immutable versionNumber; 24 | constructor(uint256 initialVersion) public { 25 | versionNumber = initialVersion; 26 | } 27 | function version() external view returns (uint256) { 28 | return versionNumber; 29 | } 30 | } 31 | 32 | import "./utils/Proxiable.sol"; 33 | contract GreeterDefenderProxiable is GreeterDefender, Proxiable {} 34 | contract GreeterDefenderV2Proxiable is GreeterDefenderV2, Proxiable {} 35 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/GreeterFallback.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >= 0.6.0 <0.8.0; 2 | 3 | contract GreeterFallback { 4 | 5 | string greeting; 6 | 7 | function initialize(string memory _greeting) public { 8 | greeting = _greeting; 9 | } 10 | 11 | function greet() public view returns (string memory) { 12 | return greeting; 13 | } 14 | 15 | function setGreeting(string memory _greeting) public { 16 | greeting = _greeting; 17 | } 18 | 19 | fallback() external {} 20 | } 21 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/GreeterProxiable40Fallback.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >= 0.6.0 <0.8.0; 2 | 3 | import { Proxiable40 } from "./utils/Proxiable40.sol"; 4 | 5 | contract GreeterProxiable40Fallback is Proxiable40 { 6 | string greeting; 7 | 8 | function initialize(string memory _greeting) public { 9 | greeting = _greeting; 10 | } 11 | 12 | function greet() public view returns (string memory) { 13 | return greeting; 14 | } 15 | 16 | fallback() external {} 17 | } 18 | 19 | contract GreeterProxiable40FallbackV2 is GreeterProxiable40Fallback { 20 | function resetGreeting() public { 21 | greeting = "Hello World"; 22 | } 23 | } -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/GreeterProxiable40FallbackString.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >= 0.6.0 <0.8.0; 2 | 3 | import { Proxiable40 } from "./utils/Proxiable40.sol"; 4 | 5 | contract GreeterProxiable40FallbackString is Proxiable40 { 6 | string greeting; 7 | 8 | function initialize(string memory _greeting) public { 9 | greeting = _greeting; 10 | } 11 | 12 | function greet() public view returns (string memory) { 13 | return greeting; 14 | } 15 | 16 | fallback(bytes calldata) external returns (bytes memory) { 17 | return abi.encode(greeting); 18 | } 19 | } 20 | 21 | contract GreeterProxiable40FallbackStringV2 is GreeterProxiable40FallbackString { 22 | function resetGreeting() public { 23 | greeting = "Hello World"; 24 | } 25 | } -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/GreeterTransparent40Fallback.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >= 0.6.0 <0.8.0; 2 | 3 | // These contracts are for testing only, they are not safe for use in production. 4 | 5 | interface ITransparentUpgradeableProxy { 6 | function upgradeTo(address) external; 7 | function upgradeToAndCall(address, bytes memory) external payable; 8 | } 9 | 10 | contract UnsafeAdminFallback { 11 | // NOT SAFE FOR PRODUCTION USE. ANYONE CAN UPGRADE THE PROXY THROUGH THE BELOW. 12 | 13 | function upgrade(ITransparentUpgradeableProxy proxy, address implementation) public virtual { 14 | proxy.upgradeTo(implementation); 15 | } 16 | 17 | function upgradeAndCall( 18 | ITransparentUpgradeableProxy proxy, 19 | address implementation, 20 | bytes memory data 21 | ) public payable virtual { 22 | proxy.upgradeToAndCall{value: msg.value}(implementation, data); 23 | } 24 | 25 | fallback() external {} 26 | } 27 | 28 | contract GreeterTransparent40Fallback { 29 | string greeting; 30 | 31 | function initialize(string memory _greeting) public { 32 | greeting = _greeting; 33 | } 34 | 35 | function greet() public view returns (string memory) { 36 | return greeting; 37 | } 38 | } 39 | 40 | contract GreeterTransparent40FallbackV2 is GreeterTransparent40Fallback { 41 | function resetGreeting() public { 42 | greeting = "Hello World"; 43 | } 44 | } -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/GreeterTransparent40FallbackString.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >= 0.6.0 <0.8.0; 2 | 3 | // These contracts are for testing only, they are not safe for use in production. 4 | 5 | interface ITransparentUpgradeableProxy { 6 | function upgradeTo(address) external; 7 | function upgradeToAndCall(address, bytes memory) external payable; 8 | } 9 | 10 | contract UnsafeAdminFallbackString { 11 | // NOT SAFE FOR PRODUCTION USE. ANYONE CAN UPGRADE THE PROXY THROUGH THE BELOW. 12 | 13 | function upgrade(ITransparentUpgradeableProxy proxy, address implementation) public virtual { 14 | proxy.upgradeTo(implementation); 15 | } 16 | 17 | function upgradeAndCall( 18 | ITransparentUpgradeableProxy proxy, 19 | address implementation, 20 | bytes memory data 21 | ) public payable virtual { 22 | proxy.upgradeToAndCall{value: msg.value}(implementation, data); 23 | } 24 | 25 | fallback(bytes calldata) external returns (bytes memory) { 26 | return abi.encode("foo"); 27 | } 28 | } 29 | 30 | contract GreeterTransparent40FallbackString { 31 | string greeting; 32 | 33 | function initialize(string memory _greeting) public { 34 | greeting = _greeting; 35 | } 36 | 37 | function greet() public view returns (string memory) { 38 | return greeting; 39 | } 40 | } 41 | 42 | contract GreeterTransparent40FallbackStringV2 is GreeterTransparent40FallbackString { 43 | function resetGreeting() public { 44 | greeting = "Hello World"; 45 | } 46 | } -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/GreeterV2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | contract GreeterV2 { 4 | 5 | string greeting; 6 | 7 | function initialize(string memory _greeting) public { 8 | greeting = _greeting; 9 | } 10 | 11 | function greet() public view returns (string memory) { 12 | return greeting; 13 | } 14 | 15 | function setGreeting(string memory _greeting) public { 16 | greeting = _greeting; 17 | } 18 | 19 | function resetGreeting() public { 20 | greeting = "Hello World"; 21 | } 22 | 23 | } 24 | 25 | import "./utils/Proxiable.sol"; 26 | contract GreeterV2Proxiable is GreeterV2, Proxiable {} 27 | 28 | import "./utils/Proxiable40.sol"; 29 | contract GreeterV2Proxiable40 is GreeterV2, Proxiable40 {} -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/GreeterV3.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | contract GreeterV3 { 4 | 5 | string greeting; 6 | 7 | function initialize(string memory _greeting) public { 8 | greeting = _greeting; 9 | } 10 | 11 | function greet() public view returns (string memory) { 12 | return greeting; 13 | } 14 | 15 | function setGreeting(string memory _greeting) public { 16 | greeting = _greeting; 17 | } 18 | 19 | function resetGreeting() public { 20 | greeting = "Hello World"; 21 | } 22 | 23 | function version() public pure returns (string memory) { 24 | return "V3"; 25 | } 26 | 27 | } 28 | 29 | import "./utils/Proxiable.sol"; 30 | contract GreeterV3Proxiable is GreeterV3, Proxiable {} 31 | 32 | import "./utils/Proxiable40.sol"; 33 | contract GreeterV3Proxiable40 is GreeterV3, Proxiable40 {} -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/HasOwner.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 5 | import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 6 | import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; 7 | 8 | /** 9 | * This contract is for testing only. 10 | * Basic but pointless contract that has its own owner and can call ProxyAdmin functions. 11 | */ 12 | contract HasOwner is Ownable { 13 | constructor(address initialOwner) Ownable(initialOwner) {} 14 | 15 | function upgradeAndCall( 16 | ProxyAdmin admin, 17 | ITransparentUpgradeableProxy proxy, 18 | address implementation, 19 | bytes memory data 20 | ) public payable virtual onlyOwner { 21 | admin.upgradeAndCall{value: msg.value}(proxy, implementation, data); 22 | } 23 | } -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/Initializers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | contract InitializerOverloaded { 4 | uint public x; 5 | 6 | function initialize(uint _x) public { 7 | x = _x; 8 | } 9 | 10 | function initialize(string memory) public {} 11 | } 12 | 13 | contract InitializerMissing { 14 | } 15 | 16 | import "./utils/Proxiable.sol"; 17 | contract InitializerOverloadedProxiable is InitializerOverloaded, Proxiable {} 18 | contract InitializerMissingProxiable is InitializerMissing, Proxiable {} 19 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/Invalid.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | contract Invalid { 4 | 5 | function initialize() public view { 6 | } 7 | 8 | function oops() public { 9 | selfdestruct(msg.sender); 10 | } 11 | 12 | } 13 | 14 | import "./utils/Proxiable.sol"; 15 | contract InvalidProxiable is Invalid, Proxiable {} 16 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/InvalidGreeter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | contract GreeterStorageConflict { 4 | 5 | uint greets; 6 | string greeting; 7 | 8 | function initialize(string memory _greeting) public { 9 | greeting = _greeting; 10 | } 11 | 12 | function greet() public returns (string memory) { 13 | greets = greets + 1; 14 | return greeting; 15 | } 16 | 17 | function setGreeting(string memory _greeting) public { 18 | greeting = _greeting; 19 | } 20 | 21 | } 22 | 23 | import "./utils/Proxiable.sol"; 24 | contract GreeterStorageConflictProxiable is GreeterStorageConflict, Proxiable {} 25 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/MakeNamespaced0516.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.16; 2 | 3 | /** 4 | * @dev foo 5 | */ 6 | contract MakeNamespaced0516 { 7 | /** 8 | * @dev bar 9 | */ 10 | function bar() public view { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/MultiPragma.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | import "./Greeter.sol"; 4 | 5 | contract GreeterMultiPragma is Greeter { 6 | function setAndGreet(string memory _greeting) public returns (string memory) { 7 | setGreeting(_greeting); 8 | return greet(); 9 | } 10 | } 11 | 12 | import "./utils/Proxiable.sol"; 13 | contract GreeterMultiPragmaProxiable is GreeterMultiPragma, Proxiable {} 14 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/MultipleExternalLibraries.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | import "./ExternalLibraries.sol"; 4 | 5 | contract MultipleExternalLibraries { 6 | 7 | function getLibraryVersion() external pure returns(string memory) { 8 | return SafeMath.version(); 9 | } 10 | 11 | function getLibrary2Version() external pure returns(string memory) { 12 | return SafeMathV2.version(); 13 | } 14 | } -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/NoLicense.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.20; 2 | 3 | contract NoLicense {} 4 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/Portfolio.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | contract Portfolio { 4 | struct Asset { 5 | bool enabled; 6 | uint amount; 7 | } 8 | 9 | mapping (string => Asset) assets; 10 | 11 | function initialize() public view { 12 | } 13 | 14 | function enable(string memory name) public returns (bool) { 15 | if (assets[name].enabled) { 16 | return false; 17 | } else { 18 | assets[name] = Asset(true, 0); 19 | return true; 20 | } 21 | } 22 | 23 | } 24 | 25 | import "./utils/Proxiable.sol"; 26 | contract PortfolioProxiable is Portfolio, Proxiable {} 27 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/PortfolioV2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | contract PortfolioV2 { 4 | struct Asset { 5 | bool enabled; 6 | uint amount; 7 | } 8 | 9 | mapping (string => Asset) assets; 10 | 11 | function initialize() public view { 12 | } 13 | 14 | function enable(string memory name) public returns (bool) { 15 | if (assets[name].enabled) { 16 | return false; 17 | } else { 18 | assets[name] = Asset(true, 10); 19 | return true; 20 | } 21 | } 22 | 23 | } 24 | 25 | contract PortfolioV2Bad { 26 | struct Asset { 27 | uint amount; 28 | } 29 | 30 | mapping (string => Asset) assets; 31 | 32 | function initialize() public view { 33 | } 34 | 35 | function enable(string memory name) public returns (bool) { 36 | assets[name] = Asset(10); 37 | return true; 38 | } 39 | 40 | } 41 | 42 | import "./utils/Proxiable.sol"; 43 | contract PortfolioV2Proxiable is PortfolioV2, Proxiable {} 44 | contract PortfolioV2BadProxiable is PortfolioV2Bad, Proxiable {} 45 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/Token.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | import "./ExternalLibraries.sol"; 4 | 5 | contract Token { 6 | 7 | using SafeMath for uint256; 8 | 9 | string public symbol; 10 | uint256 public totalSupply; 11 | 12 | address public owner; 13 | 14 | mapping(address => uint256) balances; 15 | 16 | function initialize(string memory tokenSymbol, uint256 amount) public { 17 | symbol = tokenSymbol; 18 | totalSupply = amount; 19 | balances[msg.sender] = amount; 20 | owner = msg.sender; 21 | } 22 | 23 | function transfer(address to, uint256 amount) public { 24 | require(balances[msg.sender] >= amount, "Not enough tokens"); 25 | balances[msg.sender] = balances[msg.sender].sub(amount); 26 | balances[to] = balances[to].add(amount); 27 | } 28 | 29 | function balanceOf(address account) external view returns (uint256) { 30 | return balances[account]; 31 | } 32 | 33 | function getLibraryVersion() external pure returns(string memory) { 34 | return SafeMath.version(); 35 | } 36 | 37 | } 38 | 39 | import "./utils/Proxiable.sol"; 40 | contract TokenProxiable is Token, Proxiable {} 41 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/TokenV2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | import "./Token.sol"; 4 | 5 | contract TokenV2 is Token { 6 | function transferAll(address to) external { 7 | uint256 amount = balances[msg.sender]; 8 | transfer(to, amount); 9 | } 10 | } 11 | 12 | import "./utils/Proxiable.sol"; 13 | contract TokenV2Proxiable is TokenV2, Proxiable {} 14 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/TokenV3.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.1; 2 | 3 | import "./Token.sol"; 4 | 5 | contract TokenV3 is Token { 6 | function transferPercent(address to, uint256 pct) external { 7 | uint256 amount = SafePercent.getPercent(balances[msg.sender], pct); 8 | transfer(to, amount); 9 | } 10 | } 11 | 12 | import "./utils/Proxiable.sol"; 13 | contract TokenV3Proxiable is TokenV3, Proxiable {} 14 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/Unlicensed.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.20; 3 | 4 | contract Unlicensed {} 5 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/UnrecognizedLicense.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UnrecognizedId 2 | pragma solidity ^0.8.20; 3 | 4 | contract UnrecognizedLicense {} 5 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/contracts/WithLicense.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | contract WithLicense { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('@openzeppelin/hardhat-upgrades'); 2 | 3 | const override = { 4 | version: '0.8.10', 5 | settings: { 6 | optimizer: { 7 | enabled: true, 8 | }, 9 | }, 10 | }; 11 | 12 | module.exports = { 13 | solidity: { 14 | compilers: [ 15 | { 16 | version: '0.8.29', 17 | }, 18 | { 19 | version: '0.8.9', 20 | }, 21 | { 22 | version: '0.7.6', 23 | }, 24 | { 25 | version: '0.6.12', 26 | }, 27 | { 28 | version: '0.5.17', 29 | }, 30 | ], 31 | overrides: { 32 | 'contracts/GapV1.sol': override, 33 | 'contracts/GapV2.sol': override, 34 | 'contracts/GapV2_Bad.sol': override, 35 | }, 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | rimraf .openzeppelin 6 | hardhat compile 7 | ava "$@" 8 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/defender/client.ts: -------------------------------------------------------------------------------- 1 | import { DeployClient } from '@openzeppelin/defender-sdk-deploy-client'; 2 | import { NetworkClient } from '@openzeppelin/defender-sdk-network-client'; 3 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 4 | import { getDefenderApiKey } from './utils'; 5 | 6 | export function getNetworkClient(hre: HardhatRuntimeEnvironment): NetworkClient { 7 | return new NetworkClient(getDefenderApiKey(hre)); 8 | } 9 | 10 | export function getDeployClient(hre: HardhatRuntimeEnvironment): DeployClient { 11 | return new DeployClient(getDefenderApiKey(hre)); 12 | } 13 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/deploy-beacon.ts: -------------------------------------------------------------------------------- 1 | import type { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import type { ContractFactory, Contract } from 'ethers'; 3 | 4 | import { Deployment } from '@openzeppelin/upgrades-core'; 5 | 6 | import { DeployBeaconOptions, deploy, DeployTransaction, getUpgradeableBeaconFactory, deployBeaconImpl } from './utils'; 7 | import { disableDefender } from './defender/utils'; 8 | import { attach, getSigner } from './utils/ethers'; 9 | import { getInitialOwner } from './utils/initial-owner'; 10 | 11 | export interface DeployBeaconFunction { 12 | (ImplFactory: ContractFactory, opts?: DeployBeaconOptions): Promise; 13 | } 14 | 15 | export function makeDeployBeacon(hre: HardhatRuntimeEnvironment, defenderModule: boolean): DeployBeaconFunction { 16 | return async function deployBeacon(ImplFactory: ContractFactory, opts: DeployBeaconOptions = {}) { 17 | disableDefender(hre, defenderModule, opts, deployBeacon.name); 18 | 19 | const { impl } = await deployBeaconImpl(hre, ImplFactory, opts); 20 | 21 | const signer = getSigner(ImplFactory.runner); 22 | const UpgradeableBeaconFactory = await getUpgradeableBeaconFactory(hre, signer); 23 | 24 | const initialOwner = await getInitialOwner(opts, signer); 25 | 26 | const beaconDeployment: Deployment & DeployTransaction = await deploy( 27 | hre, 28 | opts, 29 | UpgradeableBeaconFactory, 30 | impl, 31 | initialOwner, 32 | ); 33 | const beaconContract = attach(UpgradeableBeaconFactory, beaconDeployment.address); 34 | 35 | // @ts-ignore Won't be readonly because beaconContract was created through attach. 36 | beaconContract.deployTransaction = beaconDeployment.deployTransaction; 37 | return beaconContract; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/deploy-implementation.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import type { ContractFactory, ethers } from 'ethers'; 3 | 4 | import { DeployImplementationOptions } from './utils'; 5 | import { deployUpgradeableImpl } from './utils/deploy-impl'; 6 | import { enableDefender } from './defender/utils'; 7 | 8 | export type DeployImplementationFunction = ( 9 | ImplFactory: ContractFactory, 10 | opts?: DeployImplementationOptions, 11 | ) => Promise; 12 | 13 | export type DeployImplementationResponse = string | ethers.TransactionResponse; 14 | 15 | export function makeDeployImplementation( 16 | hre: HardhatRuntimeEnvironment, 17 | defenderModule: boolean, 18 | ): DeployImplementationFunction { 19 | return async function deployImplementation(ImplFactory, opts: DeployImplementationOptions = {}) { 20 | opts = enableDefender(hre, defenderModule, opts); 21 | 22 | const deployedImpl = await deployUpgradeableImpl(hre, ImplFactory, opts); 23 | 24 | if (opts.getTxResponse && deployedImpl.txResponse) { 25 | return deployedImpl.txResponse; 26 | } else { 27 | return deployedImpl.impl; 28 | } 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/scripts/migrate-oz-cli-project.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { migrateLegacyProject } from '@openzeppelin/upgrades-core'; 3 | 4 | migrateLegacyProject().catch(e => { 5 | console.error(e); 6 | process.exit(1); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/type-extensions.ts: -------------------------------------------------------------------------------- 1 | import 'hardhat/types/runtime'; 2 | import 'hardhat/types/config'; 3 | 4 | import type { HardhatUpgrades, DefenderHardhatUpgrades } from '.'; 5 | import { ContractFactory } from 'ethers'; 6 | 7 | export type ContractTypeOfFactory = ReturnType & ReturnType; 8 | 9 | declare module 'hardhat/types/runtime' { 10 | export interface HardhatRuntimeEnvironment { 11 | upgrades: HardhatUpgrades; 12 | defender: DefenderHardhatUpgrades; 13 | } 14 | } 15 | 16 | export interface HardhatDefenderConfig { 17 | apiKey: string; 18 | apiSecret: string; 19 | useDefenderDeploy?: boolean; 20 | network?: string; 21 | } 22 | 23 | export type NamespacedCompileErrorsRule = 'error' | 'warn' | 'ignore'; 24 | 25 | declare module 'hardhat/types/config' { 26 | export interface HardhatUserConfig { 27 | defender?: HardhatDefenderConfig; 28 | namespacedCompileErrors?: NamespacedCompileErrorsRule; 29 | } 30 | 31 | export interface HardhatConfig { 32 | defender?: HardhatDefenderConfig; 33 | namespacedCompileErrors?: NamespacedCompileErrorsRule; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/upgrade-beacon.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import type { ContractFactory, Contract } from 'ethers'; 3 | 4 | import { 5 | getContractAddress, 6 | ContractAddressOrInstance, 7 | getUpgradeableBeaconFactory, 8 | deployBeaconImpl, 9 | UpgradeBeaconOptions, 10 | attach, 11 | getSigner, 12 | } from './utils'; 13 | import { disableDefender } from './defender/utils'; 14 | 15 | export type UpgradeBeaconFunction = ( 16 | beacon: ContractAddressOrInstance, 17 | ImplFactory: ContractFactory, 18 | opts?: UpgradeBeaconOptions, 19 | ) => Promise; 20 | 21 | export function makeUpgradeBeacon(hre: HardhatRuntimeEnvironment, defenderModule: boolean): UpgradeBeaconFunction { 22 | return async function upgradeBeacon(beacon, ImplFactory, opts: UpgradeBeaconOptions = {}) { 23 | disableDefender(hre, defenderModule, opts, upgradeBeacon.name); 24 | 25 | const beaconAddress = await getContractAddress(beacon); 26 | const { impl: nextImpl } = await deployBeaconImpl(hre, ImplFactory, opts, beaconAddress); 27 | 28 | const UpgradeableBeaconFactory = await getUpgradeableBeaconFactory(hre, getSigner(ImplFactory.runner)); 29 | const beaconContract = attach(UpgradeableBeaconFactory, beaconAddress); 30 | 31 | const overrides = opts.txOverrides ? [opts.txOverrides] : []; 32 | const upgradeTx = await beaconContract.upgradeTo(nextImpl, ...overrides); 33 | 34 | // @ts-ignore Won't be readonly because beaconContract was created through attach. 35 | beaconContract.deployTransaction = upgradeTx; 36 | return beaconContract; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/utils/contract-types.ts: -------------------------------------------------------------------------------- 1 | export type ContractAddressOrInstance = string | { getAddress(): Promise }; 2 | 3 | export async function getContractAddress(addressOrInstance: ContractAddressOrInstance): Promise { 4 | if (typeof addressOrInstance === 'string') { 5 | return addressOrInstance; 6 | } else { 7 | return await addressOrInstance.getAddress(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/utils/debug.ts: -------------------------------------------------------------------------------- 1 | import debug from 'debug'; 2 | 3 | export default debug('@openzeppelin:upgrades:hardhat'); 4 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/utils/ethers.ts: -------------------------------------------------------------------------------- 1 | import { Contract, ContractFactory, ContractRunner, Signer } from 'ethers'; 2 | 3 | /** 4 | * Attaches a ContractFactory to an address and returns a Contract instance. 5 | */ 6 | export function attach(contractFactory: ContractFactory, address: string): Contract { 7 | return contractFactory.attach(address) as Contract; // Needed because ethers attach returns a BaseContract. 8 | } 9 | 10 | /** 11 | * Best effort to get a signer from a ContractRunner. Returns undefined if the runner is not a signer. 12 | */ 13 | export function getSigner(runner?: null | ContractRunner): Signer | undefined { 14 | return runner && 'getAddress' in runner ? (runner as Signer) : undefined; 15 | } 16 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/utils/factories.ts: -------------------------------------------------------------------------------- 1 | import { ContractFactory, Signer } from 'ethers'; 2 | 3 | import ERC1967Proxy from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json'; 4 | import BeaconProxy from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol/BeaconProxy.json'; 5 | import UpgradeableBeacon from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/beacon/UpgradeableBeacon.sol/UpgradeableBeacon.json'; 6 | import TransparentUpgradeableProxy from '@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts-v5/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'; 7 | 8 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 9 | 10 | export async function getProxyFactory(hre: HardhatRuntimeEnvironment, signer?: Signer): Promise { 11 | return hre.ethers.getContractFactory(ERC1967Proxy.abi, ERC1967Proxy.bytecode, signer); 12 | } 13 | 14 | export async function getTransparentUpgradeableProxyFactory( 15 | hre: HardhatRuntimeEnvironment, 16 | signer?: Signer, 17 | ): Promise { 18 | return hre.ethers.getContractFactory(TransparentUpgradeableProxy.abi, TransparentUpgradeableProxy.bytecode, signer); 19 | } 20 | 21 | export async function getBeaconProxyFactory(hre: HardhatRuntimeEnvironment, signer?: Signer): Promise { 22 | return hre.ethers.getContractFactory(BeaconProxy.abi, BeaconProxy.bytecode, signer); 23 | } 24 | 25 | export async function getUpgradeableBeaconFactory( 26 | hre: HardhatRuntimeEnvironment, 27 | signer?: Signer, 28 | ): Promise { 29 | return hre.ethers.getContractFactory(UpgradeableBeacon.abi, UpgradeableBeacon.bytecode, signer); 30 | } 31 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './deploy'; 2 | export { deployProxyImpl, deployBeaconImpl } from './deploy-impl'; 3 | export { simulateDeployImpl } from './simulate-deploy'; 4 | export * from './factories'; 5 | export * from './is-full-solc-output'; 6 | export * from './validations'; 7 | export * from './contract-types'; 8 | export * from './options'; 9 | export * from './initializer-data'; 10 | export { attach, getSigner } from './ethers'; 11 | export { 12 | attachITransparentUpgradeableProxyV4, 13 | attachITransparentUpgradeableProxyV5, 14 | attachProxyAdminV4, 15 | attachProxyAdminV5, 16 | } from './attach-abi'; 17 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/utils/initial-owner.ts: -------------------------------------------------------------------------------- 1 | import { Signer } from 'ethers'; 2 | import { InitialOwner } from './options'; 3 | import { UpgradesError } from '@openzeppelin/upgrades-core'; 4 | 5 | export async function getInitialOwner(opts: InitialOwner, signer?: Signer) { 6 | const result = opts.initialOwner ?? (await signer?.getAddress()) ?? undefined; 7 | if (result === undefined) { 8 | throw new UpgradesError( 9 | 'Initial owner must be specified', 10 | () => `Set the initial owner address using the \`initialOwner\` option`, 11 | ); 12 | } 13 | return result; 14 | } 15 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/utils/initializer-data.ts: -------------------------------------------------------------------------------- 1 | import { UpgradesError } from '@openzeppelin/upgrades-core'; 2 | import { Interface } from 'ethers'; 3 | 4 | export function getInitializerData( 5 | contractInterface: Interface, 6 | args: unknown[], 7 | initializer?: string | false, 8 | ): string { 9 | if (initializer === false) { 10 | return '0x'; 11 | } 12 | 13 | const allowNoInitialization = initializer === undefined && args.length === 0; 14 | initializer = initializer ?? 'initialize'; 15 | 16 | const fragment = contractInterface.getFunction(initializer); 17 | if (fragment === null) { 18 | if (allowNoInitialization) { 19 | return '0x'; 20 | } else { 21 | throw new UpgradesError( 22 | `The contract has no initializer function matching the name or signature: ${initializer}`, 23 | () => 24 | `Ensure that the initializer function exists, specify an existing function with the 'initializer' option, or set the 'initializer' option to false to omit the initializer call.`, 25 | ); 26 | } 27 | } else { 28 | return contractInterface.encodeFunctionData(fragment, args); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/utils/is-full-solc-output.ts: -------------------------------------------------------------------------------- 1 | import type { SolcOutput } from '@openzeppelin/upgrades-core'; 2 | 3 | type RecursivePartial = { [k in keyof T]?: RecursivePartial }; 4 | 5 | type MaybeSolcOutput = RecursivePartial; 6 | 7 | export function isFullSolcOutput(output: MaybeSolcOutput | undefined): boolean { 8 | if (output?.contracts == undefined || output?.sources == undefined) { 9 | return false; 10 | } 11 | 12 | for (const file of Object.values(output.contracts)) { 13 | if (file == undefined) { 14 | return false; 15 | } 16 | for (const contract of Object.values(file)) { 17 | if (contract?.evm?.bytecode == undefined) { 18 | return false; 19 | } 20 | } 21 | } 22 | 23 | for (const file of Object.values(output.sources)) { 24 | if (file?.ast == undefined || file?.id == undefined) { 25 | return false; 26 | } 27 | } 28 | 29 | return true; 30 | } 31 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/utils/simulate-deploy.ts: -------------------------------------------------------------------------------- 1 | import { fetchOrDeploy } from '@openzeppelin/upgrades-core'; 2 | import type { ContractFactory } from 'ethers'; 3 | import type { HardhatRuntimeEnvironment } from 'hardhat/types'; 4 | import { getDeployData } from './deploy-impl'; 5 | import { UpgradeOptions } from './options'; 6 | 7 | // To import an already deployed contract we want to reuse fetchOrDeploy for its ability to validate 8 | // a deployment and record it in the network file. We are able to do this by "simulating" a deployment: 9 | // for the "deploy" part we pass a function that simply returns the contract to be imported, rather than 10 | // actually deploying something. 11 | 12 | export async function simulateDeployImpl( 13 | hre: HardhatRuntimeEnvironment, 14 | ImplFactory: ContractFactory, 15 | opts: UpgradeOptions, 16 | implAddress: string, 17 | ) { 18 | const { deployData, simulateDeploy } = await getSimulatedData(hre, ImplFactory, opts, implAddress); 19 | await fetchOrDeploy(deployData.version, deployData.provider, simulateDeploy, opts, true); 20 | } 21 | 22 | /** 23 | * Gets data for a simulated deployment of the given contract to the given address. 24 | */ 25 | async function getSimulatedData( 26 | hre: HardhatRuntimeEnvironment, 27 | ImplFactory: ContractFactory, 28 | opts: UpgradeOptions, 29 | implAddress: string, 30 | ) { 31 | const deployData = await getDeployData(hre, ImplFactory, opts); 32 | const simulateDeploy = async () => { 33 | return { 34 | abi: ImplFactory.interface.format(true), 35 | layout: deployData.layout, 36 | address: implAddress, 37 | }; 38 | }; 39 | return { deployData, simulateDeploy }; 40 | } 41 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/src/validate-implementation.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import type { ContractFactory } from 'ethers'; 3 | 4 | import { validateImpl } from './utils/validate-impl'; 5 | import { getDeployData } from './utils/deploy-impl'; 6 | import { ValidateImplementationOptions } from './utils'; 7 | 8 | export type ValidateImplementationFunction = ( 9 | ImplFactory: ContractFactory, 10 | opts?: ValidateImplementationOptions, 11 | ) => Promise; 12 | 13 | export function makeValidateImplementation(hre: HardhatRuntimeEnvironment): ValidateImplementationFunction { 14 | return async function validateImplementation(ImplFactory, opts: ValidateImplementationOptions = {}) { 15 | const deployData = await getDeployData(hre, ImplFactory, opts); 16 | await validateImpl(deployData, opts); 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/beacon-deploy-overload.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.DeployOverload = await ethers.getContractFactory('DeployOverload'); 7 | }); 8 | 9 | test('no args', async t => { 10 | const beacon = await upgrades.deployBeacon(t.context.DeployOverload); 11 | const c = await upgrades.deployBeaconProxy(beacon, t.context.DeployOverload, { 12 | kind: 'beacon', 13 | initializer: 'customInitialize', 14 | }); 15 | t.is((await c.value()).toString(), '42'); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/beacon-deploy-validation.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Invalid = await ethers.getContractFactory('Invalid'); 7 | }); 8 | 9 | test('invalid deployment', async t => { 10 | const { Invalid } = t.context; 11 | await t.throwsAsync(() => upgrades.deployBeacon(Invalid), undefined, 'Contract `Invalid` is not upgrade safe'); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/beacon-happy-path-with-enums.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | upgrades.silenceWarnings(); 6 | 7 | test.before(async t => { 8 | t.context.Action = await ethers.getContractFactory('Action'); 9 | t.context.ActionV2 = await ethers.getContractFactory('ActionV2'); 10 | t.context.ActionV2Bad = await ethers.getContractFactory('ActionV2Bad'); 11 | }); 12 | 13 | test('deployBeaconProxy', async t => { 14 | const { Action } = t.context; 15 | const beacon = await upgrades.deployBeacon(Action); 16 | await upgrades.deployBeaconProxy(beacon, Action, []); 17 | }); 18 | 19 | test('upgradeBeacon', async t => { 20 | const { Action, ActionV2 } = t.context; 21 | const beacon = await upgrades.deployBeacon(Action); 22 | await upgrades.deployBeaconProxy(beacon, Action, []); 23 | 24 | await upgrades.upgradeBeacon(beacon, ActionV2); 25 | }); 26 | 27 | test('upgradeBeacon with incompatible layout', async t => { 28 | const { Action, ActionV2Bad } = t.context; 29 | const beacon = await upgrades.deployBeacon(Action); 30 | await upgrades.deployBeaconProxy(beacon, Action, []); 31 | 32 | const error = await t.throwsAsync(() => upgrades.upgradeBeacon(beacon, ActionV2Bad)); 33 | t.true(error.message.includes('Upgraded `action` to an incompatible type')); 34 | }); 35 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/beacon-happy-path-with-library.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Adder = await ethers.getContractFactory('Adder'); 7 | t.context.AdderV2 = await ethers.getContractFactory('AdderV2'); 8 | }); 9 | 10 | test('happy path with library', async t => { 11 | const { Adder, AdderV2 } = t.context; 12 | 13 | const beacon = await upgrades.deployBeacon(Adder); 14 | const adder = await upgrades.deployBeaconProxy(beacon, Adder); 15 | 16 | await upgrades.upgradeBeacon(beacon, AdderV2); 17 | const adder2 = Adder.attach(await adder.getAddress()); 18 | await adder2.add(1); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/beacon-happy-path-with-structs.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | upgrades.silenceWarnings(); 6 | 7 | test.before(async t => { 8 | t.context.Portfolio = await ethers.getContractFactory('Portfolio'); 9 | t.context.PortfolioV2 = await ethers.getContractFactory('PortfolioV2'); 10 | t.context.PortfolioV2Bad = await ethers.getContractFactory('PortfolioV2Bad'); 11 | }); 12 | 13 | test('deployBeaconProxy', async t => { 14 | const { Portfolio } = t.context; 15 | const beacon = await upgrades.deployBeacon(Portfolio); 16 | await beacon.waitForDeployment(); 17 | 18 | const portfolio = await upgrades.deployBeaconProxy(beacon, Portfolio, []); 19 | await portfolio.waitForDeployment(); 20 | 21 | await portfolio.enable('ETH'); 22 | }); 23 | 24 | test('upgradeBeacon', async t => { 25 | const { Portfolio, PortfolioV2 } = t.context; 26 | const beacon = await upgrades.deployBeacon(Portfolio); 27 | await beacon.waitForDeployment(); 28 | 29 | const portfolio = await upgrades.deployBeaconProxy(beacon, Portfolio, []); 30 | await portfolio.waitForDeployment(); 31 | 32 | await upgrades.upgradeBeacon(beacon, PortfolioV2); 33 | 34 | const portfolio2 = PortfolioV2.attach(await portfolio.getAddress()); 35 | await portfolio2.enable('ETH'); 36 | }); 37 | 38 | test('upgradeBeacon with incompatible layout', async t => { 39 | const { Portfolio, PortfolioV2Bad } = t.context; 40 | 41 | const beacon = await upgrades.deployBeacon(Portfolio); 42 | await upgrades.deployBeaconProxy(beacon, Portfolio, []); 43 | const error = await t.throwsAsync(() => upgrades.upgradeBeacon(beacon, PortfolioV2Bad)); 44 | t.true(error.message.includes('Upgraded `assets` to an incompatible type')); 45 | }); 46 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/beacon-initializers.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.InitializerOverloaded = await ethers.getContractFactory('InitializerOverloaded'); 7 | t.context.InitializerMissing = await ethers.getContractFactory('InitializerMissing'); 8 | }); 9 | 10 | test('multiple matching functions', async t => { 11 | const { InitializerOverloaded } = t.context; 12 | const beacon = await upgrades.deployBeacon(InitializerOverloaded); 13 | await t.throwsAsync( 14 | () => upgrades.deployBeaconProxy(beacon, InitializerOverloaded, [42]), 15 | undefined, 16 | 'multiple matching functions', 17 | ); 18 | }); 19 | 20 | test('unique function selector', async t => { 21 | const { InitializerOverloaded } = t.context; 22 | const beacon = await upgrades.deployBeacon(InitializerOverloaded); 23 | const instance = await upgrades.deployBeaconProxy(beacon, InitializerOverloaded, [42], { 24 | kind: 'beacon', 25 | initializer: 'initialize(uint256)', 26 | }); 27 | t.is((await instance.x()).toString(), '42'); 28 | }); 29 | 30 | test('no initialize function and no args', async t => { 31 | const { InitializerMissing } = t.context; 32 | const beacon = await upgrades.deployBeacon(InitializerMissing); 33 | await upgrades.deployBeaconProxy(beacon, InitializerMissing); 34 | }); 35 | 36 | test('no initialize function and explicit args', async t => { 37 | const { InitializerMissing } = t.context; 38 | const beacon = await upgrades.deployBeacon(InitializerMissing); 39 | await t.throwsAsync( 40 | () => upgrades.deployBeaconProxy(beacon, InitializerMissing, [42]), 41 | undefined, 42 | 'no matching function', 43 | ); 44 | }); 45 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/beacon-upgrade-storage.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 7 | t.context.GreeterStorageConflict = await ethers.getContractFactory('GreeterStorageConflict'); 8 | }); 9 | 10 | test('incompatible storage', async t => { 11 | const { Greeter, GreeterStorageConflict } = t.context; 12 | 13 | const beacon = await upgrades.deployBeacon(Greeter); 14 | await t.throwsAsync( 15 | () => upgrades.upgradeBeacon(beacon, GreeterStorageConflict), 16 | undefined, 17 | 'New storage layout is incompatible due to the following changes', 18 | ); 19 | }); 20 | 21 | test('incompatible storage - forced', async t => { 22 | const { Greeter, GreeterStorageConflict } = t.context; 23 | 24 | const beacon = await upgrades.deployBeacon(Greeter); 25 | await upgrades.upgradeBeacon(beacon, GreeterStorageConflict, { unsafeSkipStorageCheck: true }); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/beacon-upgrade-validation.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 7 | t.context.Invalid = await ethers.getContractFactory('Invalid'); 8 | }); 9 | 10 | test('invalid upgrade', async t => { 11 | const { Greeter, Invalid } = t.context; 12 | 13 | const beacon = await upgrades.deployBeacon(Greeter); 14 | const greeter = await upgrades.deployBeaconProxy(beacon, Greeter, ['Hola mundo!']); 15 | await t.throwsAsync( 16 | () => upgrades.upgradeProxy(greeter, Invalid), 17 | undefined, 18 | 'Contract `Invalid` is not upgrade safe', 19 | ); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/beacon-with-call.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 7 | t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); 8 | }); 9 | 10 | test('call with args', async t => { 11 | const { Greeter, GreeterV2 } = t.context; 12 | 13 | const greeterBeacon = await upgrades.deployBeacon(Greeter); 14 | const greeter = await upgrades.deployBeaconProxy(greeterBeacon, Greeter, ['Hello, Hardhat!']); 15 | 16 | t.is(await greeter.greet(), 'Hello, Hardhat!'); 17 | 18 | await upgrades.upgradeBeacon(greeterBeacon, GreeterV2, { 19 | call: { fn: 'setGreeting', args: ['Called during upgrade'] }, 20 | }); 21 | // the above call does nothing useful since beacon upgrades do not use that option 22 | 23 | t.is(await greeter.greet(), 'Hello, Hardhat!'); 24 | }); 25 | 26 | test('call without args', async t => { 27 | const { Greeter, GreeterV2 } = t.context; 28 | 29 | const greeterBeacon = await upgrades.deployBeacon(Greeter); 30 | const greeter = await upgrades.deployBeaconProxy(greeterBeacon, Greeter, ['Hello, Hardhat!']); 31 | 32 | t.is(await greeter.greet(), 'Hello, Hardhat!'); 33 | 34 | await upgrades.upgradeBeacon(greeterBeacon, GreeterV2, { 35 | call: 'resetGreeting', 36 | }); 37 | // the above call does nothing useful since beacon upgrades do not use that option 38 | 39 | t.is(await greeter.greet(), 'Hello, Hardhat!'); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/defender-deploy-implementation.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | const proxyquire = require('proxyquire').noCallThru(); 3 | 4 | const hre = require('hardhat'); 5 | const { ethers } = hre; 6 | 7 | const manifest = require('@openzeppelin/upgrades-core/dist/manifest'); 8 | 9 | test.before(async t => { 10 | t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); 11 | t.context.Invalid = await ethers.getContractFactory('Invalid'); 12 | t.context.deployImplementation = proxyquire('../dist/deploy-implementation', { 13 | './utils/deploy': { 14 | deploy: async (hre, opts, factory, ...args) => { 15 | opts.useDefenderDeploy = false; 16 | return { 17 | // just do regular deploy but add a deployment id 18 | ...(await require('../dist/utils/deploy').deploy(hre, opts, factory, ...args)), 19 | remoteDeploymentId: 'abc', 20 | }; 21 | }, 22 | '@global': true, 23 | }, 24 | }).makeDeployImplementation(hre, true); 25 | }); 26 | 27 | test('deploy implementation', async t => { 28 | const { deployImplementation, GreeterProxiable } = t.context; 29 | 30 | const inst = await deployImplementation(GreeterProxiable); 31 | t.not(inst, undefined); 32 | 33 | // check that manifest has deployment id 34 | const m = await manifest.Manifest.forNetwork(ethers.provider); 35 | await m.lockedRun(async () => { 36 | const deployment = await m.getDeploymentFromAddress(inst); 37 | t.is(deployment.remoteDeploymentId, 'abc'); 38 | }); 39 | }); 40 | 41 | test('deploy implementation - unsafe', async t => { 42 | const { deployImplementation, Invalid } = t.context; 43 | 44 | await t.throwsAsync(() => deployImplementation(Invalid), undefined, 'Contract `Invalid` is not upgrade safe'); 45 | }); 46 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/defender-hardhat-setup.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { defender } = require('hardhat'); 4 | 5 | test('creates defender object in hardhat runtime', async t => { 6 | t.is(typeof defender.proposeUpgradeWithApproval, 'function'); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/infer-proxy-kind.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 7 | t.context.GreeterProxiable = await ethers.getContractFactory('GreeterProxiable'); 8 | }); 9 | 10 | test('infer proxy kind', async t => { 11 | const { Greeter, GreeterProxiable } = t.context; 12 | 13 | const uups = await upgrades.deployProxy(GreeterProxiable, ['Hello, Hardhat!']); 14 | t.is(await upgrades.erc1967.getAdminAddress(await uups.getAddress()), ethers.ZeroAddress); 15 | 16 | const transparent = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!']); 17 | t.not(await upgrades.erc1967.getAdminAddress(await transparent.getAddress()), ethers.ZeroAddress); 18 | 19 | const beacon = await upgrades.deployBeacon(Greeter); 20 | const beaconProxy = await upgrades.deployBeaconProxy(beacon, Greeter, ['Hello, Hardhat!']); 21 | t.is(await upgrades.erc1967.getAdminAddress(await beaconProxy.getAddress()), ethers.ZeroAddress); 22 | t.not(await upgrades.erc1967.getBeaconAddress(await beaconProxy.getAddress()), ethers.ZeroAddress); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/namespaced.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-upgrades/2f26508ddfaa390dfd3c2860047b1ae519ce01b4/packages/plugin-hardhat/test/namespaced.js.snap -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/redeploy-implementation-onchange.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 7 | t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); 8 | }); 9 | 10 | test('onchange', async t => { 11 | const { Greeter, GreeterV2 } = t.context; 12 | 13 | const impl1 = await upgrades.deployImplementation(Greeter); 14 | const impl2 = await upgrades.deployImplementation(Greeter, { redeployImplementation: 'onchange' }); 15 | t.is(impl2, impl1); 16 | 17 | const impl3 = await upgrades.deployImplementation(GreeterV2, { redeployImplementation: 'onchange' }); 18 | t.not(impl3, impl1); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/solidity-overrides-storage.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.GapV1 = await ethers.getContractFactory('GapV1'); 7 | t.context.GapV2 = await ethers.getContractFactory('GapV2'); 8 | t.context.GapV2_Bad = await ethers.getContractFactory('GapV2_Bad'); 9 | }); 10 | 11 | test('use gap correctly with solidity config overrides', async t => { 12 | const { GapV1, GapV2 } = t.context; 13 | const proxy = await upgrades.deployProxy(GapV1); 14 | await upgrades.upgradeProxy(proxy, GapV2); 15 | }); 16 | 17 | test('use gap incorrectly', async t => { 18 | const { GapV1, GapV2_Bad } = t.context; 19 | const proxy = await upgrades.deployProxy(GapV1); 20 | await t.throwsAsync(() => upgrades.upgradeProxy(proxy, GapV2_Bad), undefined, 'Set __gap array to size 47'); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-deploy-overload.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.DeployOverload = await ethers.getContractFactory('DeployOverload'); 7 | }); 8 | 9 | test('no args', async t => { 10 | const c = await upgrades.deployProxy(t.context.DeployOverload, { 11 | kind: 'transparent', 12 | initializer: 'customInitialize', 13 | }); 14 | t.is((await c.value()).toString(), '42'); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-deploy-validation.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Invalid = await ethers.getContractFactory('Invalid'); 7 | }); 8 | 9 | test('invalid deployment', async t => { 10 | const { Invalid } = t.context; 11 | await t.throwsAsync( 12 | () => upgrades.deployProxy(Invalid, { kind: 'transparent' }), 13 | undefined, 14 | 'Contract `Invalid` is not upgrade safe', 15 | ); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-happy-path-with-call.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 7 | t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); 8 | }); 9 | 10 | test('happy path - call with args', async t => { 11 | const { Greeter, GreeterV2 } = t.context; 12 | 13 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'transparent' }); 14 | 15 | t.is(await greeter.greet(), 'Hello, Hardhat!'); 16 | 17 | await upgrades.upgradeProxy(greeter, GreeterV2, { 18 | call: { fn: 'setGreeting', args: ['Called during upgrade'] }, 19 | }); 20 | 21 | t.is(await greeter.greet(), 'Called during upgrade'); 22 | }); 23 | 24 | test('happy path - call without args', async t => { 25 | const { Greeter, GreeterV2 } = t.context; 26 | 27 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'transparent' }); 28 | 29 | t.is(await greeter.greet(), 'Hello, Hardhat!'); 30 | 31 | await upgrades.upgradeProxy(greeter, GreeterV2, { 32 | call: 'resetGreeting', 33 | }); 34 | 35 | t.is(await greeter.greet(), 'Hello World'); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-happy-path-with-enums.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | upgrades.silenceWarnings(); 6 | 7 | test.before(async t => { 8 | t.context.Action = await ethers.getContractFactory('Action'); 9 | t.context.ActionV2 = await ethers.getContractFactory('ActionV2'); 10 | t.context.ActionV2Bad = await ethers.getContractFactory('ActionV2Bad'); 11 | }); 12 | 13 | test('deployProxy', async t => { 14 | const { Action } = t.context; 15 | await upgrades.deployProxy(Action, [], { kind: 'transparent' }); 16 | }); 17 | 18 | test('upgradeProxy', async t => { 19 | const { Action, ActionV2 } = t.context; 20 | const action = await upgrades.deployProxy(Action, [], { kind: 'transparent' }); 21 | await upgrades.upgradeProxy(action, ActionV2); 22 | }); 23 | 24 | test('upgradeProxy with incompatible layout', async t => { 25 | const { Action, ActionV2Bad } = t.context; 26 | const action = await upgrades.deployProxy(Action, [], { kind: 'transparent' }); 27 | const error = await t.throwsAsync(() => upgrades.upgradeProxy(action, ActionV2Bad)); 28 | t.true(error.message.includes('Upgraded `action` to an incompatible type')); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-happy-path-with-library.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Adder = await ethers.getContractFactory('Adder'); 7 | t.context.AdderV2 = await ethers.getContractFactory('AdderV2'); 8 | }); 9 | 10 | test('happy path with library', async t => { 11 | const { Adder, AdderV2 } = t.context; 12 | const adder = await upgrades.deployProxy(Adder, { kind: 'transparent' }); 13 | const adder2 = await upgrades.upgradeProxy(adder, AdderV2); 14 | await adder2.add(1); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-happy-path-with-structs.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | upgrades.silenceWarnings(); 6 | 7 | test.before(async t => { 8 | t.context.Portfolio = await ethers.getContractFactory('Portfolio'); 9 | t.context.PortfolioV2 = await ethers.getContractFactory('PortfolioV2'); 10 | t.context.PortfolioV2Bad = await ethers.getContractFactory('PortfolioV2Bad'); 11 | }); 12 | 13 | test('deployProxy', async t => { 14 | const { Portfolio } = t.context; 15 | const portfolio = await upgrades.deployProxy(Portfolio, [], { kind: 'transparent' }); 16 | await portfolio.enable('ETH'); 17 | }); 18 | 19 | test('upgradeProxy', async t => { 20 | const { Portfolio, PortfolioV2 } = t.context; 21 | const portfolio = await upgrades.deployProxy(Portfolio, [], { kind: 'transparent' }); 22 | const portfolio2 = await upgrades.upgradeProxy(portfolio, PortfolioV2); 23 | await portfolio2.enable('ETH'); 24 | }); 25 | 26 | test('upgradeProxy with incompatible layout', async t => { 27 | const { Portfolio, PortfolioV2Bad } = t.context; 28 | const portfolio = await upgrades.deployProxy(Portfolio, [], { kind: 'transparent' }); 29 | const error = await t.throwsAsync(() => upgrades.upgradeProxy(portfolio, PortfolioV2Bad)); 30 | t.true(error.message.includes('Upgraded `assets` to an incompatible type')); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-happy-path.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 7 | t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2'); 8 | t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3'); 9 | }); 10 | 11 | test('happy path', async t => { 12 | const { Greeter, GreeterV2, GreeterV3 } = t.context; 13 | 14 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'transparent' }); 15 | 16 | const greeter2 = await upgrades.upgradeProxy(greeter, GreeterV2); 17 | await greeter2.waitForDeployment(); 18 | await greeter2.resetGreeting(); 19 | 20 | const greeter3ImplAddr = await upgrades.prepareUpgrade(await greeter.getAddress(), GreeterV3); 21 | const greeter3 = GreeterV3.attach(greeter3ImplAddr); 22 | const version3 = await greeter3.version(); 23 | t.is(version3, 'V3'); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-initializers.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.InitializerOverloaded = await ethers.getContractFactory('InitializerOverloaded'); 7 | t.context.InitializerMissing = await ethers.getContractFactory('InitializerMissing'); 8 | }); 9 | 10 | test('multiple matching functions', async t => { 11 | const { InitializerOverloaded } = t.context; 12 | await t.throwsAsync( 13 | () => upgrades.deployProxy(InitializerOverloaded, [42], { kind: 'transparent' }), 14 | undefined, 15 | 'multiple matching functions', 16 | ); 17 | }); 18 | 19 | test('unique function selector', async t => { 20 | const { InitializerOverloaded } = t.context; 21 | const instance = await upgrades.deployProxy(InitializerOverloaded, [42], { 22 | kind: 'transparent', 23 | initializer: 'initialize(uint256)', 24 | }); 25 | t.is((await instance.x()).toString(), '42'); 26 | }); 27 | 28 | test('no initialize function and no args', async t => { 29 | const { InitializerMissing } = t.context; 30 | await upgrades.deployProxy(InitializerMissing, { kind: 'transparent' }); 31 | }); 32 | 33 | test('no initialize function and explicit args', async t => { 34 | const { InitializerMissing } = t.context; 35 | await t.throwsAsync( 36 | () => upgrades.deployProxy(InitializerMissing, [42], { kind: 'transparent' }), 37 | undefined, 38 | 'no matching function', 39 | ); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-multi-compiler.js: -------------------------------------------------------------------------------- 1 | const { getVersion, getContractNameAndRunValidation } = require('@openzeppelin/upgrades-core'); 2 | const test = require('ava'); 3 | 4 | const hre = require('hardhat'); 5 | const { readValidations } = require('../dist/utils/validations'); 6 | 7 | test.before(async t => { 8 | const { ethers } = hre; 9 | t.context.validations = await readValidations(hre); 10 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 11 | t.context.GreeterMulti = await ethers.getContractFactory('GreeterMultiPragma'); 12 | }); 13 | 14 | test('multiple compiler runs', async t => { 15 | const { validations, Greeter, GreeterMulti } = t.context; 16 | const GreeterVersion = getVersion(Greeter.bytecode); 17 | const GreeterMultiVersion = getVersion(GreeterMulti.bytecode); 18 | 19 | const [, GreeterRunValidation] = getContractNameAndRunValidation(validations, GreeterVersion); 20 | const [, GreeterMultiRunValidation] = getContractNameAndRunValidation(validations, GreeterMultiVersion); 21 | 22 | t.notDeepEqual(GreeterRunValidation, GreeterMultiRunValidation); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-transfer-admin-ownership-happy-path.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const hre = require('hardhat'); 4 | const { ethers, upgrades } = hre; 5 | 6 | const TEST_ADDRESS = '0x1E6876a6C2757de611c9F12B23211dBaBd1C9028'; 7 | const OWNABLE_ABI = ['function owner() view returns (address)']; 8 | 9 | test.before(async t => { 10 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 11 | }); 12 | 13 | test('transferProxyAdminOwnership', async t => { 14 | // we need to deploy a proxy so we have a Proxy Admin 15 | const { Greeter } = t.context; 16 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'transparent' }); 17 | 18 | await upgrades.admin.transferProxyAdminOwnership(await greeter.getAddress(), TEST_ADDRESS); 19 | 20 | const adminAddress = await upgrades.erc1967.getAdminAddress(await greeter.getAddress()); 21 | const admin = await hre.ethers.getContractAt(OWNABLE_ABI, adminAddress); 22 | const newOwner = await admin.owner(); 23 | 24 | t.is(newOwner, TEST_ADDRESS); 25 | }); 26 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-transfer-admin-ownership-signer.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const hre = require('hardhat'); 4 | const { ethers, upgrades } = hre; 5 | 6 | const TEST_ADDRESS = '0x1E6876a6C2757de611c9F12B23211dBaBd1C9028'; 7 | const OWNABLE_ABI = ['function owner() view returns (address)']; 8 | 9 | test('transferProxyAdminOwnership - signer', async t => { 10 | // we need to deploy a proxy so we have a Proxy Admin 11 | const signer = await ethers.provider.getSigner(1); 12 | const Greeter = await ethers.getContractFactory('Greeter', signer); 13 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'transparent' }); 14 | 15 | await upgrades.admin.transferProxyAdminOwnership(await greeter.getAddress(), TEST_ADDRESS, signer); 16 | 17 | const adminAddress = await upgrades.erc1967.getAdminAddress(await greeter.getAddress()); 18 | const admin = await hre.ethers.getContractAt(OWNABLE_ABI, adminAddress); 19 | const newOwner = await admin.owner(); 20 | 21 | t.is(newOwner, TEST_ADDRESS); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-transfer-admin-ownership-wrong-signer.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const hre = require('hardhat'); 4 | const { ethers, upgrades } = hre; 5 | const testAddress = '0x1E6876a6C2757de611c9F12B23211dBaBd1C9028'; 6 | 7 | test.before(async t => { 8 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 9 | }); 10 | 11 | test('transferProxyAdminOwnership - wrong signer', async t => { 12 | // we need to deploy a proxy so we have a Proxy Admin 13 | const { Greeter } = t.context; 14 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'transparent' }); 15 | 16 | const signer = await ethers.provider.getSigner(1); 17 | 18 | const proxyAddress = await greeter.getAddress(); 19 | 20 | await t.throwsAsync( 21 | () => upgrades.admin.transferProxyAdminOwnership(proxyAddress, testAddress, signer), 22 | { message: /0x118cdaa7/ }, // bytes4(keccak256('OwnableUnauthorizedAccount(address)')) 23 | ); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-upgrade-storage.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 7 | t.context.GreeterStorageConflict = await ethers.getContractFactory('GreeterStorageConflict'); 8 | }); 9 | 10 | test('incompatible storage', async t => { 11 | const { Greeter, GreeterStorageConflict } = t.context; 12 | const greeter = await upgrades.deployProxy(Greeter, ['Hola mundo!'], { kind: 'transparent' }); 13 | await t.throwsAsync( 14 | () => upgrades.upgradeProxy(greeter, GreeterStorageConflict), 15 | undefined, 16 | 'New storage layout is incompatible due to the following changes', 17 | ); 18 | }); 19 | 20 | test('incompatible storage - forced', async t => { 21 | const { Greeter, GreeterStorageConflict } = t.context; 22 | const greeter = await upgrades.deployProxy(Greeter, ['Hola mundo!'], { kind: 'transparent' }); 23 | await upgrades.upgradeProxy(greeter, GreeterStorageConflict, { unsafeSkipStorageCheck: true }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-upgrade-validation.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 7 | t.context.Invalid = await ethers.getContractFactory('Invalid'); 8 | }); 9 | 10 | test('invalid upgrade', async t => { 11 | const { Greeter, Invalid } = t.context; 12 | 13 | const greeter = await upgrades.deployProxy(Greeter, ['Hola mundo!'], { kind: 'transparent' }); 14 | await t.throwsAsync( 15 | () => upgrades.upgradeProxy(greeter, Invalid), 16 | undefined, 17 | 'Contract `Invalid` is not upgrade safe', 18 | ); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-v4-change-admin-happy-path.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | const ProxyAdmin = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); 6 | const TransparentUpgradableProxy = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json'); 7 | 8 | const testAddress = '0x1E6876a6C2757de611c9F12B23211dBaBd1C9028'; 9 | 10 | test.before(async t => { 11 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 12 | t.context.ProxyAdmin = await ethers.getContractFactory(ProxyAdmin.abi, ProxyAdmin.bytecode); 13 | t.context.TransparentUpgradableProxy = await ethers.getContractFactory( 14 | TransparentUpgradableProxy.abi, 15 | TransparentUpgradableProxy.bytecode, 16 | ); 17 | }); 18 | 19 | test('changeProxyAdmin', async t => { 20 | const { Greeter, ProxyAdmin, TransparentUpgradableProxy } = t.context; 21 | 22 | // Deploy a v4 proxy and admin, and import them 23 | const impl = await Greeter.deploy(); 24 | await impl.waitForDeployment(); 25 | const admin = await ProxyAdmin.deploy(); 26 | await admin.waitForDeployment(); 27 | const proxy = await TransparentUpgradableProxy.deploy( 28 | await impl.getAddress(), 29 | await admin.getAddress(), 30 | Greeter.interface.encodeFunctionData('initialize', ['Hello, Hardhat!']), 31 | ); 32 | const greeter = await upgrades.forceImport(await proxy.getAddress(), Greeter); 33 | 34 | await upgrades.admin.changeProxyAdmin(await greeter.getAddress(), testAddress); 35 | const newAdmin = await upgrades.erc1967.getAdminAddress(await greeter.getAddress()); 36 | 37 | t.is(newAdmin, testAddress); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/transparent-v5-with-v4-manifest-admin.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | const hre = require('hardhat'); 5 | 6 | const { fetchOrDeployAdmin } = require('@openzeppelin/upgrades-core'); 7 | const { deploy } = require('../dist/utils'); 8 | 9 | const ProxyAdmin = require('@openzeppelin/upgrades-core/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json'); 10 | 11 | test.before(async t => { 12 | t.context.Greeter = await ethers.getContractFactory('Greeter'); 13 | t.context.ProxyAdmin = await ethers.getContractFactory(ProxyAdmin.abi, ProxyAdmin.bytecode); 14 | }); 15 | 16 | test('add v4 admin to manifest, then deploy v5 transparent', async t => { 17 | const { Greeter, ProxyAdmin } = t.context; 18 | 19 | const admin = await ProxyAdmin.deploy(); 20 | await admin.waitForDeployment(); 21 | 22 | await fetchOrDeployAdmin(ethers.provider, () => deploy(hre, {}, ProxyAdmin), {}); 23 | 24 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!']); 25 | await greeter.waitForDeployment(); 26 | const greeterAdmin = await upgrades.erc1967.getAdminAddress(await greeter.getAddress()); 27 | 28 | t.not(greeterAdmin, await admin.getAddress()); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-deploy-overload.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.DeployOverload = await ethers.getContractFactory('DeployOverloadProxiable'); 7 | }); 8 | 9 | test('no args', async t => { 10 | const c = await upgrades.deployProxy(t.context.DeployOverload, { 11 | kind: 'uups', 12 | initializer: 'customInitialize', 13 | }); 14 | t.is((await c.value()).toString(), '42'); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-deploy-validation.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Invalid = await ethers.getContractFactory('InvalidProxiable'); 7 | }); 8 | 9 | test('invalid deployment', async t => { 10 | const { Invalid } = t.context; 11 | await t.throwsAsync( 12 | () => upgrades.deployProxy(Invalid, { kind: 'uups' }), 13 | undefined, 14 | 'Contract `Invalid` is not upgrade safe', 15 | ); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-happy-path-v4.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('GreeterProxiable40'); 7 | t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2Proxiable40'); 8 | t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3Proxiable40'); 9 | }); 10 | 11 | test('happy path', async t => { 12 | const { Greeter, GreeterV2, GreeterV3 } = t.context; 13 | 14 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'uups' }); 15 | 16 | const greeter2 = await upgrades.upgradeProxy(greeter, GreeterV2); 17 | await greeter2.waitForDeployment(); 18 | await greeter2.resetGreeting(); 19 | 20 | const greeter3ImplAddr = await upgrades.prepareUpgrade(await greeter.getAddress(), GreeterV3); 21 | const greeter3 = GreeterV3.attach(greeter3ImplAddr); 22 | const version3 = await greeter3.version(); 23 | t.is(version3, 'V3'); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-happy-path-with-call-v4.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('GreeterProxiable40'); 7 | t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2Proxiable40'); 8 | }); 9 | 10 | test('happy path - call with args', async t => { 11 | const { Greeter, GreeterV2 } = t.context; 12 | 13 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'uups' }); 14 | 15 | t.is(await greeter.greet(), 'Hello, Hardhat!'); 16 | 17 | await upgrades.upgradeProxy(greeter, GreeterV2, { 18 | call: { fn: 'setGreeting', args: ['Called during upgrade'] }, 19 | }); 20 | 21 | t.is(await greeter.greet(), 'Called during upgrade'); 22 | }); 23 | 24 | test('happy path - call without args', async t => { 25 | const { Greeter, GreeterV2 } = t.context; 26 | 27 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'uups' }); 28 | 29 | t.is(await greeter.greet(), 'Hello, Hardhat!'); 30 | 31 | await upgrades.upgradeProxy(greeter, GreeterV2, { 32 | call: 'resetGreeting', 33 | }); 34 | 35 | t.is(await greeter.greet(), 'Hello World'); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-happy-path-with-call.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); 7 | t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2Proxiable'); 8 | }); 9 | 10 | test('happy path - call with args', async t => { 11 | const { Greeter, GreeterV2 } = t.context; 12 | 13 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'uups' }); 14 | 15 | t.is(await greeter.greet(), 'Hello, Hardhat!'); 16 | 17 | await upgrades.upgradeProxy(greeter, GreeterV2, { 18 | call: { fn: 'setGreeting', args: ['Called during upgrade'] }, 19 | }); 20 | 21 | t.is(await greeter.greet(), 'Called during upgrade'); 22 | }); 23 | 24 | test('happy path - call without args', async t => { 25 | const { Greeter, GreeterV2 } = t.context; 26 | 27 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'uups' }); 28 | 29 | t.is(await greeter.greet(), 'Hello, Hardhat!'); 30 | 31 | await upgrades.upgradeProxy(greeter, GreeterV2, { 32 | call: 'resetGreeting', 33 | }); 34 | 35 | t.is(await greeter.greet(), 'Hello World'); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-happy-path-with-enums.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | upgrades.silenceWarnings(); 6 | 7 | test.before(async t => { 8 | t.context.Action = await ethers.getContractFactory('ActionProxiable'); 9 | t.context.ActionV2 = await ethers.getContractFactory('ActionV2Proxiable'); 10 | t.context.ActionV2Bad = await ethers.getContractFactory('ActionV2BadProxiable'); 11 | }); 12 | 13 | test('deployProxy', async t => { 14 | const { Action } = t.context; 15 | await upgrades.deployProxy(Action, [], { kind: 'uups' }); 16 | }); 17 | 18 | test('upgradeProxy', async t => { 19 | const { Action, ActionV2 } = t.context; 20 | const action = await upgrades.deployProxy(Action, [], { kind: 'uups' }); 21 | await upgrades.upgradeProxy(action, ActionV2); 22 | }); 23 | 24 | test('upgradeProxy with incompatible layout', async t => { 25 | const { Action, ActionV2Bad } = t.context; 26 | const action = await upgrades.deployProxy(Action, [], { kind: 'uups' }); 27 | const error = await t.throwsAsync(() => upgrades.upgradeProxy(action, ActionV2Bad)); 28 | t.true(error.message.includes('Upgraded `action` to an incompatible type')); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-happy-path-with-library.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Adder = await ethers.getContractFactory('AdderProxiable'); 7 | t.context.AdderV2 = await ethers.getContractFactory('AdderV2Proxiable'); 8 | }); 9 | 10 | test('happy path with library', async t => { 11 | const { Adder, AdderV2 } = t.context; 12 | const adder = await upgrades.deployProxy(Adder, { kind: 'uups' }); 13 | const adder2 = await upgrades.upgradeProxy(adder, AdderV2); 14 | await adder2.add(1); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-happy-path-with-structs.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | upgrades.silenceWarnings(); 6 | 7 | test.before(async t => { 8 | t.context.Portfolio = await ethers.getContractFactory('PortfolioProxiable'); 9 | t.context.PortfolioV2 = await ethers.getContractFactory('PortfolioV2Proxiable'); 10 | t.context.PortfolioV2Bad = await ethers.getContractFactory('PortfolioV2BadProxiable'); 11 | }); 12 | 13 | test('deployProxy', async t => { 14 | const { Portfolio } = t.context; 15 | const portfolio = await upgrades.deployProxy(Portfolio, [], { kind: 'uups' }); 16 | await portfolio.enable('ETH'); 17 | }); 18 | 19 | test('upgradeProxy', async t => { 20 | const { Portfolio, PortfolioV2 } = t.context; 21 | const portfolio = await upgrades.deployProxy(Portfolio, [], { kind: 'uups' }); 22 | const portfolio2 = await upgrades.upgradeProxy(portfolio, PortfolioV2); 23 | await portfolio2.enable('ETH'); 24 | }); 25 | 26 | test('upgradeProxy with incompatible layout', async t => { 27 | const { Portfolio, PortfolioV2Bad } = t.context; 28 | const portfolio = await upgrades.deployProxy(Portfolio, [], { kind: 'uups' }); 29 | const error = await t.throwsAsync(() => upgrades.upgradeProxy(portfolio, PortfolioV2Bad)); 30 | t.true(error.message.includes('Upgraded `assets` to an incompatible type')); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-happy-path.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); 7 | t.context.GreeterV2 = await ethers.getContractFactory('GreeterV2Proxiable'); 8 | t.context.GreeterV3 = await ethers.getContractFactory('GreeterV3Proxiable'); 9 | }); 10 | 11 | test('happy path', async t => { 12 | const { Greeter, GreeterV2, GreeterV3 } = t.context; 13 | 14 | const greeter = await upgrades.deployProxy(Greeter, ['Hello, Hardhat!'], { kind: 'uups' }); 15 | 16 | const greeter2 = await upgrades.upgradeProxy(greeter, GreeterV2); 17 | await greeter2.waitForDeployment(); 18 | await greeter2.resetGreeting(); 19 | 20 | const greeter3ImplAddr = await upgrades.prepareUpgrade(await greeter.getAddress(), GreeterV3); 21 | const greeter3 = GreeterV3.attach(greeter3ImplAddr); 22 | const version3 = await greeter3.version(); 23 | t.is(version3, 'V3'); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-initial-owner.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); 7 | }); 8 | 9 | test('uups with initialOwner option', async t => { 10 | const { Greeter } = t.context; 11 | 12 | const initialOwner = await ethers.provider.getSigner(1); 13 | 14 | await t.throwsAsync(upgrades.deployProxy(Greeter, ['hello'], { initialOwner: initialOwner.address }), { 15 | message: /The `initialOwner` option is not supported for this kind of proxy \('uups'\)/, 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-multi-compiler.js: -------------------------------------------------------------------------------- 1 | const { getVersion, getContractNameAndRunValidation } = require('@openzeppelin/upgrades-core'); 2 | const test = require('ava'); 3 | 4 | const hre = require('hardhat'); 5 | const { readValidations } = require('../dist/utils/validations'); 6 | 7 | test.before(async t => { 8 | const { ethers } = hre; 9 | t.context.validations = await readValidations(hre); 10 | t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); 11 | t.context.GreeterMulti = await ethers.getContractFactory('GreeterMultiPragmaProxiable'); 12 | }); 13 | 14 | test('multiple compiler runs', async t => { 15 | const { validations, Greeter, GreeterMulti } = t.context; 16 | const GreeterVersion = getVersion(Greeter.bytecode); 17 | const GreeterMultiVersion = getVersion(GreeterMulti.bytecode); 18 | 19 | const [, GreeterRunValidation] = getContractNameAndRunValidation(validations, GreeterVersion); 20 | const [, GreeterMultiRunValidation] = getContractNameAndRunValidation(validations, GreeterMultiVersion); 21 | 22 | t.notDeepEqual(GreeterRunValidation, GreeterMultiRunValidation); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-upgrade-storage.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); 7 | t.context.GreeterStorageConflict = await ethers.getContractFactory('GreeterStorageConflictProxiable'); 8 | }); 9 | 10 | test('incompatible storage', async t => { 11 | const { Greeter, GreeterStorageConflict } = t.context; 12 | const greeter = await upgrades.deployProxy(Greeter, ['Hola mundo!'], { kind: 'uups' }); 13 | await t.throwsAsync( 14 | () => upgrades.upgradeProxy(greeter, GreeterStorageConflict), 15 | undefined, 16 | 'New storage layout is incompatible due to the following changes', 17 | ); 18 | }); 19 | 20 | test('incompatible storage - forced', async t => { 21 | const { Greeter, GreeterStorageConflict } = t.context; 22 | const greeter = await upgrades.deployProxy(Greeter, ['Hola mundo!'], { kind: 'uups' }); 23 | await upgrades.upgradeProxy(greeter, GreeterStorageConflict, { unsafeSkipStorageCheck: true }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/test/uups-upgrade-validation.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | 3 | const { ethers, upgrades } = require('hardhat'); 4 | 5 | test.before(async t => { 6 | t.context.Greeter = await ethers.getContractFactory('GreeterProxiable'); 7 | t.context.Invalid = await ethers.getContractFactory('InvalidProxiable'); 8 | }); 9 | 10 | test('invalid upgrade', async t => { 11 | const { Greeter, Invalid } = t.context; 12 | 13 | const greeter = await upgrades.deployProxy(Greeter, ['Hola mundo!'], { kind: 'uups' }); 14 | await t.throwsAsync( 15 | () => upgrades.upgradeProxy(greeter, Invalid), 16 | undefined, 17 | 'Contract `Invalid` is not upgrade safe', 18 | ); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/plugin-hardhat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "composite": true, 5 | "outDir": "dist", 6 | "rootDir": "src", 7 | "resolveJsonModule": true, 8 | "skipLibCheck": true, 9 | }, 10 | "include": [ 11 | "src/**/*" 12 | ], 13 | "references": [ 14 | { "path": "../core" } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "github>OpenZeppelin/configs" 4 | ], 5 | "ignoreDeps": [ 6 | "@openzeppelin/upgrades-core-legacy" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /scripts/check-diff-docs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Checks if Foundry docs are up to date. If this fails, commit the changes from the above commands. 4 | git diff --exit-code docs/modules/ROOT/pages/foundry -------------------------------------------------------------------------------- /scripts/prepare-foundry-docs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rm -rf docs/modules/ROOT/pages/foundry 4 | cp -r submodules/openzeppelin-foundry-upgrades/docs/modules docs/modules/ROOT/pages/foundry -------------------------------------------------------------------------------- /scripts/release/format-changelog.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Adjusts the format of the changelog that changesets generates. 4 | 5 | const { readFileSync, writeFileSync } = require('fs'); 6 | const { join } = require('path'); 7 | 8 | function formatChangelog(dir) { 9 | const changelogPath = join(dir, 'CHANGELOG.md'); 10 | 11 | const changelog = readFileSync(changelogPath, 'utf8'); 12 | 13 | // Groups: 14 | // - 1: Pull Request Number and URL 15 | // - 2: Changeset entry 16 | const RELEASE_LINE_REGEX = /^- (\[#.*?\]\(.*?\))?.*?! - (.*)$/gm; 17 | 18 | // Captures X.Y.Z or X.Y.Z-rc.W 19 | const VERSION_TITLE_REGEX = /^## (\d+\.\d+\.\d+(-rc\.\d+)?)$/gm; 20 | 21 | const formatted = changelog 22 | // Remove titles 23 | .replace(/^### Major Changes\n\n/gm, '') 24 | .replace(/^### Minor Changes\n\n/gm, '') 25 | .replace(/^### Patch Changes\n\n/gm, '') 26 | // Remove extra whitespace between items 27 | .replace(/^(- \[.*\n)\n(?=-)/gm, '$1') 28 | // Format each release line 29 | .replace(RELEASE_LINE_REGEX, (_, pr, entry) => (pr ? `- ${entry} (${pr})` : `- ${entry}`)) 30 | // Add date to new version 31 | .replace(VERSION_TITLE_REGEX, `\n## $1 (${new Date().toISOString().split('T')[0]})`); 32 | 33 | writeFileSync(changelogPath, formatted); 34 | } 35 | 36 | const packageFolders = ['core', 'plugin-hardhat']; 37 | for (const folder of packageFolders) { 38 | console.log(`Formatting changelog for ${folder}...`); 39 | const packageDir = join('./packages', folder); 40 | formatChangelog(packageDir); 41 | } 42 | -------------------------------------------------------------------------------- /scripts/release/publish.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | yarn install --frozen-lockfile 6 | changeset publish 7 | git push --follow-tags 8 | -------------------------------------------------------------------------------- /scripts/release/version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | changeset version 6 | 7 | node scripts/release/format-changelog.js 8 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "es2020", 5 | "module": "nodenext", 6 | "strict": true, 7 | "moduleResolution": "nodenext", 8 | "esModuleInterop": true, 9 | "sourceMap": true, 10 | "declaration": true, 11 | "declarationMap": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { "path": "packages/core" }, 4 | { "path": "packages/plugin-hardhat" } 5 | ], 6 | "files": [] 7 | } 8 | --------------------------------------------------------------------------------