@vechain/vebetterdao-contracts
Version:
Open-source repository that houses the smart contracts powering the decentralized VeBetterDAO on the VeChain Thor blockchain.
336 lines (335 loc) ⢠15 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const axios_1 = __importDefault(require("axios"));
const glob_1 = require("glob");
const verify_utils_1 = require("./verify-utils");
// Network mappings
const NETWORKS = {
mainnet: { id: "100009", name: "VeChain Mainnet" },
testnet: { id: "100010", name: "VeChain Testnet" },
};
async function main() {
// Parse command line arguments
const args = process.argv.slice(2);
if (args.length < 2) {
console.error("Usage: ts-node verify-contract.ts <contract-address> <network> [contract-name] [--partial-match]");
console.error("Available networks: mainnet, testnet");
console.error("Example: ts-node verify-contract.ts 0x123... mainnet StargateNFT");
process.exit(1);
}
const contractAddress = args[0];
const network = args[1];
let contractName = null;
// Parse the remaining arguments
for (let i = 2; i < args.length; i++) {
if (!args[i].startsWith("--")) {
contractName = args[i];
}
}
// Validate network
if (!NETWORKS[network]) {
console.error(`Error: Network '${network}' not supported.`);
console.error("Available networks: mainnet, testnet");
process.exit(1);
}
// Discover available contracts
const availableContracts = discoverContracts();
if (availableContracts.length === 0) {
console.error("No contracts found in the contracts directory.");
process.exit(1);
}
// If no contract name provided, show available contracts
if (!contractName) {
console.error("Please specify a contract name.");
console.error("Available contracts:");
availableContracts.forEach(contract => {
console.error(` - ${contract.name} (${contract.path})`);
});
process.exit(1);
}
// Find the specified contract
const targetContract = availableContracts.find(c => c.name === contractName);
if (!targetContract) {
console.error(`Error: Contract '${contractName}' not found.`);
console.error("Available contracts:");
availableContracts.forEach(contract => {
console.error(` - ${contract.name} (${contract.path})`);
});
process.exit(1);
}
const chainId = NETWORKS[network].id;
const networkName = NETWORKS[network].name;
console.log(`Verifying ${contractName} at ${contractAddress} on ${networkName} (chainId: ${chainId})...`);
console.log(`Contract path: ${targetContract.path}`);
// Get dynamic paths based on where script is running from
const { contractsDir, packageDir } = (0, verify_utils_1.getProjectPaths)();
// Paths for temporary files
const tempDir = path.join(packageDir, `temp-verify-${contractAddress}`);
const contractsBaseDir = contractsDir;
// Create temp directory if it doesn't exist
if (fs.existsSync(tempDir)) {
console.log("Cleaning up existing temp directory...");
fs.rmSync(tempDir, { recursive: true, force: true });
}
fs.mkdirSync(tempDir, { recursive: true });
// Extract metadata from compiled artifacts
console.log(`Extracting metadata for ${contractName}...`);
const metadata = (0, verify_utils_1.findContractMetadata)(targetContract.path, contractName);
if (!metadata) {
console.error(`Metadata for ${contractName} not found in any build-info file.`);
console.error("Make sure the contract has been compiled successfully.");
process.exit(1);
}
// Save metadata to temp directory
const metadataPath = path.join(tempDir, "metadata.json");
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
// Copy source files based on metadata
const copiedFiles = (0, verify_utils_1.copySourceFiles)(metadata, tempDir, contractsBaseDir);
if (copiedFiles.length === 0) {
console.error("No source files were copied. Cannot proceed with verification.");
process.exit(1);
}
console.log(`š Prepared ${copiedFiles.length} source files for verification`);
try {
// Check verification status
console.log("\nš Checking if contract is already verified...");
const checkResult = await checkVerificationStatusV2(chainId, contractAddress);
if (checkResult.isVerified && checkResult.data) {
const matchType = checkResult.data.match || "unknown";
console.log(`\nā
Contract is already verified on Sourcify! (Match type: ${matchType})`);
console.log(` Verified at: ${checkResult.data.verifiedAt || "unknown"}`);
console.log(` View contract: https://sourcify.dev/lookup/${chainId}/${contractAddress}`);
console.log(` API v2 lookup: https://sourcify.dev/server/v2/contract/${chainId}/${contractAddress}?fields=all`);
return;
}
// Proceed with verification
const verificationResult = await verifyContractV2(chainId, contractAddress, metadata, copiedFiles, tempDir, contractName);
if (verificationResult.success) {
console.log(`\nš Verification successful using Sourcify v2!`);
console.log(`š Contract: ${contractName}`);
console.log(`š View verified contract: https://repo.sourcify.dev/${chainId}/${contractAddress}`);
// Display verification result details if available
if (verificationResult.data) {
console.log("\nš Verification details:", JSON.stringify(verificationResult.data, null, 2));
}
}
}
catch (err) {
const error = err;
// Handle timeout errors
if (error.code === "ECONNABORTED" || error.message?.includes("timeout")) {
console.error("\nā° Request timed out!");
console.error("This can happen when:");
console.error("1. Sourcify servers are experiencing high load");
console.error("2. Network connectivity is slow");
console.error("3. The contract is complex and requires more processing time");
console.error("\nš Try running the command again in a few minutes.");
return;
}
// Handle network errors
if (error.code === "ENOTFOUND" || error.code === "ECONNREFUSED") {
console.error("\nNetwork connection error!");
console.error("Please check your internet connection and try again.");
console.error("If the problem persists, Sourcify servers might be temporarily unavailable.");
return;
}
if (error.response) {
console.error("Verification failed with status:", error.response.status);
console.error("Error message:", JSON.stringify(error.response.data, null, 2));
// Handle bytecode mismatch error
if (error.response.data &&
typeof error.response.data === "object" &&
error.response.data.error &&
(error.response.data.error.includes("bytecode length doesn't match") ||
error.response.data.error === "The recompiled bytecode length doesn't match the onchain bytecode length.")) {
console.log("\nBytecode mismatch detected!");
console.log("This can happen if:");
console.log("1. The contract was deployed with a different compiler version or settings");
console.log("2. The contract was deployed with constructor parameters");
console.log("3. The contract was deployed from a different source code than what you're trying to verify");
}
// Handle already verified
if (error.response.data &&
typeof error.response.data === "string" &&
error.response.data.includes("already verified")) {
console.log("\nThe contract seems to be already verified on Sourcify.");
console.log(`You can check it here: https://sourcify.dev/lookup/${chainId}/${contractAddress}`);
console.log(`API v2 lookup: https://sourcify.dev/server/v2/contract/${chainId}/${contractAddress}?fields=all`);
}
}
else {
console.error("Error:", error.message);
}
}
finally {
// Clean up the temp directory
console.log("Cleaning up temporary files...");
fs.rmSync(tempDir, { recursive: true, force: true });
}
}
/**
* Discovers all available contracts in the contracts directory
*/
function discoverContracts() {
const { contractsDir } = (0, verify_utils_1.getProjectPaths)();
const contracts = [];
// Find all .sol files recursively
const solidityFiles = glob_1.glob.sync("**/*.sol", {
cwd: contractsDir,
ignore: ["node_modules/**", "test/**", "mocks/**"],
});
for (const filePath of solidityFiles) {
const fullPath = path.join(contractsDir, filePath);
const fileName = path.basename(filePath, ".sol");
// Skip files that start with lowercase (likely libraries or interfaces)
if (fileName[0] === fileName[0].toLowerCase()) {
continue;
}
contracts.push({
name: fileName,
path: filePath,
fullPath: fullPath,
});
}
return contracts;
}
/**
* Checks verification status using v2 API
*/
async function checkVerificationStatusV2(chainId, contractAddress, timeout = 15000) {
const checkUrl = `https://sourcify.dev/server/v2/contract/${chainId}/${contractAddress}`;
// Request compilation info in addition to default fields
const params = new URLSearchParams({
fields: "compilation",
});
console.log(`š Checking verification status: ${checkUrl}?${params}`);
try {
const response = await axios_1.default.get(`${checkUrl}?${params}`, {
timeout,
headers: {
"User-Agent": "Contract-Verification-Script/2.0",
},
});
const data = response.data;
console.log("ā
Contract verification status retrieved");
console.log(` Match type: ${data.match || "unknown"}`);
console.log(` Creation match: ${data.creationMatch || "unknown"}`);
console.log(` Runtime match: ${data.runtimeMatch || "unknown"}`);
console.log(` Verified at: ${data.verifiedAt || "unknown"}`);
if (data.compilation) {
console.log(` Contract name: ${data.compilation.name || "unknown"}`);
console.log(` Compiler: ${data.compilation.compilerVersion || "unknown"}`);
}
return { isVerified: true, data };
}
catch (error) {
if (error.response?.status === 404) {
console.log("š Contract not verified yet");
return { isVerified: false };
}
throw error;
}
}
/**
* Submits verification job using v2 API
*/
async function submitVerificationJobV2(chainId, contractAddress, metadata, copiedFiles, tempDir, timeout = 60000) {
console.log("š¤ Submitting verification job to Sourcify v2...");
console.log(`š Sources: ${copiedFiles.length} files`);
const result = await (0, verify_utils_1.submitVerification)(chainId, contractAddress, metadata, copiedFiles, tempDir);
if (!result.success) {
if (result.error === "ALREADY_VERIFIED") {
throw new Error("ALREADY_VERIFIED");
}
throw new Error(result.error || "Verification submission failed");
}
if (!result.verificationId) {
throw new Error("No verification ID returned");
}
return {
jobId: result.verificationId,
status: "pending",
result: undefined,
};
}
/**
* Polls verification job status using v2 endpoint
*/
async function pollVerificationJob(verificationId, maxWaitTime = 150000, // 2.5 minutes
pollInterval = 3000) {
console.log(`š Polling verification job: ${verificationId}`);
const result = await (0, verify_utils_1.pollVerificationJob)(verificationId, maxWaitTime, pollInterval);
if (!result.success) {
throw new Error(result.error || "Verification job failed");
}
if (result.data) {
console.log("ā
Verification job completed successfully");
return result.data;
}
throw new Error("No contract data returned");
}
/**
* Verifies contract using v2 API
*/
async function verifyContractV2(chainId, contractAddress, metadata, copiedFiles, tempDir, contractName) {
console.log("š Starting contract verification with Sourcify v2...");
try {
// Submit verification job
const job = await submitVerificationJobV2(chainId, contractAddress, metadata, copiedFiles, tempDir);
if (job.jobId) {
// Job-based verification - poll for completion
const result = await pollVerificationJob(job.jobId);
return { success: true, data: result };
}
throw new Error("Invalid job response");
}
catch (error) {
if (error.message === "ALREADY_VERIFIED") {
console.log("ā
Contract is already verified on Sourcify!");
return { success: true, data: { alreadyVerified: true } };
}
throw error;
}
}
main().catch((err) => {
console.error("Error during verification:", err);
process.exit(1);
});