Skip to content

Commit 7515916

Browse files
authored
Verify contracts with sourcify (#574)
* feat: add hardhat task to verify contracts with sourcify Signed-off-by: Tomás Migone <[email protected]>
1 parent acd6879 commit 7515916

File tree

5 files changed

+120
-1
lines changed

5 files changed

+120
-1
lines changed

hardhat.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const SKIP_LOAD = process.env.SKIP_LOAD === 'true'
2828

2929
function loadTasks() {
3030
require('./tasks/gre.ts')
31-
;['contracts', 'misc', 'deployment', 'actions'].forEach((folder) => {
31+
;['contracts', 'misc', 'deployment', 'actions', 'verify'].forEach((folder) => {
3232
const tasksPath = path.join(__dirname, 'tasks', folder)
3333
fs.readdirSync(tasksPath)
3434
.filter((pth) => pth.includes('.ts'))
@@ -103,6 +103,9 @@ const config: HardhatUserConfig = {
103103
enabled: true,
104104
runs: 200,
105105
},
106+
metadata: {
107+
useLiteralContent: true,
108+
},
106109
outputSelection: {
107110
'*': {
108111
'*': ['storageLayout'],

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"eslint-plugin-no-only-tests": "^2.4.0",
4949
"eslint-plugin-prettier": "^3.4.0",
5050
"ethereum-waffle": "^3.3.0",
51+
"form-data": "^4.0.0",
5152
"graphql-tag": "^2.12.4",
5253
"hardhat": "^2.9.5",
5354
"hardhat-abi-exporter": "^2.2.0",

tasks/verify/sourcify.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import axios from 'axios'
3+
import FormData from 'form-data'
4+
import { HardhatRuntimeEnvironment } from 'hardhat/types'
5+
import { Readable } from 'stream'
6+
7+
// Inspired by:
8+
// - https://github.com/wighawag/hardhat-deploy/blob/9c8cd433a37188e793181b727222e2d22aef34b0/src/sourcify.ts
9+
// - https://github.com/zoey-t/hardhat-sourcify/blob/26f10a08eb6cf97700c78989bf42b009c9cb3275/src/sourcify.ts
10+
export async function submitSourcesToSourcify(
11+
hre: HardhatRuntimeEnvironment,
12+
contract: {
13+
source: string
14+
name: string
15+
address: string
16+
fqn: string
17+
},
18+
): Promise<void> {
19+
const chainId = hre.network.config.chainId
20+
const sourcifyUrl = 'https://sourcify.dev/server/'
21+
22+
// Get contract metadata
23+
const contractBuildInfo = await hre.artifacts.getBuildInfo(contract.fqn)
24+
const contractMetadata = (
25+
contractBuildInfo.output.contracts[contract.source][contract.name] as any
26+
).metadata
27+
28+
if (contractMetadata === undefined) {
29+
console.error(
30+
`Contract ${contract.name} was deployed without saving metadata. Cannot submit to sourcify, skipping.`,
31+
)
32+
return
33+
}
34+
35+
// Check if contract already verified
36+
try {
37+
const checkResponse = await axios.get(
38+
`${sourcifyUrl}checkByAddresses?addresses=${contract.address.toLowerCase()}&chainIds=${chainId}`,
39+
)
40+
const { data: checkData } = checkResponse
41+
if (checkData[0].status === 'perfect') {
42+
console.log(`already verified: ${contract.name} (${contract.address}), skipping.`)
43+
return
44+
}
45+
} catch (e) {
46+
console.error(((e as any).response && JSON.stringify((e as any).response.data)) || e)
47+
}
48+
49+
console.log(`verifying ${contract.name} (${contract.address} on chain ${chainId}) ...`)
50+
51+
// Build form data
52+
const formData = new FormData()
53+
formData.append('address', contract.address)
54+
formData.append('chain', chainId)
55+
56+
const fileStream = new Readable()
57+
fileStream.push(contractMetadata)
58+
fileStream.push(null)
59+
formData.append('files', fileStream)
60+
61+
// Verify contract
62+
try {
63+
const submissionResponse = await axios.post(sourcifyUrl, formData, {
64+
headers: formData.getHeaders(),
65+
})
66+
const { status } = submissionResponse.data.result[0]
67+
if (status === 'perfect') {
68+
console.log(` => contract ${contract.name} is now verified`)
69+
} else if (status === 'partial') {
70+
console.log(` => contract ${contract.name} is partially verified`)
71+
} else {
72+
console.error(` => contract ${contract.name} is not verified`)
73+
}
74+
} catch (e) {
75+
console.error(((e as any).response && JSON.stringify((e as any).response.data)) || e)
76+
}
77+
}

tasks/verify/verify.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { task } from 'hardhat/config'
2+
import * as types from 'hardhat/internal/core/params/argumentTypes'
3+
import { submitSourcesToSourcify } from './sourcify'
4+
import { isFullyQualifiedName, parseFullyQualifiedName } from 'hardhat/utils/contract-names'
5+
import { TASK_COMPILE } from 'hardhat/builtin-tasks/task-names'
6+
import fs from 'fs'
7+
8+
task('sourcify', 'Verifies contract on sourcify')
9+
.addPositionalParam('address', 'Address of the smart contract to verify', undefined, types.string)
10+
.addParam('contract', 'Fully qualified name of the contract to verify.', undefined, types.string)
11+
.setAction(async (args, hre) => {
12+
if (!isFullyQualifiedName(args.contract)) {
13+
throw new Error('Invalid fully qualified name of the contract.')
14+
}
15+
16+
const { contractName, sourceName: contractSource } = parseFullyQualifiedName(args.contract)
17+
18+
if (!fs.existsSync(contractSource)) {
19+
throw new Error(`Contract source ${contractSource} not found.`)
20+
}
21+
22+
await hre.run(TASK_COMPILE)
23+
await submitSourcesToSourcify(hre, {
24+
source: contractSource,
25+
name: contractName,
26+
address: args.address,
27+
fqn: args.contract,
28+
})
29+
})

yarn.lock

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5402,6 +5402,15 @@ form-data@^3.0.0:
54025402
combined-stream "^1.0.8"
54035403
mime-types "^2.1.12"
54045404

5405+
form-data@^4.0.0:
5406+
version "4.0.0"
5407+
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
5408+
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
5409+
dependencies:
5410+
asynckit "^0.4.0"
5411+
combined-stream "^1.0.8"
5412+
mime-types "^2.1.12"
5413+
54055414
form-data@~2.3.2:
54065415
version "2.3.3"
54075416
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"

0 commit comments

Comments
 (0)