Verify a QRL 2.0 Smart Contract
Verification recompiles your source with the same pinned Hyperion compiler you used and compares the result byte for byte with the code running at the contract address. A match publishes the source and ABI on the contract page and unlocks the Read tab.
It proves the source is what runs on chain. It does not mean the contract was audited or is safe: what "Source verified" means.
1. Know your compiler
QRL 2.0 contracts are written in Hyperion and compiled with hypc. The verifier runs these two releases of @theqrl/hypc; pick the one you deployed with.
| compilerversion | VM | Networks | Addresses |
|---|---|---|---|
| v0.0.2+commit.3e18e55d | VM32 | QRL 2.0 testnet v2 (chain id 1337) | Q + 40 hex (20 bytes) |
| v0.2.0+commit.e36a0aea | VM64 | Testnet V3 and mainnet | Q + 128 hex (64 bytes) |
Hyperion 0.2.0 is the VM64 release (theQRL/web3.js #118). The compiler version is also written into the metadata at the end of every contract's bytecode, so the web form pre-selects it for you.
2. From the website
- Open your contract: search for its address, or find it under Smart Contracts.
- Choose Verify, or go straight to
https://quantascan.io/contract/<address>/verify. - Paste the source file, check the compiler and optimizer settings, and submit. The result appears on the page within seconds.
The form takes one source file. For contracts split over several files, use the API below with your standard JSON input.
3. From the command line
The explorer-compatible API at https://quantascan.io/api accepts the standard verification calls. You need a free API key. Submit with a form-encoded POST; the result is a guid:
# input.json = the standard JSON input you compiled with ("language": "Hyperion")
curl -s https://quantascan.io/api \
--data-urlencode "apikey=$QS_API_KEY" \
--data-urlencode "module=contract" \
--data-urlencode "action=verifysourcecode" \
--data-urlencode "contractaddress=$CONTRACT" \
--data-urlencode "codeformat=solidity-standard-json-input" \
--data-urlencode "contractname=Token.hy:Token" \
--data-urlencode "compilerversion=v0.0.2+commit.3e18e55d" \
--data-urlencode "constructorArguements=" \
--data-urlencode "sourceCode@input.json"{"status":"1","message":"OK","result":"19a3f0c2b7e0d4c8a91f2e3b4c5d6"}Then poll the guid every few seconds until it passes or fails:
curl -s "https://quantascan.io/api?module=contract&action=checkverifystatus&guid=$GUID&apikey=$QS_API_KEY"{"status":"0","message":"NOTOK","result":"Pending in queue"}
{"status":"1","message":"OK","result":"Pass - Verified"}
{"status":"0","message":"NOTOK","result":"Fail - Unable to verify. The compiled runtime bytecode does not match the code deployed at this address (…)"}Fields
- apikey
- Your free key (qs_…). Query string or form field.
- module / action
- contract / verifysourcecode. Query string or form field.
- contractaddress
- Q + 40 hex (VM32) or Q + 128 hex (VM64). 0x + the same hex is accepted.
- sourceCode
- The standard JSON input you compiled with (language "Hyperion"), or a single source file.
- codeformat
- solidity-standard-json-input (the input format name, also for Hyperion) or solidity-single-file.
- contractname
- path:ContractName as it appears in sources, e.g. Token.hy:Token.
- compilerversion
- v0.0.2+commit.3e18e55d or v0.2.0+commit.e36a0aea (the bare version also works).
- constructorArguements
- Optional ABI-encoded constructor arguments as hex. The corrected spelling constructorArguments is accepted too.
- licenseType
- Optional; numeric code or SPDX id. Defaults to the SPDX header of the file.
- optimizationUsed / runs
- Single-file format only (in standard JSON the optimizer comes from settings).
Answers to expect
Pending in queue→ keep polling.Pass - Verified→ done; this covers full and partial matches (the contract page shows which).Fail - Unable to verify. …→ the reason follows; a bytecode mismatch almost always means a different compiler version, optimizer setting or source.Contract source code already verifiedon submit → a full match is already on record. A partial match can be resubmitted to upgrade it.Unable to locate ContractCode at …→ there is no contract at that address on this network.- Invalid input is answered with HTTP 400 and a message that names the problem, for example a Solidity input or a setting the verifier cannot reproduce.
4. From a deploy script
Verifying straight after deployment is the easiest way to get it right: you still have the exact input you compiled. This Node script compiles with @theqrl/hypc, keeps the input, and verifies it.
// npm i @theqrl/hypc@0.0.2 (0.2.0 for VM64 networks)
import fs from "node:fs";
import hypc from "@theqrl/hypc";
// 1. Compile exactly as you deploy. Keep this input: it is what you verify.
const input = {
language: "Hyperion",
sources: { "Token.hy": { content: fs.readFileSync("Token.hy", "utf8") } },
settings: {
optimizer: { enabled: true, runs: 200 },
outputSelection: { "*": { "*": ["*"] } },
},
};
const out = JSON.parse(hypc.compile(JSON.stringify(input)));
const artifact = out.contracts["Token.hy"].Token;
const vm = artifact.qrvm ?? artifact.zvm; // 0.2.0 names it qrvm, 0.0.2 zvm
// … deploy vm.bytecode.object with @theqrl/web3, note the contract address …
// 2. Verify.
const API = "https://quantascan.io/api";
const key = process.env.QS_API_KEY;
const submit = await fetch(API, {
method: "POST",
body: new URLSearchParams({
apikey: key,
module: "contract",
action: "verifysourcecode",
contractaddress: process.env.CONTRACT,
codeformat: "solidity-standard-json-input",
contractname: "Token.hy:Token",
compilerversion: "v" + hypc.version(), // e.g. v0.0.2+commit.3e18e55d.Emscripten.clang
constructorArguements: "", // ABI-encoded hex, no 0x needed
sourceCode: JSON.stringify(input),
}),
}).then((r) => r.json());
if (submit.status !== "1") throw new Error(submit.result);
// 3. Poll until it passes or fails.
for (;;) {
await new Promise((r) => setTimeout(r, 3000));
const q = new URLSearchParams({
apikey: key, module: "contract", action: "checkverifystatus", guid: submit.result,
});
const { result } = await fetch(`${API}?${q}`).then((r) => r.json());
if (result !== "Pending in queue") { console.log(result); break; }
}Deployment itself is left out; the QRL contract example shows how to deploy with @theqrl/web3.
5. Hardhat and Foundry
The endpoint implements the same verification protocol as the verify plugins of Hardhat (@nomicfoundation/hardhat-verify) and Foundry (forge verify-contract): a form-encoded verifysourcecode submit, a guid, and checkverifystatus polling.
Untested, and not usable today. Both tools compile with the Solidity compiler and talk to nodes with eth_* methods; QRL 2.0 uses Hyperion and the qrl_* namespace, so neither can build or deploy a QRL 2.0 contract yet. The QRL team has said a Hardhat-like tool is being developed. The snippets below show where the Quantascan endpoint goes once a Hyperion-aware toolchain exists.
// hardhat.config.ts — hardhat-verify 3.x (reference only, see the note above)
export default {
chainDescriptors: {
1337: {
name: "QRL 2.0 Testnet",
blockExplorers: {
etherscan: {
name: "Quantascan",
url: "https://quantascan.io",
apiUrl: "https://quantascan.io/api",
},
},
},
},
verify: { etherscan: { apiKey: process.env.QS_API_KEY } },
};# reference only, see the note above
forge verify-contract \
--verifier etherscan \
--verifier-url https://quantascan.io/api \
--etherscan-api-key "$QS_API_KEY" \
--chain 1337 \
--compiler-version v0.0.2+commit.3e18e55d \
"$CONTRACT" Token.hy:Token6. Show it in your README
Once verified, link to your contract with a live badge. It shows the current verification state (full, partial or unverified) and updates within an hour. Add ?style=for-the-badge for the larger style.
[](https://quantascan.io/contract/<address>)<a href="https://quantascan.io/contract/<address>">
<img alt="Source verified on Quantascan"
src="https://quantascan.io/api/v1/qrl2/contracts/<address>/badge?style=flat">
</a>FAQ about contract verification
Common questions about this page
- Which compiler version do I pick?
- The one you deployed with. hypc 0.0.2 (v0.0.2+commit.3e18e55d) compiles for VM32, the QRL 2.0 testnet v2 with 20-byte addresses (Q + 40 hex). hypc 0.2.0 (v0.2.0+commit.e36a0aea) compiles for VM64, the 64-byte-address networks (Q + 128 hex) of Testnet V3 and mainnet. A VM32 compiler can never reproduce a VM64 contract, so the API refuses that combination up front.
- What is the difference between a full and a partial match?
- Full: every byte of the recompiled runtime code equals the code on chain. Partial: the code is identical but the metadata hash at the end differs, usually because a file name or comment changed. Both publish the source; the contract page shows which one you got. See what 'Source verified' means.
- Why is my verification failing with a bytecode mismatch?
- The usual causes are a different compiler version, a different optimizer setting (enabled and runs must match the deployment exactly), a different contract name, or source that changed after deployment. Verification compares the code that runs at the address, so constructor arguments do not decide the outcome.
- Which standard JSON settings are supported?
- The optimizer (enabled, runs) and the default metadata. Settings that only choose outputs, such as outputSelection, are ignored. Settings that change the emitted code (viaIR, optimizer details, custom metadata hashing, remappings, linked libraries) are refused with a message that says so, because the verifier could never reproduce that bytecode. evmVersion is not a Hyperion setting: hypc itself rejects it.
- Can I verify with Hardhat or Foundry?
- Not yet in practice: both compile with the Solidity compiler and talk to nodes with eth_* JSON-RPC methods, while QRL 2.0 uses Hyperion and the qrl_* namespace. The QRL team has said a Hardhat-like tool is being developed. The Quantascan endpoint already implements the verification protocol those plugins use, so a Hyperion-aware toolchain can point its verifier at https://quantascan.io/api.
- How many verifications can I submit?
- Five submissions per minute per client, shared with the web form, and three failed attempts on the same contract within ten minutes pause new attempts for that contract. Each API call also counts against your key's daily quota.