Verify Contract With Hardhat

Verified

TL;DR;

6月1日那天,我正式决定先续约三个月工作室,同时为了防止在劣币圈里被驱使,最终导致劣化,暂时不接受不合适的工作。我开始为之前的项目收尾,同时继续将个人项目深入开发。这是一个迂回的反复尝试的过程,在进入个人饥饿期中,是一种不错的知识体系升级。
我从4月初开始,认真学习编写Solidity,虽然之前也是通过remix简单玩耍过,在过去的差不多一年时间里,也一直作为代码的reviewer查看合作的小博士写的合约,但真正想要承接核心逻辑,是从4月初才开始的。对各个部分的理解在逐步加深。下面记录一下如何verify发布的合约。

Verify with online tools

我首先在Base Sepolia的basescan上的在线工具尝试,在填写了部署好的合约地址后,选取了Solidity Single File,版本为0.8.20+,随后的界面中,需要粘贴合约源代码。
第一次尝试时我粘贴了原始的合约代码,得到结果是:

1
2
3
4
5
ParserError: Source "erc721a/contracts/ERC721A.sol" not found: File import callback not supported
--> myc:6:1:
|
6 | import "erc721a/contracts/ERC721A.sol"
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

原因是这里的源代码不支持import的模块,问询GPT后,发现可以通过flatten 方式,将所有引入的部分进行封装,为了满足验证时的类型说明,调用办法为:

1
npx hardhat flatten contracts/Foo.sol > Flattened.sol

我严格遵守了GPT给出的建议,移除多个interface和library声明的重复SPDX-License-Identifier, 以及pragma solidity,
但是仍然有问题:

1
2
Found the following ContractName(s) in source code : Context, ERC721A, ERC721A__IERC721Receiver, IERC721A, Math, Ownable, SignedMath, Strings
But we were unable to locate a matching bytecode (err_code_2)

在flatten之后的代码中,我明明十分清楚地看到了提及名称的定义代码,但是他就是十分执着地视而不见,我决定看看另外的一种办法,利用hardhat工具链中的hardhat-verify。

hardhat-verify

  1. 安装工具包
1
npm i --save-dev @nomicfoundation/hardhat-verify
  1. 在配置文件,hardhat.config.ts中,增加对应配置,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import '@nomicfoundation/hardhat-verify';
import '@nomicfoundation/hardhat-toolbox';
import { HardhatUserConfig } from 'hardhat/config';
import dotenv from 'dotenv';
dotenv.config();

const config: HardhatUserConfig = {
solidity: {
compilers: [{ version: '0.8.20' }, { version: '0.4.18' }],
settings: {
optimizer: {
enabled: true,
runs: 1000,
},
},
},
networks: {
sepolia: {
url: `https://eth-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_SEPOLIA_API_KEY}`,
accounts: [process.env.SEPOLIA_ACCOUNT_PRIVATE_KEY!],
chainId: 11155111,
},
baseSepolia: {
url: `https://sepolia.base.org`,
accounts: [process.env.SEPOLIA_ACCOUNT_PRIVATE_KEY!],
chainId: 84532,
},
},
sourcify: {
enabled: true,
},
etherscan: {
apiKey: {
baseSepolia: process.env.BASESCAN_API_KEY!,
},
customChains: [
{
network: "baseSepolia",
chainId: 84532,
urls: {
apiURL: "https://api-sepolia.basescan.org/api",
browserURL: "https://sepolia.basescan.org"
}
}
]
},
};

export default config;
  1. task冲突的问题

这里hardhat自己的工具链中会有冲突,如果直接这样保存运行任何hardhat命令,都会发生如下问题:

1
Error HH209: Redefinition of task verify:get-contract-information failed. Unsupported operation adding mandatory (non optional) param definitions in an overridden task.

进一步查询后很多人说hardhat-verify和hardhat-toolbox中定义的task冲突,后发现npx hardhat verify支持参数 –config,所以可以将配置文件拆分,既然你们冲突,verify的单独建立一个就可以了。以下为verify.config.ts。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import '@nomicfoundation/hardhat-verify';
import { HardhatUserConfig } from 'hardhat/config';
import dotenv from 'dotenv';
dotenv.config();

const config: HardhatUserConfig = {
solidity: {
compilers: [{ version: '0.8.20' }, { version: '0.4.18' }],
settings: {
optimizer: {
enabled: true,
runs: 1000,
},
},
},
networks: {
sepolia: {
url: `https://eth-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_SEPOLIA_API_KEY}`,
accounts: [process.env.SEPOLIA_ACCOUNT_PRIVATE_KEY!],
chainId: 11155111,
},
baseSepolia: {
url: `https://sepolia.base.org`,
accounts: [process.env.SEPOLIA_ACCOUNT_PRIVATE_KEY!],
chainId: 84532,
}
},
sourcify: {
enabled: true,
},
etherscan: {
apiKey: {
baseSepolia: process.env.BASESCAN_API_KEY!,
},
customChains: [
{
network: "baseSepolia",
chainId: 84532,
urls: {
apiURL: "https://api-sepolia.basescan.org/api",
browserURL: "https://sepolia.basescan.org"
}
},
]
},
};

export default config;
  1. 准备部署合约时的constructor arguments

hardhat verify可以指定自定义一个arg.js传入参数,对准备创建合约时候的参数十分友好。
一个参数例子:

1
2
3
4
5
6
7
module.exports = [
'StringArg1',//字符串参数
BigInt(100) * 1000n * 10n ** 18n, //因为是js,可以写计算单位
[
'0x0000',//address 数组
],
]
  1. 完整调用,加上–verbose方便诊断
    1
    npx hardhat verify --config verify.config.ts --network baseSepolia DEPLOYED_CONTRACT_ADDRESS --constructor-args args.js --verbose

这次调用后,得到了一个新错误:

1
The HTTP server response is not ok. Status code: 500 Response text: {"error":"Invalid chainIds: 84532","message":"Invalid chainIds: 84532"}

因为开启了verbose,可以看到其本质上是一个工具链,其中在调用一个服务时,
https://sourcify.dev/server/check-all-by-addresses 时,返回值说明这个chainId并没有被支持。
我们来到其主页时,调用了它的公共api中的
https://sourcify.dev/server/chains, 看到其中Base Sepolia不在被支持之列。但是Base自己本身是在的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"name": "Base",
"chainId": 8453,
"rpc": [
"https://mainnet.base.org/",
"https://developer-access-mainnet.base.org/",
"https://base.gateway.tenderly.co",
"wss://base.gateway.tenderly.co",
"https://base-rpc.publicnode.com",
"wss://base-rpc.publicnode.com"
],
"supported": true,
"etherscanAPI": "https://api.basescan.org/"
}

这可如何是好。

借尸还魂/借鸡生蛋

诸多L2的链都对标准的solidity合约兼容,所以说理论上我的合约可以发布到任何一个热门的L2上,只要其可以通过合约验证,说明我们的合约在规范层面是不存在什么问题的。于是我去看了一下热门的L2都有哪些,再去sourcify的支持列表看看支持情况。最终选定了Arbitrum Sepolia,获取对应的测试ETH后,在配置文件中新增network配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  ...
networks: {
sepolia: {
url: `https://eth-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_SEPOLIA_API_KEY}`,
accounts: [process.env.SEPOLIA_ACCOUNT_PRIVATE_KEY!],
chainId: 11155111,
},
baseSepolia: {
url: `https://sepolia.base.org`,
accounts: [process.env.SEPOLIA_ACCOUNT_PRIVATE_KEY!],
chainId: 84532,
},
arbitrumSepolia: {
url: `https://arb-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`,
accounts: [process.env.SEPOLIA_ACCOUNT_PRIVATE_KEY!],
chainId: 421614,
},
},
sourcify: {
enabled: true,
},
etherscan: {
apiKey: {
baseSepolia: process.env.ETHERSCAN_API_KEY!,
arbitrumSepolia: process.env.ARBITRUMSCAN_SEPOLIA_API_KEY,
},
customChains: [
{
network: "baseSepolia",
chainId: 84532,
urls: {
apiURL: "https://api-sepolia.basescan.org/api",
browserURL: "https://sepolia.basescan.org"
}
},
{
network: "arbitrumSepolia",
chainId: 421614,
urls: {
apiURL: "https://api-sepolia.arbiscan.io/api",
browserURL: "https://sepolia.arbiscan.io/"
}
}
]
},

随后这次,我们再使用arbitrumSepolia网络调用verify,在工具链最后一步还是会报错,我已经准备放弃了,但是我却看到这么一行。

1
2
The contract 0x0 has already been verified on the block explorer.
https://sepolia.arbiscan.io/address/0x0#code

管他的,去查看后,确实发现合约已经被verified了。

未解之谜

  1. 继续尝试使用在线工具验证
  2. 看看hardhat-verify在验证之后还在搞什么鬼导致最后执行错误。