文章目录
- golang验证Etherscan上的智能合约
- 为什么要验证智能合约
- 如何使用golang去验证合约
- 获取EtherscanAPI密钥
- Verify Source Code接口
- Check Source Code Verification Status接口
- 演示示例及注意事项
- 网络问题无法调用Etherscan接口(最重要的步骤)
golang验证Etherscan上的智能合约
在阅读此文章前,您需要掌握一定的基础知识,如golang与以太坊交互-CSDN博客,此篇文章是对其的补充,提供利用代码自动验证智能合约,减少不太必要的人工操作,如果由于Etherscan接口的更新,导致代码不适用,请随时与我联系。
为什么要验证智能合约
出于多种原因,您可能希望在公共区块浏览器上开源(验证)您的智能合约。
在Etherscan或其他类似的区块链浏览器上验证智能合约具有以下几个重要的用途:
- 透明度和信任:通过验证智能合约,你向社区展示了你的合约代码是公开的、可审查的。这增加了用户和其他开发者对你项目的信任,因为他们可以查看代码,确认合约行为的逻辑和功能。
- 安全性审查:验证后的智能合约会吸引更多人的关注,特别是智能合约专家和安全研究人员。他们可以帮助发现潜在的安全漏洞或问题,并提供改进建议。这有助于提升你的合约的安全性和可靠性。
- 抵抗黑客攻击:通过让更多人审查你的合约代码,可以提前发现和修复可能存在的漏洞,从而降低黑客攻击的风险。黑客往往会寻找未经审查或有漏洞的智能合约来进行攻击,而经过验证的合约能够减少这种风险。
- 开发者社区增长:开源并验证的智能合约能够鼓励更多开发者参与到你的项目中来。他们可以基于你的代码进行二次开发、添加新功能或者将你的合约作为基础构建新的应用程序,从而推动生态系统的发展和扩展。
总结来说,通过在Etherscan上验证智能合约,你不仅增加了透明度和安全性,还能吸引更多开发者和用户参与到你的区块链项目中来,推动项目的发展和采纳。
如何使用golang去验证合约
如果你是通过在线工具如Remix或OpenZeppelin的合约向导部署了你的合约,请考虑使用合约验证页面进行验证。Verify & Publish Contract Source Code | Etherscan
如果你是通过开发工具如Hardhat、Foundry、Truffle等部署了你的合约,请考虑使用插件来自动化验证过程。Contract Verification Plugins | Etherscan
如果你是一名使用golang去开发Web3应用的开发者或学习者,我们可以查看Etherscan提供的api接口,然后根据需要,自己造轮子。Contracts | Etherscan
这里我们找到验证合约所必要的两个api接口,Verify Source Code和Check Source Code Verification Status,意如其名,一个是将合约源代码提交给类似 Etherscan 的浏览器进行验证,一个是返回合同验证请求的成功或错误状态。
只要这两个接口都返回成功,我们的智能合约就已经在Etherscan上进行了验证。
获取EtherscanAPI密钥
注册登录->仪表盘->API-KEYs->Add->copy,具体请看链接Getting an API key | Etherscan。
Verify Source Code接口
将合约源代码提交给类似 Etherscan 的浏览器进行验证。
根据官方提供的api接口详情,我们可以造出以下的代码,但是略有不同,上面表单中没有提供OptimizationUsed这个参数(0没有,1有),但是我在使用postman测试的时候,得到必须添加这个参数的结果,此外,这个接口必须使用POST请求,尽管它更像是GET请求,没有Body,只有Params。
package utils
import (
"encoding/json"
"fmt"
"io"
"math/big"
"net/http"
"net/url"
"reflect"
)
type VerifyRequest struct {
ApiKey string `json:"apiKey"` // API密钥
ChainId *big.Int `json:"chainId"` // 链ID
CodeFormat string `json:"codeformat"` // 代码格式
SourceCode string `json:"sourceCode"` // 合约源代码
ConstructorArguments string `json:"constructorArguements,omitempty"` // 构造函数参数
ContractAddress string `json:"contractaddress"` // 合约地址
ContractName string `json:"contractname"` // 合约名称
CompilerVersion string `json:"compilerversion"` // 编译器版本
OptimizationUsed int `json:"OptimizationUsed"` // 是否使用了优化
}
type VerifyResponse struct {
Status string `json:"status"` // 状态
Message string `json:"message"` // 详细信息
Result string `json:"result"` // 具体结果
}
func Verify(apiKey string, chainId *big.Int, codeFormat, sourceCode, constructorArgs, contractAddress, contractName, compilerVersion string, optimizationUsed int) error {
// 构造请求数据
requestData := VerifyRequest{
ApiKey: apiKey,
ChainId: chainId,
CodeFormat: codeFormat,
SourceCode: sourceCode,
ConstructorArguments: constructorArgs,
ContractAddress: contractAddress,
ContractName: contractName,
CompilerVersion: compilerVersion,
OptimizationUsed: optimizationUsed,
}
// 创建一个 Client 实例
client := &http.Client{}
// 准备查询参数
params := url.Values{}
// 使用反射获取requestData结构体中的字段和值
val := reflect.ValueOf(requestData)
// 如果是指针类型,则获取其指向的值
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
for i := 0; i < val.NumField(); i++ {
field := val.Type().Field(i)
value := val.Field(i).Interface()
// 获取标签的值
tag := field.Tag.Get("json")
if tag == "" {
// 如果没有标签,默认使用字段名
tag = field.Name
}
// 将字段名和值添加到查询参数中
params.Set(tag, fmt.Sprint(value))
}
// 构建POST请求的URL
apiURL := "https://api.etherscan.io/api?module=contract&action=verifysourcecode&" + params.Encode()
// 创建POST请求
req, err := http.NewRequest("POST", apiURL, nil)
if err != nil {
return fmt.Errorf("创建POST请求失败:%v", err)
}
// 发送POST请求到Etherscan API
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("POST请求失败:%v", err)
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("读取响应体失败:%v", err)
}
// 解析JSON响应
var verifyResponse VerifyResponse
err = json.Unmarshal(body, &verifyResponse)
if err != nil {
return fmt.Errorf("解析JSON响应失败:%v", err)
}
// 检查验证提交状态
if verifyResponse.Status != "1" {
return fmt.Errorf("验证提交失败:%s,%s\n", verifyResponse.Message, verifyResponse.Result)
}
fmt.Printf("验证提交成功:%s,%s\n", verifyResponse.Message, verifyResponse.Result)
return nil
}
其实在这里,也可以把verifyResponse.Result返回出去,因为接下来的检查验证会用到它。总之根据自己所需,可以随意更改函数形式,代码的灵活性和趣味性不尽也如此了吧。
参数列表:
参数 | 类型 | 描述 | 举例 |
---|---|---|---|
apiKey | string | Etherscan的Api密钥,注册账户免费获取 | ********************************** |
chainId | *big.Int | 提交验证的链,例如主网1 | big.NewInt(int64(11155111)) |
codeFormat | string | 单个文件,使用solidity-single-file 、使用JSON文件solidity-standard-json-input | solidity-single-file |
sourceCode | string | Solidity 源代码 | // SPDX-License-Identifier…… |
constructorArgs | string | 可选,如果合约使用构造函数参数,则包括 | nil |
contractAddress | string | 您的合约部署地址 | 0x**************************************** |
contractName | string | 合同的名称,例如contracts/Verified.sol:Verified | Verified |
compilerVersion | string | 使用的编译器版本,例如v0.8.26+commit.8a97fa7a | v0.8.26+commit.8a97fa7a |
optimizationUsed | int | 是否使用了优化,否0 ,是1 | 0 |
补充:
获取solc编译器版本:打开cmd,输入
solc --version
Check Source Code Verification Status接口
返回合同验证请求的成功或错误状态。
这个接口相对于上一个,就好写很多,只是简单的一个GET请求,我们很容易地写出以下代码。
package utils
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
type CheckVerificationStatusRequest struct {
ApiKey string `json:"apiKey"` // API密钥
Guid string `json:"guid"` // 从验证请求收到的唯一值
}
type CheckVerificationStatusResponse struct {
Status string `json:"status"` // 状态
Message string `json:"message"` // 详细信息
Result string `json:"result"` // 具体结果
}
func CheckVerificationStatus(apiKey, guid string) error {
requestData := CheckVerificationStatusRequest{
ApiKey: apiKey,
Guid: guid,
}
// 创建一个 Client 实例
client := &http.Client{}
// 构建GET请求的URL
apiURL := "https://api.etherscan.io/api?module=contract&action=checkverifystatus&guid=" + requestData.Guid + "&apikey=" + requestData.ApiKey
// 创建GET请求
req, err := http.NewRequest("GET", apiURL, nil)
if err != nil {
return fmt.Errorf("创建GET请求失败:%v", err)
}
// 发送GET请求到Etherscan API
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("GET请求失败:%v", err)
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("读取响应体失败:%v", err)
}
// 解析JSON响应
var checkVerificationStatusResponse CheckVerificationStatusResponse
err = json.Unmarshal(body, &checkVerificationStatusResponse)
if err != nil {
return fmt.Errorf("解析JSON响应失败:%v", err)
}
// 检查验证状态
if checkVerificationStatusResponse.Status != "1" {
return fmt.Errorf("验证失败:%s,%s\n", checkVerificationStatusResponse.Message, checkVerificationStatusResponse.Result)
}
fmt.Printf("验证成功:%s,%s\n", checkVerificationStatusResponse.Message, checkVerificationStatusResponse.Result)
return nil
}
通过这个函数,我们可以查询我们提交的验证是否已经通过了。
参数列表:
参数 | 类型 | 描述 | 举例 |
---|---|---|---|
apiKey | string | Etherscan的Api密钥,注册账户免费获取 | ********************************** |
guid | string | 从验证请求收到的唯一值guid | pdresyk3uidtwtcn5qxp3gqyp4ifsughl9hr8xdt3t2iw7acug |
演示示例及注意事项
然后我用我在上一篇博客,也就是文章最开始的链接中的这个样例合约(已经部署在Sepolia上,但未进行验证),进行演示。
下面是调用Verify()函数的过程,(注意,因为网络问题,所以我对这个函数进行了小小的修改,所以叫VerifyZh()函数,后面会进行说明的),然后我们得到返回的guid标识。
接着,下面是调用CheckVerificationStatus()函数的过程,(注意,因为网络问题,所以我对这个函数进行了小小的修改,所以叫CheckVerificationStatusZh()函数,后面会进行说明的),然后我们看到我们的验证已经完成了!
当然也有可能失败,因为验证是需要排队的,等待一段时间,再次查询就好了。
然后打开Etherscan,查看我们的合约,可以看到它的Contract上多了一个√,我们点击后,就可以看到我们合约的源代码以及其他信息了。
注意
无法验证合约,有很多原因,请根据报错信息自行摸索,或者在下方留言。
此外Etherscan很聪明,如果别人已经验证过一个合约,然后你部署了跟他一模一样的字节码,就可能导致你部署的合约不用验证,就已经验证了。
网络问题无法调用Etherscan接口(最重要的步骤)
打开魔法软件面板,点击设置,查看代理端口。
或者,打开windows设置,点击网络和Internet->代理->手动设置代理服务器->编辑,查看端口。
然后将之前的代码中的如下代码
// 创建一个 Client 实例
client := &http.Client{}
替换为(注意端口号!!!)
// 创建一个自定义的 Transport 实例
transport := &http.Transport{
Proxy: func(req *http.Request) (*url.URL, error) {
return url.Parse("http://127.0.0.1:7897") // 设置代理(注意端口号!!!)
},
}
// 创建一个自定义的 Client 实例
client := &http.Client{
Transport: transport, // 设置 Transport
}
就可以了,我的加了Zh的函数就是修改了这段代码。
上述方法适用于大部分因网络问题,无法调用第三方api接口的问题。
这篇文章到这里就结束了,希望对您有所帮助,欢迎点赞、评论加收藏,您的支持是对我最大的帮助!