Skip to content

Commit

Permalink
Defender: Improve license type handling (#1013)
Browse files Browse the repository at this point in the history
  • Loading branch information
ericglau authored Apr 22, 2024
1 parent 20f0207 commit 0f6bf7b
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 28 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = {
'unicorn/no-array-reduce': 'warn',
'no-plusplus': ['warn', { allowForLoopAfterthoughts: true }],
},
ignorePatterns: ['submodules'],
overrides: [
{
files: ['*.ts'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ struct DefenderOptions {
string relayerId;
bytes32 salt;
string upgradeApprovalProcessId;
string licenseType;
bool skipLicenseType;
}
```

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

## 3.1.0 (2024-04-22)

- Defender: Fix handling of license types for block explorer verification, support `licenseType` and `skipLicenseType` options. ([#1013](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/1013))

### Breaking changes
- When deploying through Defender, if your contract does not have an SPDX license identifier, the verified source code on Etherscan will no longer show any license type.
- If you want the license type to appear as "None", either set your contract to have `// SPDX-License-Identifier: UNLICENSED` according to [Solidity docs](https://docs.soliditylang.org/en/latest/layout-of-source-files.html#spdx-license-identifier), or set the `licenseType` option to `"None"`.

## 3.0.5 (2024-03-08)

- Simplify console logging for `admin.transferProxyAdminOwnership`. ([#978](https://github.com/OpenZeppelin/openzeppelin-upgrades/pull/978))
Expand Down
3 changes: 3 additions & 0 deletions packages/plugin-hardhat/contracts/NoLicense.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pragma solidity ^0.8.20;

contract NoLicense {}
4 changes: 4 additions & 0 deletions packages/plugin-hardhat/contracts/Unlicensed.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

contract Unlicensed {}
4 changes: 4 additions & 0 deletions packages/plugin-hardhat/contracts/UnrecognizedLicense.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// SPDX-License-Identifier: UnrecognizedId
pragma solidity ^0.8.20;

contract UnrecognizedLicense {}
2 changes: 1 addition & 1 deletion packages/plugin-hardhat/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openzeppelin/hardhat-upgrades",
"version": "3.0.5",
"version": "3.1.0",
"description": "",
"repository": "https://github.com/OpenZeppelin/openzeppelin-upgrades/tree/master/packages/plugin-hardhat",
"license": "MIT",
Expand Down
93 changes: 76 additions & 17 deletions packages/plugin-hardhat/src/defender/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,39 @@ export async function defenderDeploy(
const verifySourceCode = opts.verifySourceCode ?? true;
debug(`Verify source code: ${verifySourceCode}`);

let license: string | undefined = undefined;
if (verifySourceCode) {
license = getLicenseFromMetadata(contractInfo);
debug(`License type: ${license}`);
}

if (opts.salt !== undefined) {
debug(`Salt: ${opts.salt}`);
}

if (opts.licenseType !== undefined) {
if (opts.verifySourceCode === false) {
throw new UpgradesError('The `licenseType` option cannot be used when the `verifySourceCode` option is `false`');
} else if (opts.skipLicenseType) {
throw new UpgradesError('The `licenseType` option cannot be used when the `skipLicenseType` option is `true`');
}
}

let licenseType: SourceCodeLicense | undefined = undefined;
if (verifySourceCode) {
if (opts.licenseType !== undefined) {
licenseType = opts.licenseType;
debug(`licenseType option: ${licenseType}`);
} else if (!opts.skipLicenseType) {
const spdxIdentifier = getSpdxLicenseIdentifier(contractInfo);
debug(`SPDX license identifier from metadata: ${spdxIdentifier}`);
if (spdxIdentifier !== undefined) {
licenseType = toLicenseType(spdxIdentifier, contractInfo);
debug(`licenseType inferred: ${licenseType}`);
}
}
}

const deploymentRequest: DeployContractRequest = {
contractName: contractInfo.contractName,
contractPath: contractInfo.sourceName,
network: network,
artifactPayload: JSON.stringify(contractInfo.buildInfo),
licenseType: license as SourceCodeLicense | undefined, // cast without validation but catch error from API below
licenseType: licenseType,
constructorInputs: constructorArgs,
verifySourceCode: verifySourceCode,
relayerId: opts.relayerId,
Expand All @@ -101,8 +118,9 @@ export async function defenderDeploy(
} catch (e: any) {
if (e.response?.data?.message?.includes('licenseType should be equal to one of the allowed values')) {
throw new UpgradesError(
`License type ${license} is not a valid SPDX license identifier for block explorer verification.`,
() => 'Specify a valid SPDX-License-Identifier in your contract.',
`The licenseType option "${licenseType}" is not valid for block explorer verification.`,
() =>
'See https://etherscan.io/contract-license-types for supported values and use the string found in brackets, e.g. "MIT"',
);
} else {
throw e;
Expand Down Expand Up @@ -197,9 +215,9 @@ async function getContractInfo(
}

/**
* Get the license type from the contract metadata without validating its validity, except converts undefined or UNLICENSED to None.
* Get the SPDX license identifier from the contract metadata without validating it.
*/
function getLicenseFromMetadata(contractInfo: ContractInfo): string {
function getSpdxLicenseIdentifier(contractInfo: ContractInfo): string | undefined {
const compilerOutput: CompilerOutputWithMetadata =
contractInfo.buildInfo.output.contracts[contractInfo.sourceName][contractInfo.contractName];

Expand All @@ -213,11 +231,52 @@ function getLicenseFromMetadata(contractInfo: ContractInfo): string {

const metadata = JSON.parse(metadataString);

const license: string = metadata.sources[contractInfo.sourceName].license;
if (license === undefined || license === 'UNLICENSED') {
// UNLICENSED means no license according to solidity docs
return 'None';
} else {
return license;
return metadata.sources[contractInfo.sourceName].license;
}

/**
* Infers a SourceCodeLicense from an SPDX license identifier.
*/
function toLicenseType(spdxIdentifier: string, contractInfo: ContractInfo): SourceCodeLicense {
switch (spdxIdentifier) {
case 'UNLICENSED':
return 'None';
case 'Unlicense':
return 'Unlicense';
case 'MIT':
return 'MIT';
case 'GPL-2.0-only':
case 'GPL-2.0-or-later':
return 'GNU GPLv2';
case 'GPL-3.0-only':
case 'GPL-3.0-or-later':
return 'GNU GPLv3';
case 'LGPL-2.1-only':
case 'LGPL-2.1-or-later':
return 'GNU LGPLv2.1';
case 'LGPL-3.0-only':
case 'LGPL-3.0-or-later':
return 'GNU LGPLv3';
case 'BSD-2-Clause':
return 'BSD-2-Clause';
case 'BSD-3-Clause':
return 'BSD-3-Clause';
case 'MPL-2.0':
return 'MPL-2.0';
case 'OSL-3.0':
return 'OSL-3.0';
case 'Apache-2.0':
return 'Apache-2.0';
case 'AGPL-3.0-only':
case 'AGPL-3.0-or-later':
return 'GNU AGPLv3';
case 'BUSL-1.1':
return 'BSL 1.1';
default:
throw new UpgradesError(
`SPDX license identifier ${spdxIdentifier} in ${contractInfo.sourceName} does not look like a supported license for block explorer verification.`,
() =>
`Use the \`licenseType\` option to specify a license type, or set the \`skipLicenseType\` option to \`true\` to skip.`,
);
}
}
3 changes: 3 additions & 0 deletions packages/plugin-hardhat/src/utils/options.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SourceCodeLicense } from '@openzeppelin/defender-sdk-deploy-client';
import {
DeployOpts,
ProxyKindOption,
Expand Down Expand Up @@ -64,6 +65,8 @@ export type DefenderDeployOptions = DefenderDeploy & {
relayerId?: string;
salt?: string;
createFactoryAddress?: string;
licenseType?: SourceCodeLicense;
skipLicenseType?: boolean;
};

/**
Expand Down
Loading

0 comments on commit 0f6bf7b

Please sign in to comment.