UNPKG

@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
"use strict"; 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); });