Skip to content

API-based Contract Verification

API Verification Banner

Introduction

Verifying smart contracts greatly improves their transparency and security. Smart contracts deployed on Moonbeam networks can be verified through API-based tools, including Moonscan API and Sourcify.

This page will outline the steps for using these API-based tools for verifying smart contracts, or retrieving verification status and metadata of smart contracts on Moonbeam networks.

Using Moonscan API

Moonscan is an official fork of Etherscan that can be used to view and search on-chain data, and comes with a suite of developer tools and analytics to interact with data on Moonbeam networks.

Moonscan exposes a set of REST API endpoints similar to the Etherscan API endpoints that can be used to verify smart contracts, retrieve verified contract ABI and source code, or otherwise interact with verified contracts on Moonbeam networks.

Generating a Moonscan API Key

Before using the Moonscan API, you need to generate a Moonscan API key. Please follow the instructions in the key generation section of the Etherscan plug-in verification page, as the API keys generated are used for both.

Moonscan Public API URL

The Moonscan API URL for Moonbeam networks is as follows:

https://api-moonbeam.moonscan.io/api
https://api-moonriver.moonscan.io/api
https://api-moonbase.moonscan.io/api

Verify Source Code

To verify a deployed contract's source code using the Moonscan API, you must form a POST request containing all the relevant contract creation information, and send the request to Moonscan's REST API. The following is sample code using JavaScript and Axios, an HTTP client:

// Submit Source Code for Verification
const response = await axios.post("https://api-moonbeam.moonscan.io/api", {
    apikey: "INSERT-API-KEY-HERE",
    module: "contract",
    action: "verifysourcecode",
    contractAddress: "INSERT-CONTRACT-ADDRESS-HERE",
    sourceCode: "INSERT-SOURCE-CODE-HERE", // flattened if necessary
    codeformat: "solidity-single-file" // or you can use "solidity-standard-json-input"
    contractname: "INSERT-CONTRACT-NAME-HERE", // if codeformat = solidity-standard-json-input, then enter contractname as ex: erc20.sol:erc20
    compilerversion: "INSERT-COMPILER-VERSION-HERE" // see https://etherscan.io/solcversions for list of support versions
    optimizationUsed: 0 // 0 = no optimization, 1 = optimization was used (applicable when codeformat=solidity-single-file)
    runs: 200 // set to 200 as default unless otherwise (applicable when codeformat=solidity-single-file) 
    constructorArguements: "INSERT-CONSTRUCTOR-ARGUMENTS-HERE" // if applicable
    evmversion: "INSERT-EVM-VERSION-HERE", // options: homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg, istanbul (applicable when codeformat=solidity-single-file)
    licenseType: 1, // valid codes 1-14 where 1=No License ... 14=Business Source License 1.1, see https://etherscan.io/contract-license-types
    libraryname1: "INSERT-LIBRARY-NAME-HERE" // if applicable, enter the name of the first library used, i.e. SafeMath (up to 10 libraries can be used)
    libraryaddress1: "INSERT-LIBRARY-ADDRESS-HERE" // if applicable, enter the address of the first library used
    libraryname2: "INSERT-LIBRARY-NAME-HERE", // if applicable, enter the name of the second library used
    libraryaddress2: "INSERT-LIBRARY-ADDRESS-HERE", // if applicable, enter the address of the second library used
    // ...
}, headers: { "Content-Type": "application/x-www-form-urlencoded" })

if (response.data.status == "1") {
    // 1 = submission success, use the guid returned (response.data.response.data) to check the status of your submission
    // average time of processing is 30-60 seconds
    console.log(response.data.status + "; " + response.data.message + "; " + response.data.result)
    // response.data.response.data is the GUID receipt for the submission, you can use this guid for checking the verification status
} else {
    // 0 = error
    console.log(response.data.status + "; " + response.data.message + "; " + response.data.result)
}
// Submit Source Code for Verification
const response = await axios.post("https://api-moonriver.moonscan.io/api", {
    apikey: "INSERT-API-KEY-HERE",
    module: "contract",
    action: "verifysourcecode",
    contractAddress: "INSERT-CONTRACT-ADDRESS-HERE",
    sourceCode: "INSERT-SOURCE-CODE-HERE", // flattened if necessary
    codeformat: "solidity-single-file" // or you can use "solidity-standard-json-input"
    contractname: "INSERT-CONTRACT-NAME-HERE", // if codeformat = solidity-standard-json-input, then enter contractname as ex: erc20.sol:erc20
    compilerversion: "INSERT-COMPILER-VERSION-HERE" // see https://etherscan.io/solcversions for list of support versions
    optimizationUsed: 0 // 0 = no optimization, 1 = optimization was used (applicable when codeformat=solidity-single-file)
    runs: 200 // set to 200 as default unless otherwise (applicable when codeformat=solidity-single-file) 
    constructorArguements: "INSERT-CONSTRUCTOR-ARGUMENTS-HERE" // if applicable
    evmversion: "INSERT-EVM-VERSION-HERE", // options: homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg, istanbul (applicable when codeformat=solidity-single-file)
    licenseType: 1, // valid codes 1-14 where 1=No License ... 14=Business Source License 1.1, see https://etherscan.io/contract-license-types
    libraryname1: "INSERT-LIBRARY-NAME-HERE" // if applicable, enter the name of the first library used, i.e. SafeMath (up to 10 libraries can be used)
    libraryaddress1: "INSERT-LIBRARY-ADDRESS-HERE" // if applicable, enter the address of the first library used
    libraryname2: "INSERT-LIBRARY-NAME-HERE", // if applicable, enter the name of the second library used
    libraryaddress2: "INSERT-LIBRARY-ADDRESS-HERE", // if applicable, enter the address of the second library used
    // ...
}, headers: { "Content-Type": "application/x-www-form-urlencoded" })

if (response.data.status == "1") {
    // 1 = submission success, use the guid returned (response.data.response.data) to check the status of your submission
    // average time of processing is 30-60 seconds
    console.log(response.data.status + "; " + response.data.message + "; " + response.data.result)
    // response.data.response.data is the GUID receipt for the submission, you can use this guid for checking the verification status
} else {
    // 0 = error
    console.log(response.data.status + "; " + response.data.message + "; " + response.data.result)
}
// Submit Source Code for Verification
const response = await axios.post("https://api-moonbase.moonscan.io/api", {
    apikey: "INSERT-API-KEY-HERE",
    module: "contract",
    action: "verifysourcecode",
    contractAddress: "INSERT-CONTRACT-ADDRESS-HERE",
    sourceCode: "INSERT-SOURCE-CODE-HERE", // flattened if necessary
    codeformat: "solidity-single-file" // or you can use "solidity-standard-json-input"
    contractname: "INSERT-CONTRACT-NAME-HERE", // if codeformat = solidity-standard-json-input, then enter contractname as ex: erc20.sol:erc20
    compilerversion: "INSERT-COMPILER-VERSION-HERE" // see https://etherscan.io/solcversions for list of support versions
    optimizationUsed: 0 // 0 = no optimization, 1 = optimization was used (applicable when codeformat=solidity-single-file)
    runs: 200 // set to 200 as default unless otherwise (applicable when codeformat=solidity-single-file) 
    constructorArguements: "INSERT-CONSTRUCTOR-ARGUMENTS-HERE" // if applicable
    evmversion: "INSERT-EVM-VERSION-HERE", // options: homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg, istanbul (applicable when codeformat=solidity-single-file)
    licenseType: 1, // valid codes 1-14 where 1=No License ... 14=Business Source License 1.1, see https://etherscan.io/contract-license-types
    libraryname1: "INSERT-LIBRARY-NAME-HERE" // if applicable, enter the name of the first library used, i.e. SafeMath (up to 10 libraries can be used)
    libraryaddress1: "INSERT-LIBRARY-ADDRESS-HERE" // if applicable, enter the address of the first library used
    libraryname2: "INSERT-LIBRARY-NAME-HERE", // if applicable, enter the name of the second library used
    libraryaddress2: "INSERT-LIBRARY-ADDRESS-HERE", // if applicable, enter the address of the second library used
    // ...
}, headers: { "Content-Type": "application/x-www-form-urlencoded" })

if (response.data.status == "1") {
    // 1 = submission success, use the guid returned (response.data.response.data) to check the status of your submission
    // average time of processing is 30-60 seconds
    console.log(response.data.status + "; " + response.data.message + "; " + response.data.result)
    // response.data.response.data is the GUID receipt for the submission, you can use this guid for checking the verification status
} else {
    // 0 = error
    console.log(response.data.status + "; " + response.data.message + "; " + response.data.result)
}

Upon successful submission, a GUID will be returned as a part of the result. This GUID can be used to check for the submission status.

curl https://api-moonbeam.moonscan.io/api
        ?module=contract
        &action=checkverifystatus
        &guid=INSERT-GUID-FROM-RESPONSE-HERE
        &apikey=INSERT-API-KEY-TOKEN-HERE
curl https://api-moonriver.moonscan.io/api
        ?module=contract
        &action=checkverifystatus
        &guid=INSERT-GUID-FROM-RESPONSE-HERE
        &apikey=INSERT-API-KEY-TOKEN-HERE
curl https://api-moonbase.moonscan.io/api
        ?module=contract
        &action=checkverifystatus
        &guid=INSERT-GUID-FROM-RESPONSE-HERE
        &apikey=INSERT-API-KEY-TOKEN-HERE

Retrieve Contract ABI for Verified Contracts

Once your contract is verified on Moonscan, you can use the following endpoint to retrieve the contract ABI:

curl https://api-moonbeam.moonscan.io/api
        ?module=contract
        &action=getabi
        &address=INSERT-CONTRACT-ADDRESS-HERE
        &apikey=INSERT-API-KEY-TOKEN-HERE
curl https://api-moonriver.moonscan.io/api
        ?module=contract
        &action=getabi
        &address=INSERT-CONTRACT-ADDRESS-HERE
        &apikey=INSERT-API-KEY-TOKEN-HERE
curl https://api-moonbase.moonscan.io/api
        ?module=contract
        &action=getabi
        &address=INSERT-CONTRACT-ADDRESS-HERE
        &apikey=INSERT-API-KEY-TOKEN-HERE

Retrieve Contract Source Code for Verified Contracts

Once your contract is verified on Moonscan, you can use the following endpoint to retrieve the contract source code:

curl https://api-moonbeam.moonscan.io/api
        ?module=contract
        &action=getsourcecode
        &address=INSERT-CONTRACT-ADDRESS-HERE
        &apikey=INSERT-API-KEY-TOKEN-HERE
curl https://api-moonriver.moonscan.io/api
        ?module=contract
        &action=getsourcecode
        &address=INSERT-CONTRACT-ADDRESS-HERE
        &apikey=INSERT-API-KEY-TOKEN-HERE
curl https://api-moonbase.moonscan.io/api
        ?module=contract
        &action=getsourcecode
        &address=INSERT-CONTRACT-ADDRESS-HERE
        &apikey=INSERT-API-KEY-TOKEN-HERE

Using Sourcify API

Sourcify is a multi-chain decentralized automated contract verification service, and maintains a public repository of contract metadata. Sourcify also provides a public server API for verification, and checking if a contract is verified, and a repository API for retrieving metadata files.

Sourcify Public Server URL

Soucify API endpoints can be accessed through the following public servers:

https://sourcify.dev/server
https://staging.sourcify.dev/server

Moonbeam Network Chain ID's

Sourcify uses chain ID's to identify the target network(s) for the request. The chain ID's of Moonbeam networks are as follows:

1284
1285
1287

Perfect vs. Partial Match

Sourcify supports two types of verification match results.

Full matches (sometimes referred as perfect matches) refer to the cases when the bytecode of the deployed contract is byte-by-byte the same as compilation output of the given source code files under the compilation settings defined in the metadata file.

Partial matches refer to cases when the deployed bytecode of the onchain contract match the bytecode resulting from the recompilation with the metadata and the source files except the metadata hash. For partial matches, the deployed contract and the given source code and metadata are functionally the same, but there are differences in source code comments, variable names, or other metadata fields such as source paths.

Verify Contract

A POST request is used to verify a contract on Sourcify. The following is sample code using JavaScript:

// Submit Contract Source Code and Metadata for Verification
const response = await axios.post("https://sourcify.dev/server/verify", {
    "address": "INSERT-CONTRACT-ADDRESS-HERE"
    "chain": 1284, // chain ID of Moonbeam
    "files": {
        "metadata-1.json": "INSERT-JSON-FILE-HERE", // metadata file for contract file 1
        "metadata-2.json": "INSERT-JSON-FILE-HERE", // metadata file for contract file 2
        "file1-name.sol": "INSERT-SOL-FILE-HERE", // contract source file 1
        "file2-name.sol": "INSERT-SOL-FILE-HERE" // contract source file 2
        //...
    },
    "chosenContract": 1 // (optional) index of the contract, if the provided files contain multiple metadata files          
})

if (result.status == "perfect") {
    // perfect match
    console.log(result.status + ";" + result.address);
} elseif (result.status == "partial") {
    // partial match
    console.log(result.status + ";" + result.address);
} else {
    // non-matching
    console.log(result.status + ";" + result.address);
}
// Submit Contract Source Code and Metadata for Verification
const response = await axios.post("https://sourcify.dev/server/verify", {
    "address": "INSERT-CONTRACT-ADDRESS-HERE"
    "chain": 1285, // chain ID of Moonriver
    "files": {
        "metadata-1.json": "INSERT-JSON-FILE-HERE", // metadata file for contract file 1
        "metadata-2.json": "INSERT-JSON-FILE-HERE", // metadata file for contract file 2
        "file1-name.sol": "INSERT-SOL-FILE-HERE", // contract source file 1
        "file2-name.sol": "INSERT-SOL-FILE-HERE" // contract source file 2
        //...
    },
    "chosenContract": 1 // (optional) index of the contract, if the provided files contain multiple metadata files          
})

if (result.status == "perfect") {
    // perfect match
    console.log(result.status + ";" + result.address);
} elseif (result.status == "partial") {
    // partial match
    console.log(result.status + ";" + result.address);
} else {
    // non-matching
    console.log(result.status + ";" + result.address);
}
// Submit Contract Source Code and Metadata for Verification
const response = await axios.post("https://sourcify.dev/server/verify", {
    "address": "INSERT-CONTRACT-ADDRESS-HERE"
    "chain": 1287, // chain ID of Moonbase Alpha
    "files": {
        "metadata-1.json": "INSERT-JSON-FILE-HERE", // metadata file for contract file 1
        "metadata-2.json": "INSERT-JSON-FILE-HERE", // metadata file for contract file 2
        "file1-name.sol": "INSERT-SOL-FILE-HERE", // contract source file 1
        "file2-name.sol": "INSERT-SOL-FILE-HERE" // contract source file 2
        //...
    },
    "chosenContract": 1 // (optional) index of the contract, if the provided files contain multiple metadata files          
})

if (result.status == "perfect") {
    // perfect match
    console.log(result.status + ";" + result.address);
} elseif (result.status == "partial") {
    // partial match
    console.log(result.status + ";" + result.address);
} else {
    // non-matching
    console.log(result.status + ";" + result.address);
}

Alternatively, you can also use the Sourcify hosted GUI to submit a contract for verification.

Check Verification Status by Address and Chain ID

Sourcify provides endpoints for checking the verification status of contracts on multiple EVM chains at once. This can be done through URL parameters, by specifying the contract addresses and the chain ID's of the networks.

There are two variations of this endpoint, one for perfect matching and one for partial matching:

curl https://sourcify.dev/server/check-by-addresses
        ?addresses={INSERT-ADDRESS-1-HERE, INSERT-ADDRESS-2-HERE, ...}
        &chainIds={INSERT-CHAIN-ID-1, INSERT-CHAIN-ID-2, ...}
curl https://sourcify.dev/server/check-all-by-addresses
        ?addresses={INSERT-ADDRESS-1-HERE, INSERT-ADDRESS-2-HERE, ...}
        &chainIds={INSERT-CHAIN-ID-1, INSERT-CHAIN-ID-2, ...}

An example response will be a JSON object of the following structure:

[
  {
    "address": "address1",
    "status": "perfect",
    "chainIds": [
      "chainId1",
      "chaindId2"
    ]
  },
  {
    "address": "address2",
    "status": "partial",
    "chainIds": [
      "chaindId2"
    ]
  }
]

Retrieve Contract Source Files for Verified Contracts

You can also retrieve the source files of verified contracts from the Sourcify repository.

There are two variations of this endpoint, one for the source files of perfect matches:

curl https://sourcify.dev/server/files/1284/INSERT-YOUR-CONTRACT-ADDRESS-HERE
curl https://sourcify.dev/server/files/1285/INSERT-YOUR-CONTRACT-ADDRESS-HERE
curl https://sourcify.dev/server/files/1287/INSERT-YOUR-CONTRACT-ADDRESS-HERE

And one for the source files of both perfect and partial matches:

curl https://sourcify.dev/server/files/any/1284/INSERT-YOUR-CONTRACT-ADDRESS-HERE
curl https://sourcify.dev/server/files/any/1285/INSERT-YOUR-CONTRACT-ADDRESS-HERE
curl https://sourcify.dev/server/files/any/1287/INSERT-YOUR-CONTRACT-ADDRESS-HERE