Skip to content

API-based Contract Verification

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',
    module: 'contract',
    action: 'verifysourcecode',
    contractAddress: 'INSERT_CONTRACT_ADDRESS',
    sourceCode: 'INSERT_SOURCE_CODE', // flattened if necessary
    codeformat: 'solidity-single-file', // or you can use "solidity-standard-json-input"
    contractname: 'INSERT_CONTRACT_NAME', // if codeformat = solidity-standard-json-input, then enter contractname as ex: erc20.sol:erc20
    compilerversion: 'INSERT_COMPILER_VERSION', // 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', // if applicable
    evmversion: 'INSERT_EVM_VERSION', // 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', // if applicable, enter the name of the first library used, i.e. SafeMath (up to 10 libraries can be used)
    libraryaddress1: 'INSERT_LIBRARY_ADDRESS', // if applicable, enter the address of the first library used
    libraryname2: 'INSERT_LIBRARY_NAME', // if applicable, enter the name of the second library used
    libraryaddress2: 'INSERT_LIBRARY_ADDRESS', // 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',
    module: 'contract',
    action: 'verifysourcecode',
    contractAddress: 'INSERT_CONTRACT_ADDRESS',
    sourceCode: 'INSERT_SOURCE_CODE', // flattened if necessary
    codeformat: 'solidity-single-file', // or you can use "solidity-standard-json-input"
    contractname: 'INSERT_CONTRACT_NAME', // if codeformat = solidity-standard-json-input, then enter contractname as ex: erc20.sol:erc20
    compilerversion: 'INSERT_COMPILER_VERSION', // 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', // if applicable
    evmversion: 'INSERT_EVM_VERSION', // 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', // if applicable, enter the name of the first library used, i.e. SafeMath (up to 10 libraries can be used)
    libraryaddress1: 'INSERT_LIBRARY_ADDRESS', // if applicable, enter the address of the first library used
    libraryname2: 'INSERT_LIBRARY_NAME', // if applicable, enter the name of the second library used
    libraryaddress2: 'INSERT_LIBRARY_ADDRESS', // 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',
    module: 'contract',
    action: 'verifysourcecode',
    contractAddress: 'INSERT_CONTRACT_ADDRESS',
    sourceCode: 'INSERT_SOURCE_CODE', // flattened if necessary
    codeformat: 'solidity-single-file', // or you can use "solidity-standard-json-input"
    contractname: 'INSERT_CONTRACT_NAME', // if codeformat = solidity-standard-json-input, then enter contractname as ex: erc20.sol:erc20
    compilerversion: 'INSERT_COMPILER_VERSION', // 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', // if applicable
    evmversion: 'INSERT_EVM_VERSION', // 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', // if applicable, enter the name of the first library used, i.e. SafeMath (up to 10 libraries can be used)
    libraryaddress1: 'INSERT_LIBRARY_ADDRESS', // if applicable, enter the address of the first library used
    libraryname2: 'INSERT_LIBRARY_NAME', // if applicable, enter the name of the second library used
    libraryaddress2: 'INSERT_LIBRARY_ADDRESS', // 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
  &apikey=INSERT_API_KEY
curl https://api-moonriver.moonscan.io/api
  ?module=contract
  &action=checkverifystatus
  &guid=INSERT_GUID_FROM_RESPONSE
  &apikey=INSERT_API_KEY
curl https://api-moonbase.moonscan.io/api
  ?module=contract
  &action=checkverifystatus
  &guid=INSERT_GUID_FROM_RESPONSE
  &apikey=INSERT_API_KEY

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
  &apikey=INSERT_API_KEY
curl https://api-moonriver.moonscan.io/api
  ?module=contract
  &action=getabi
  &address=INSERT_CONTRACT_ADDRESS
  &apikey=INSERT_API_KEY
curl https://api-moonbase.moonscan.io/api
  ?module=contract
  &action=getabi
  &address=INSERT_CONTRACT_ADDRESS
  &apikey=INSERT_API_KEY

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
  &apikey=INSERT_API_KEY
curl https://api-moonriver.moonscan.io/api
  ?module=contract
  &action=getsourcecode
  &address=INSERT_CONTRACT_ADDRESS
  &apikey=INSERT_API_KEY
curl https://api-moonbase.moonscan.io/api
  ?module=contract
  &action=getsourcecode
  &address=INSERT_CONTRACT_ADDRESS
  &apikey=INSERT_API_KEY

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',
  chain: 1284, // chain ID of Moonbeam
  files: {
    'metadata-1.json': 'INSERT_JSON_FILE', // metadata file for contract file 1
    'metadata-2.json': 'INSERT_JSON_FILE', // metadata file for contract file 2
    'file1-name.sol': 'INSERT_SOL_FILE', // contract source file 1
    'file2-name.sol': 'INSERT_SOL_FILE', // 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);
} else if (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',
  chain: 1285, // chain ID of Moonriver
  files: {
    'metadata-1.json': 'INSERT_JSON_FILE', // metadata file for contract file 1
    'metadata-2.json': 'INSERT_JSON_FILE', // metadata file for contract file 2
    'file1-name.sol': 'INSERT_SOL_FILE', // contract source file 1
    'file2-name.sol': 'INSERT_SOL_FILE', // 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);
} else if (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',
  chain: 1287, // chain ID of Moonbase Alpha
  files: {
    'metadata-1.json': 'INSERT_JSON_FILE', // metadata file for contract file 1
    'metadata-2.json': 'INSERT_JSON_FILE', // metadata file for contract file 2
    'file1-name.sol': 'INSERT_SOL_FILE', // contract source file 1
    'file2-name.sol': 'INSERT_SOL_FILE', // 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);
} else if (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, INSERT_ADDRESS_2, ...}
  &chainIds={INSERT_CHAIN_ID_1, INSERT_CHAIN_ID_2, ...}
curl https://sourcify.dev/server/check-all-by-addresses
  ?addresses={INSERT_ADDRESS_1, INSERT_ADDRESS_2, ...}
  &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_CONTRACT_ADDRESS
curl https://sourcify.dev/server/files/1285/INSERT_CONTRACT_ADDRESS
curl https://sourcify.dev/server/files/1287/INSERT_CONTRACT_ADDRESS

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

curl https://sourcify.dev/server/files/any/1284/INSERT_CONTRACT_ADDRESS
curl https://sourcify.dev/server/files/any/1285/INSERT_CONTRACT_ADDRESS
curl https://sourcify.dev/server/files/any/1287/INSERT_CONTRACT_ADDRESS

Using Sourcify with Foundry

Foundry's Forge tool has built-in support for Sourcify verification similar to how it has built-in support for Etherscan. The example in this section of the guide will use the MyToken.sol contract that was created in the Using Foundry to Deploy to Moonbeam guide.

A Foundry project that uses Sourcify must have their compiler emit metadata files. This can be configured in the foundry.toml file:

[profile.default]
# Input your custom or default config options here
extra_output_files = ["metadata"]

If you have already deployed the example contract, you can verify it with the verify-contract command. Before you can verify the contract, you will need to ABI-encode the constructor arguments. To do so for the example contract, you can run the following command:

cast abi-encode "constructor(uint256)" 100

The result should be 0x0000000000000000000000000000000000000000000000000000000000000064. You can then verify the contract using the following command:

forge verify-contract --chain-id 1284 \
--constructor-args 0x0000000000000000000000000000000000000000000000000000000000000064 \
--verifier sourcify INSERT_CONTRACT_ADDRESS src/MyToken.sol:MyToken 
forge verify-contract --chain-id 1285 \
--constructor-args 0x0000000000000000000000000000000000000000000000000000000000000064 \
--verifier sourcify INSERT_CONTRACT_ADDRESS src/MyToken.sol:MyToken 
forge verify-contract --chain-id 1287 \
--constructor-args 0x0000000000000000000000000000000000000000000000000000000000000064 \
--verifier sourcify INSERT_CONTRACT_ADDRESS src/MyToken.sol:MyToken 

Foundry Verify

If you wanted to deploy the example contract and verify at the same time, then you would use the following command:

forge create --rpc-url INSERT_RPC_API_ENDPOINT \
--constructor-args 100 \
--verify --verifier sourcify \
--private-key INSERT_YOUR_PRIVATE_KEY \
src/MyToken.sol:MyToken  
forge create --rpc-url INSERT_RPC_API_ENDPOINT \
--constructor-args 100 \
--verify --verifier sourcify \
--private-key INSERT_YOUR_PRIVATE_KEY \
src/MyToken.sol:MyToken  
forge create --rpc-url https://rpc.api.moonbase.moonbeam.network \
--constructor-args 100 \
--verify --verifier sourcify \
--private-key INSERT_YOUR_PRIVATE_KEY \
src/MyToken.sol:MyToken

Foundry Contract Deploy and Verify

Last update: January 25, 2024
| Created: July 6, 2022