-
Notifications
You must be signed in to change notification settings - Fork 170
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🗞️ Add verify-contract package (#390)
- Loading branch information
1 parent
3fb1788
commit 1ea1aae
Showing
40 changed files
with
13,316 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.turbo | ||
dist | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "../../.eslintrc.json" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
<p align="center"> | ||
<a href="https://layerzero.network"> | ||
<img alt="LayerZero" style="max-width: 500px" src="https://d3a2dpnnrypp5h.cloudfront.net/bridge-app/lz.png"/> | ||
</a> | ||
</p> | ||
|
||
<h1 align="center">@layerzerolabs/verify-contract</h1> | ||
|
||
<!-- The badges section --> | ||
<p align="center"> | ||
<!-- Shields.io NPM published package version --> | ||
<a href="https://www.npmjs.com/package/@layerzerolabs/verify-contract"><img alt="NPM Version" src="https://img.shields.io/npm/v/@layerzerolabs/verify-contract"/></a> | ||
<!-- Shields.io NPM downloads --> | ||
<a href="https://www.npmjs.com/package/@layerzerolabs/verify-contract"><img alt="Downloads" src="https://img.shields.io/npm/dm/@layerzerolabs/verify-contract"/></a> | ||
<!-- Shields.io license badge --> | ||
<a href="https://www.npmjs.com/package/@layerzerolabs/verify-contract"><img alt="NPM License" src="https://img.shields.io/npm/l/@layerzerolabs/verify-contract"/></a> | ||
</p> | ||
|
||
## Installation | ||
|
||
```bash | ||
yarn add @layerzerolabs/verify-contract | ||
|
||
pnpm add @layerzerolabs/verify-contract | ||
|
||
npm install @layerzerolabs/verify-contract | ||
``` | ||
|
||
## Usage | ||
|
||
### CLI | ||
|
||
This package comes with a CLI interface: | ||
|
||
```bash | ||
npx @layerzerolabs/verify-contract --help | ||
``` | ||
|
||
Using the CLI, contracts can be verified one network at a time. | ||
|
||
### Programatic usage | ||
|
||
The package provides two types of verification for hardhat deploy: _target_ and _non-target_. | ||
|
||
#### Target verification | ||
|
||
This is suitable for verifying contracts that have been the compilation targets for a deployment, i.e. they have their own deployment file. | ||
This is the default and easiest case for which we know all the information we need from the deployment file. | ||
|
||
```typescript | ||
import { verifyHardhatDeployTarget } from "@layerzerolabs/verify-contract"; | ||
|
||
// Programmatic usage allows for more fine-grained and multi-network verification | ||
verifyHardhatDeployTarget({ | ||
paths: { | ||
deployments: "./my/little/deployments/folder", | ||
}, | ||
networks: { | ||
whatachain: { | ||
apiUrl: "https://api.whatachain.io/api", | ||
apiKey: "david.hasselhoff.1234", | ||
}, | ||
}, | ||
// The filter option allows you to limit the scope of verification to | ||
// specific contracts | ||
// | ||
// It supports several ways of scoping the verification: | ||
// | ||
// A list of case-sensitive contract names | ||
filter: ["Factory", "Router"], | ||
// A single contract name | ||
filter: "ONFT1155", | ||
// Boolean to toggle the verification as a whole | ||
filter: false, | ||
// A function that gets passed the contract name and an relative contract path and returns a boolean to signify the contract needs to be verified | ||
filter: (name, path) => name.startsWith("Potato721"), | ||
}); | ||
``` | ||
|
||
#### Non-target verification | ||
|
||
This is suitable for verifying contracts that have been e.g. deployed dynamically from other contracts within the deployment. | ||
|
||
In this case we need to know more information - the specific deployment file to use, the address of the contract and also its constructor arguments. | ||
|
||
```typescript | ||
import { verifyHardhatDeployNonTarget } from "@layerzerolabs/verify-contract"; | ||
|
||
// Programmatic usage allows for more fine-grained and multi-network verification | ||
verifyHardhatDeployNonTarget({ | ||
paths: { | ||
deployments: "./my/little/deployments/folder", | ||
}, | ||
networks: { | ||
whatachain: { | ||
apiUrl: "https://api.whatachain.io/api", | ||
apiKey: "david.hasselhoff.1234", | ||
}, | ||
}, | ||
// The contracts array is used to pass the contract details | ||
contracts: [ | ||
{ | ||
address: "0x0", | ||
network: "whatachain", | ||
// We'll need to pass the name of the deployment file to use (relative to the deployments path) | ||
deployment: "OtherContract.json", | ||
constructorArguments: [1000, "0x0"], | ||
// In this case we'll need to pass a fully-qualified contract name | ||
contractName: "contracts/examples/Pool.sol", | ||
}, | ||
], | ||
}); | ||
``` | ||
|
||
### Default configuration | ||
|
||
The package is preconfigured for scan API URLs for several well-known networks: | ||
|
||
| Network | API URL | | ||
| -------------------------------------------------------------- | ------------------------------------------------ | | ||
| `avalanche`, `avalanche-mainnet` | `https://api.snowtrace.io/api` | | ||
| `fuji`, `avalanche-testnet` | `https://api-testnet.snowtrace.io/api` | | ||
| `bsc` | `https://api.bscscan.com/api` | | ||
| `bsc-testnet` | `https://api-testnet.bscscan.com/api` | | ||
| `ethereum` | `https://api.etherscan.io/api` | | ||
| `ethereum-goerli` | `https://api-goerli.etherscan.io/api` | | ||
| `goerli` | `https://api-goerli.etherscan.io/api` | | ||
| `fantom` | `https://api.ftmscan.com/api` | | ||
| `fantom-testnet` | `https://api-testnet.ftmscan.com/api` | | ||
| `arbitrum` | `https://api.arbiscan.io/api` | | ||
| `arbitrum-goerli` | `https://api-goerli.arbiscan.io/api` | | ||
| `polygon` | `https://api.polygonscan.com/api` | | ||
| `mumbai` | `https://api-testnet.polygonscan.com/api` | | ||
| `optimism` | `https://api-optimistic.etherscan.io/api` | | ||
| `optimism-goerli` | `https://api-goerli-optimistic.etherscan.io/api` | | ||
| `gnosis` | `https://api.gnosisscan.io/api` | | ||
| `zkpolygon`, `zkpolygon-mainnet` | `https://api-zkevm.polygonscan.com/api` | | ||
| `base`, `base-mainnet` | `https://api.basescan.org/api` | | ||
| `base-goerli` | `https://api-goerli.basescan.org/api` | | ||
| `zkconsensys`, `zkconsensys-mainnet`, `linea`, `linea-mainnet` | `https://api.lineascan.build/api` | | ||
| `moonbeam` | `https://api-moonbeam.moonscan.io/api` | | ||
| `moonbeam-testnet` | `https://api-moonbase.moonscan.io/api` | | ||
| `kava`, `kava-mainnet` | `https://kavascan.com/api` | | ||
| `kava-testnet` | `https://testnet.kavascan.com/api` | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#!/usr/bin/env node | ||
|
||
import('./dist/cli.js'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** @type {import('ts-jest').JestConfigWithTsJest} */ | ||
module.exports = { | ||
cache: false, | ||
testEnvironment: 'node', | ||
moduleNameMapper: { | ||
'^@/(.*)$': '<rootDir>/src/$1', | ||
}, | ||
transform: { | ||
'^.+\\.(t|j)sx?$': '@swc/jest', | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
{ | ||
"name": "@layerzerolabs/verify-contract", | ||
"version": "1.1.9", | ||
"description": "Verify Solidity contracts on supported block explorers", | ||
"bugs": { | ||
"url": "https://github.com/LayerZero-Labs/devtools/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/LayerZero-Labs/devtools.git", | ||
"directory": "packages/verify-contract" | ||
}, | ||
"license": "MIT", | ||
"main": "./dist/index.js", | ||
"module": "./dist/index.mjs", | ||
"types": "./dist.index.d.ts", | ||
"bin": "./cli.js", | ||
"files": [ | ||
"./dist/*", | ||
"./cli.js" | ||
], | ||
"scripts": { | ||
"build": "$npm_execpath tsup", | ||
"clean": "rm -rf dist", | ||
"dev": "$npm_execpath tsup --watch", | ||
"lint": "$npm_execpath eslint '**/*.{js,ts,json}'", | ||
"lint:fix": "eslint --fix '**/*.{js,ts,json}'", | ||
"start": "node ./cli.js", | ||
"test": "jest" | ||
}, | ||
"devDependencies": { | ||
"@ethersproject/abi": "^5.7.0", | ||
"@layerzerolabs/io-devtools": "~0.1.3", | ||
"@solidity-parser/parser": "^0.16.1", | ||
"@swc/core": "^1.4.0", | ||
"@swc/jest": "^0.2.36", | ||
"@types/jest": "^29.5.12", | ||
"@types/node": "^18.18.14", | ||
"chalk": "^4.1.2", | ||
"commander": "^11.1.0", | ||
"esbuild-plugin-copy": "~2.1.1", | ||
"got": "12.6.1", | ||
"jest": "^29.7.0", | ||
"tsup": "^8.0.1", | ||
"typescript": "^5.3.3", | ||
"zod": "^3.22.4" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import { Command, InvalidOptionArgumentError, Option } from 'commander' | ||
import { verifyTarget, verifyNonTarget } from './hardhat-deploy/verify' | ||
import { COLORS } from './common/logger' | ||
import { createLogger } from '@layerzerolabs/io-devtools' | ||
import { VerifyHardhatNonTargetConfig, type VerifyHardhatTargetConfig } from './hardhat-deploy/types' | ||
import { version } from '../package.json' | ||
|
||
const logLevelOption = new Option('-l,--log-level <level>', 'Log level') | ||
.choices(['error', 'warn', 'info', 'http', 'verbose', 'debug', 'silly']) | ||
.default('info') | ||
|
||
const deploymentsPathOption = new Option('-d,--deployments <path>', 'Path to the deployments folder') | ||
|
||
const dryRunOption = new Option( | ||
'--dry-run', | ||
'Do not verify anything, just output the verifications that would be performed' | ||
) | ||
|
||
const networkOption = new Option('-n,--network <network name>', 'Network to verify').makeOptionMandatory() | ||
|
||
const apiUrlOption = new Option('-u,--api-url <url>', 'Scan API URL (fully qualified, with protocol and path)') | ||
|
||
const apiKeyOption = new Option('-k,--api-key <key>', 'Scan API Key') | ||
|
||
const verifyNonTargetCommand = new Command('non-target') | ||
.description( | ||
'Verifies a contract that does not have its own deployment file, i.e. has not been a target of a deployment' | ||
) | ||
.addOption(logLevelOption) | ||
.addOption(dryRunOption) | ||
.addOption(deploymentsPathOption) | ||
.addOption(networkOption) | ||
.addOption(apiUrlOption) | ||
.addOption(apiKeyOption) | ||
.requiredOption('--address <address>', 'Contract address to verify') | ||
.requiredOption('--name <contract name>', 'Fully qualified contract name to verify, e.g. contracts/MyToken.sol') | ||
.requiredOption('--deployment <deployment file name>', 'Deployment file name, e.g. MyOtherToken.json') | ||
.option( | ||
'--arguments <constructor arguments>', | ||
'JSON encoded array of constructor arguments, e.g. [1234, "0x0"]', | ||
(value: string) => { | ||
try { | ||
const decoded = JSON.parse(value) | ||
if (!Array.isArray(decoded)) { | ||
throw new Error(`Constructor arguments must be an array, got ${decoded}`) | ||
} | ||
|
||
return decoded | ||
} catch (error) { | ||
throw new InvalidOptionArgumentError(`Malformed constructor arguments specified: ${error}`) | ||
} | ||
} | ||
) | ||
.action(async (args: any) => { | ||
const logger = createLogger(args.logLevel) | ||
const config: VerifyHardhatNonTargetConfig = { | ||
dryRun: args.dryRun, | ||
paths: { | ||
deployments: args.deployments, | ||
}, | ||
networks: { | ||
[args.network]: { | ||
apiUrl: args.apiUrl, | ||
apiKey: args.apiKey, | ||
}, | ||
}, | ||
contracts: [ | ||
{ | ||
network: args.network, | ||
address: args.address, | ||
contractName: args.name, | ||
deployment: args.deployment, | ||
constructorArguments: args.arguments, | ||
}, | ||
], | ||
} | ||
|
||
try { | ||
await verifyNonTarget(config, logger) | ||
} catch (error) { | ||
logger.error(COLORS.error`The verification script exited with an error: ${error}`) | ||
|
||
process.exit(1) | ||
} | ||
}) | ||
|
||
const verifyTargetCommand = new Command('target') | ||
.description('Verifies contracts that have been a part of a deployment, i.e. have their own deployment files') | ||
.addOption(logLevelOption) | ||
.addOption(dryRunOption) | ||
.addOption(deploymentsPathOption) | ||
.addOption(networkOption) | ||
.addOption(apiUrlOption) | ||
.addOption(apiKeyOption) | ||
.option( | ||
'-c,--contracts <contract names>', | ||
'Comma-separated list of case-sensitive contract names to verify', | ||
(value: string | null | undefined) => { | ||
return value?.trim() ? value.split(',').map((c: string) => c.trim()) : undefined | ||
} | ||
) | ||
.action(async (args: any) => { | ||
const logger = createLogger(args.logLevel) | ||
const config: VerifyHardhatTargetConfig = { | ||
dryRun: args.dryRun, | ||
paths: { | ||
deployments: args.deployments, | ||
}, | ||
networks: { | ||
[args.network]: { | ||
apiUrl: args.apiUrl, | ||
apiKey: args.apiKey, | ||
}, | ||
}, | ||
filter: args.contracts, | ||
} | ||
|
||
try { | ||
await verifyTarget(config, logger) | ||
} catch (error) { | ||
logger.error(COLORS.error`The verification script exited with an error: ${error}`) | ||
|
||
process.exit(1) | ||
} | ||
}) | ||
|
||
new Command('@layerzerolabs/verify-contract') | ||
.version(version) | ||
.description('Verify a set of contracts based on hardhat-deploy outputs') | ||
.addCommand(verifyNonTargetCommand) | ||
.addCommand(verifyTargetCommand, { isDefault: true }) | ||
.parseAsync() |
Oops, something went wrong.