导航
English

开发者工具

获取地址授权和代币授权详细信息,以及提供域名风险、广播交易上链、代理合约和普通合约验证等 API 工具。

合约验证

验证合约源代码是一种项目开源的方式,便于终端用户查看合约,提高合约的透明度。
该功能模块接口,支持提交合约源代码进行合约验证、验证代理合约、查询已验证合约的合约 ABI 和源代码。

验证合约源代码

通过上传合约源代码,OKLink 浏览器会将编译后的合约字节码和区块链上的字节码进行匹配,并将其显示在浏览器的合约页面中。
您可使用验证合约源代码 API,进行合约的快速验证,提高验证效率。合约验证的平均处理时间在 30-60s 之间。

每次调用消耗 0 点

HTTP请求

POST /api/v5/explorer/contract/verify-source-code

请求示例

POST /api/v5/explorer/contract/verify-source-code
body
{
    "chainShortName":"ETH",
    "contractAddress":"0x9Dca580D2c8B8e19e9d77345a5b4C2820B25b386",
    "contractName":"HelloWorld",
    "sourceCode":"pragma solidity ^0.7.6;↵contract HelloWorl {↵ string public greet = 'Hello Worl!';↵}",
    "codeFormat":"solidity-single-file",
    "compilerVersion":"v0.7.6+commit.7338295f",
    "optimization":"1",
    "optimizationRuns":"200",
    "contractAbi":"0xfce353f66162630000000000000000000000000",
    "evmVersion":"tangerineWhistle",
    "viaIr":false,
    "libraryInfo":[
        {
        "libraryName":"libraryName1",
        "libraryAddress":"0xCfE28868F6E0A24b7333D22D8943279e76aC2cdc"
        },
        {
        "libraryName":"libraryName2",
        "libraryAddress":"0xCfE28868F6E0A24b7333D22D8943279e76aC2cdc"
        },
        {
        "libraryName":"libraryName3",
        "libraryAddress":"0xCfE28868F6E0A24b7333D22D8943279e76aC2cdc"
        }
    ]
}

请求参数

参数名 类型 是否必须 描述
chainShortName String 公链缩写符号
contractAddress String 合约地址
contractName String 合约名称
sourceCode String 合约源代码;如果合约使用 imports,需要将代码连接成一个文件(即 flattening),可以使用 Solidity flatteners 工具SolidityFlattery(由@DaveAppleton开发)
codeFormat String 代码格式,支持solidity-single-filesolidity-standard-json-inputVyper
compilerVersion String 使用的编译器版本,如v0.7.6+commit.7338295fvyper:0.2.11,可在 OKLink 浏览器-合约验证查看支持的编译器版本;
codeFormat为solidity-standard-json-input时非必填,其他必填
optimization String 编译合约时是否使用了优化,0无优化,1有优化;
codeFormat为solidity-standard-json-input时非必填,其他必填
optimizationRuns String 执行优化时运行代码的次数
contractAbi String 合约ABI
evmVersion String 编译合约的EVM版本,若编译时使用了默认版本无需填写;其他指定版本如:tangerineWhistlespuriousDragonbyzantium
licenseType String 开源许可证类型
viaIr Bol 是否引入基于IR的代码生成器,请与编译时的设置保持一致;true / false,默认为false
libraryInfo Array 合约中引用的库的信息;libraryName和libraryAddress需要进行一一匹配;最多支持10个不同的库对
> libraryName String 库名称
> libraryAddress String 库地址,如0xCfE28868F6E0A24b7333D22D8943279e76aC2cdc

返回结果

{
    "code": "0",
    "msg": "",
    "data": [
        "eb5c06099d3841359d398541166343fe"
    ]
}

返回参数

参数名 类型 描述
guid String 若提交成功会返回GUID,可根据该GUID查询验证结果

查询合约源代码验证结果

您可以在提交源代码验证之后,根据返回的 GUID 查询验证结果。

每次调用消耗 0 点

HTTP请求

POST /api/v5/explorer/contract/check-verify-result

请求示例

POST /api/v5/explorer/contract/check-verify-result
body
{
    "chainShortName":"ETH",
    "guid":"eb5c06099d3841359d398541166343fe"
}

请求参数

参数名 类型 是否必须 描述
chainShortName String 公链缩写符号
guid String 根据GUID查询合约源代码验证结果

返回结果

{
    "code": "0",
    "msg": "",
    "data": [
        "Success"
    ]
}

返回参数

参数名 类型 描述
result String 合约源代码验证结果;Success代表验证通过,Fail代表未通过验证,Pending代表验证中

验证代理合约

验证代理合约是否按照预期调用实现合约。

每次调用消耗 0 点

HTTP请求

POST /api/v5/explorer/contract/verify-proxy-contract

请求示例

POST /api/v5/explorer/contract/verify-proxy-contract
body
{
    "chainShortName": "ETH",
    "proxyContractAddress": "0xfeee12d53ddb7ce61ee467ddf7243212a953174a",
    "expectedImplementation": "0x0ecbefc71524068cf18f9d4e50d787e134ee70b8"
}

请求参数

参数名 类型 是否必须 描述
chainShortName String 公链缩写符号
proxyContractAddress String 代理合约地址
expectedImplementation String 验证该代理合约转发调用的实现合约是否为该地址

返回结果

{
    "code": "0",
    "msg": "",
    "data": [
        "4f2e75682f75410f958c0a3bbf754358"
    ]
}

返回参数

参数名 类型 描述
guid String 若提交成功会返回GUID,可根据该GUID查询验证结果

查询代理合约验证结果

您可以在提交代理合约验证之后,根据返回的 GUID 查询验证结果。

每次调用消耗 0 点

HTTP请求

POST /api/v5/explorer/contract/check-proxy-verify-result

请求示例

POST /api/v5/explorer/contract/check-proxy-verify-result
body
{
    "chainShortName":"ETH",
    "guid":"4f2e75682f75410f958c0a3bbf754358"
}

请求参数

参数名 类型 是否必须 描述
chainShortName String 公链缩写符号
guid String 根据GUID查询代理合约验证结果

返回结果

{
    "code": "0",
    "msg": "The proxy's (0x826427966fb2e7edee940c5d99b7d66062faef2e) implementation contract is found at 0xd4a2dca4e03713d5bf7d2173237058466a9c1be4 and is successfully updated.",
    "data": []
}

返回参数

参数名 类型 描述
result String 代理合约源代码验证结果
若验证通过,则返回实现合约的合约地址;若验证失败,则返回“未检测到该代理合约的实现合约”

查询已验证合约的合约 ABI 和源代码

查询已验证合约的合约 ABI、源代码等基本信息,或查询已验证代理合约的实现合约地址信息。

每次调用消耗 0 点

HTTP请求

GET /api/v5/explorer/contract/verify-contract-info

请求示例

/api/v5/explorer/contract/verify-contract-info?chainShortName=ETH&contractAddress=0xcF80631b469A54dcba8c8ee1aF84505f496ed248

请求参数

参数名 类型 是否必须 描述
chainShortName String 公链缩写符号
contractAddress String 合约地址

返回结果

{
    "code": "0",
    "msg": "",
    "data": [
        {
            "sourceCode": "// proxy.sol - execute actions atomically through the proxy's identity\r\n\r\n// Copyright (C) 2017  DappHub, LLC\r\n\r\n// This program is free software: you can redistribute it and/or modify\r\n// it under the terms of the GNU General Public License as published by\r\n// the Free Software Foundation, either version 3 of the License, or\r\n// (at your option) any later version.\r\n\r\n// This program is distributed in the hope that it will be useful,\r\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n// GNU General Public License for more details.\r\n\r\n// You should have received a copy of the GNU General Public License\r\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\r\n\r\npragma solidity ^0.4.23;\r\n\r\ncontract DSAuthority {\r\n    function canCall(\r\n        address src, address dst, bytes4 sig\r\n    ) public view returns (bool);\r\n}\r\n\r\ncontract DSAuthEvents {\r\n    event LogSetAuthority (address indexed authority);\r\n    event LogSetOwner     (address indexed owner);\r\n}\r\n\r\ncontract DSAuth is DSAuthEvents {\r\n    DSAuthority  public  authority;\r\n    address      public  owner;\r\n\r\n    constructor() public {\r\n        owner = msg.sender;\r\n        emit LogSetOwner(msg.sender);\r\n    }\r\n\r\n    function setOwner(address owner_)\r\n        public\r\n        auth\r\n    {\r\n        owner = owner_;\r\n        emit LogSetOwner(owner);\r\n    }\r\n\r\n    function setAuthority(DSAuthority authority_)\r\n        public\r\n        auth\r\n    {\r\n        authority = authority_;\r\n        emit LogSetAuthority(authority);\r\n    }\r\n\r\n    modifier auth {\r\n        require(isAuthorized(msg.sender, msg.sig));\r\n        _;\r\n    }\r\n\r\n    function isAuthorized(address src, bytes4 sig) internal view returns (bool) {\r\n        if (src == address(this)) {\r\n            return true;\r\n        } else if (src == owner) {\r\n            return true;\r\n        } else if (authority == DSAuthority(0)) {\r\n            return false;\r\n        } else {\r\n            return authority.canCall(src, this, sig);\r\n        }\r\n    }\r\n}\r\n\r\ncontract DSNote {\r\n    event LogNote(\r\n        bytes4   indexed  sig,\r\n        address  indexed  guy,\r\n        bytes32  indexed  foo,\r\n        bytes32  indexed  bar,\r\n        uint              wad,\r\n        bytes             fax\r\n    ) anonymous;\r\n\r\n    modifier note {\r\n        bytes32 foo;\r\n        bytes32 bar;\r\n\r\n        assembly {\r\n            foo := calldataload(4)\r\n            bar := calldataload(36)\r\n        }\r\n\r\n        emit LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);\r\n\r\n        _;\r\n    }\r\n}\r\n\r\n// DSProxy\r\n// Allows code execution using a persistant identity This can be very\r\n// useful to execute a sequence of atomic actions. Since the owner of\r\n// the proxy can be changed, this allows for dynamic ownership models\r\n// i.e. a multisig\r\ncontract DSProxy is DSAuth, DSNote {\r\n    DSProxyCache public cache;  // global cache for contracts\r\n\r\n    constructor(address _cacheAddr) public {\r\n        require(setCache(_cacheAddr));\r\n    }\r\n\r\n    function() public payable {\r\n    }\r\n\r\n    // use the proxy to execute calldata _data on contract _code\r\n    function execute(bytes _code, bytes _data)\r\n        public\r\n        payable\r\n        returns (address target, bytes32 response)\r\n    {\r\n        target = cache.read(_code);\r\n        if (target == 0x0) {\r\n            // deploy contract & store its address in cache\r\n            target = cache.write(_code);\r\n        }\r\n\r\n        response = execute(target, _data);\r\n    }\r\n\r\n    function execute(address _target, bytes _data)\r\n        public\r\n        auth\r\n        note\r\n        payable\r\n        returns (bytes32 response)\r\n    {\r\n        require(_target != 0x0);\r\n\r\n        // call contract in current context\r\n        assembly {\r\n            let succeeded := delegatecall(sub(gas, 5000), _target, add(_data, 0x20), mload(_data), 0, 32)\r\n            response := mload(0)      // load delegatecall output\r\n            switch iszero(succeeded)\r\n            case 1 {\r\n                // throw if delegatecall failed\r\n                revert(0, 0)\r\n            }\r\n        }\r\n    }\r\n\r\n    //set new cache\r\n    function setCache(address _cacheAddr)\r\n        public\r\n        auth\r\n        note\r\n        returns (bool)\r\n    {\r\n        require(_cacheAddr != 0x0);        // invalid cache address\r\n        cache = DSProxyCache(_cacheAddr);  // overwrite cache\r\n        return true;\r\n    }\r\n}\r\n\r\n// DSProxyFactory\r\n// This factory deploys new proxy instances through build()\r\n// Deployed proxy addresses are logged\r\ncontract DSProxyFactory {\r\n    event Created(address indexed sender, address indexed owner, address proxy, address cache);\r\n    mapping(address=>bool) public isProxy;\r\n    DSProxyCache public cache = new DSProxyCache();\r\n\r\n    // deploys a new proxy instance\r\n    // sets owner of proxy to caller\r\n    function build() public returns (DSProxy proxy) {\r\n        proxy = build(msg.sender);\r\n    }\r\n\r\n    // deploys a new proxy instance\r\n    // sets custom owner of proxy\r\n    function build(address owner) public returns (DSProxy proxy) {\r\n        proxy = new DSProxy(cache);\r\n        emit Created(msg.sender, owner, address(proxy), address(cache));\r\n        proxy.setOwner(owner);\r\n        isProxy[proxy] = true;\r\n    }\r\n}\r\n\r\n// DSProxyCache\r\n// This global cache stores addresses of contracts previously deployed\r\n// by a proxy. This saves gas from repeat deployment of the same\r\n// contracts and eliminates blockchain bloat.\r\n\r\n// By default, all proxies deployed from the same factory store\r\n// contracts in the same cache. The cache a proxy instance uses can be\r\n// changed.  The cache uses the sha3 hash of a contract's bytecode to\r\n// lookup the address\r\ncontract DSProxyCache {\r\n    mapping(bytes32 => address) cache;\r\n\r\n    function read(bytes _code) public view returns (address) {\r\n        bytes32 hash = keccak256(_code);\r\n        return cache[hash];\r\n    }\r\n\r\n    function write(bytes _code) public returns (address target) {\r\n        assembly {\r\n            target := create(0, add(_code, 0x20), mload(_code))\r\n            switch iszero(extcodesize(target))\r\n            case 1 {\r\n                // throw if contract failed to deploy\r\n                revert(0, 0)\r\n            }\r\n        }\r\n        bytes32 hash = keccak256(_code);\r\n        cache[hash] = target;\r\n    }\r\n}",
            "contractName": "DSProxy",
            "compilerVersion": "v0.4.23+commit.124ca40d",
            "optimization": "1",
            "optimizationRuns": "200",
            "contractAbi": "[{\"constant\":false,\"inputs\":[{\"indexed\":false,\"name\":\"owner_\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"indexed\":false,\"name\":\"_target\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"execute\",\"outputs\":[{\"name\":\"response\",\"type\":\"bytes32\"}],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"indexed\":false,\"name\":\"_code\",\"type\":\"bytes\"},{\"indexed\":false,\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"execute\",\"outputs\":[{\"name\":\"target\",\"type\":\"address\"},{\"name\":\"response\",\"type\":\"bytes32\"}],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"cache\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"indexed\":false,\"name\":\"authority_\",\"type\":\"address\"}],\"name\":\"setAuthority\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"indexed\":false,\"name\":\"_cacheAddr\",\"type\":\"address\"}],\"name\":\"setCache\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"authority\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"indexed\":false,\"name\":\"_cacheAddr\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"constant\":false,\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"constant\":false,\"inputs\":[{\"indexed\":true,\"name\":\"sig\",\"type\":\"bytes4\"},{\"indexed\":true,\"name\":\"guy\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"foo\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"bar\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"wad\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"fax\",\"type\":\"bytes\"}],\"name\":\"LogNote\",\"payable\":false,\"type\":\"event\"},{\"constant\":false,\"inputs\":[{\"indexed\":true,\"name\":\"authority\",\"type\":\"address\"}],\"name\":\"LogSetAuthority\",\"payable\":false,\"type\":\"event\"},{\"constant\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"LogSetOwner\",\"payable\":false,\"type\":\"event\"}]",
            "evmVersion": "Default",
            "licenseType": "No License (None)",
            "libraryInfo": "",
            "proxy": "0",
            "implementation": "",
            "swarmSource": "bzzr://e498874c9ba9e75028e0c84f1b1d83b2dad5de910c59b837b32e5a190794c5e1"
        }
    ]
}

返回参数

参数名 类型 描述
sourceCode String 合约源代码
contractName String 合约名称
compilerVersion String 使用的编译器版本
optimization String 编译合约时是否使用了优化,0无优化,1有优化
optimizationRuns String 执行优化运行代码的次数
contractAbi String 合约ABI
evmVersion String 编译合约的EVM版本
licenseType String 开源许可证类型
libraryInfo Array 合约中引用的库的信息
> libraryName String 库名称
> libraryAddress String 库地址,如0xCfE28868F6E0A24b7333D22D8943279e76aC2cdc
proxy String 是否为代理合约,0表示不是代理合约,1表示是代理合约
implementation String 代理合约的实现合约的地址
swarmSource String 合约源代码的Swarm的哈希值

合约验证插件

OKLink 支持使用第三方插件如 hardhat、truffle、foundry 进行合约验证,大幅提升您的合约验证效率

支持公链:ETH, XLAYER, XLAYER_TESTNET, BSC, POLYGON, AVAXC, FTM, OP, ARBITRUM, LINEA, MANTA, CANTO, BASE, SCROLL, OPBNB, POLYGON_ZKEVM, SEPOLIA_TESTNET, GOERLI_TESTNET, AMOY_TESTNET, MUMBAI_TESTNET, POLYGON_ZKEVM_TESTNET

使用 Hardhat 进行合约验证

方法一(推荐):通过@okxweb3/hardhat-explorer-verify 插件验证

1、安装插件:使用以下命令在您的 Hardhat 项目中安装此插件
npm install @okxweb3/hardhat-explorer-verify

2、配置 Hardhat:在 Hardhat 配置文件(通常是 hardhat.config.jshardhat.config.ts)中,引入并配置插件。确保您的网络配置和 API 密钥正确。

示例如下:

import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import '@okxweb3/hardhat-explorer-verify';  // Import the plugin

const config: HardhatUserConfig = {
    solidity: "0.8.24",
    sourcify: {
        enabled: true,
    },
    networks: {
        xlayer: {
            url: "https://xlayerrpc.example.com",
            accounts: ["<Your Wallet Private Key>"],
        },
    },
    etherscan: {
        apiKey: '...'
    },
    okxweb3explorer: {
        apiKey: "<Your API Key>",
        customChains: [
            {
                network: "Fractal Bitcoin Mainnet",
                chainId: 70000061,
                urls: {
                    apiURL: "https://www.oklink.com/api/v5/explorer/contract/verify-source-code-plugin/FRACTAL",
                    browserURL: "https://www.oklink.com",
                }
             }
        ]
    }
};

export default config;

可根据openAPI中支持的链https://www.oklink.com/docs/zh/#quickstart-guide-list-of-supported-chains配置自定义链。
customChains: [{ network: "chainName", chainId: {chainId}, urls: { apiURL: "https://www.oklink.com/api/v5/explorer/contract/verify-source-code-plugin/{chainShortName}", browserURL: "https://www.oklink.com", }}]

3、验证合约:在部署合约后,使用Hardhat运行验证脚本。这通常涉及运行一个特定的 Hardhat 任务,该任务将自动抓取合约数据并发送到 OKLink 区块链浏览器进行验证。
命令示例:
npx hardhat okverify --network xlayer <Your Contract Address>

4、查看验证结果:验证成功后,您可以在 OKLink 区块链浏览器中查看验证状态和合约代码

5、验证代理合约
命令示例:
npx hardhat okverify --network xlayer --contract <Contract>:<Name> --proxy <address>

可以在 https://github.com/okx/hardhat-explorer-verify 中查看详细用法和说明




以 ETH 链为例,配置如下:

module.exports = {
    ...
    etherscan: {
        apiKey: {OKLink API key},
        customChains: [
            {
                network: "eth",
                chainId: 1,
                urls: {
                    apiURL: "https://www.oklink.com/api/v5/explorer/contract/verify-source-code-plugin/eth",
                    browserURL: "https://www.oklink.com",
                }
            }
        ]
    }
};

方法二:通过对 hardhat.config.js 文件做如下修改进行验证:

使用 Foundry 进行合约验证

如果使用 foundry 插件 进行合约验证,需要在配置文件中里的 forge verify-contract 模块增加以下内容:

验证一个合约必须提供以下参数

填写APIkey代码示例:

forge verify-contract <the_contract_address>
       src/MyToken.sol:MyToken
       --verifier oklink
       --verifier-url https://www.oklink.com/api/v5/explorer/contract/verify-source-code-plugin/eth
       --api-key oklinkApiKey

检查合约验证的结果

检查合约验证结果代码示例:

forge verify-check --chain 11155111 --verifier oklink --verifier-url https://www.oklink.com/api/explorer/v1/contract/verify/async/api/ethsepolia --api-key <your_OKLink_api_key>  <GUID>

使用 Truffle 进行合约验证

以 ETH 链为例,配置如下:

plugins: ['truffle-plugin-verify']
oklinkVerify: {
      provider: () => new HDWalletProvider(privateKey, infuraUrl),     // Localhost (default: none)
      gas: gasAmount,
      network_id: 1,
      verify: {
        apiUrl: 'https://www.oklink.com/api/v5/explorer/contract/verify-source-code-plugin/eth@truffle',
        apiKey: '{OKLink API key}',
        explorerUrl: 'https://www.oklink.com/',
      }
    },

如果使用 truffle 插件 进行合约验证,需要在配置文件中里的 module.exports 模块增加以下内容: