@mapprotocol/common-contracts 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ export interface VerifyOptions {
2
+ address: string;
3
+ contractName: string;
4
+ contractPath?: string;
5
+ constructorArgs?: any[];
6
+ constructorParams?: string;
7
+ compiler?: string;
8
+ optimizer?: boolean;
9
+ optimizerRuns?: number;
10
+ }
11
+ /**
12
+ * Unified contract verification — auto-routes to EVM (hardhat verify) or Tron (TronScan API)
13
+ */
14
+ export declare function verify(hre: any, opts: VerifyOptions): Promise<void>;
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.verify = verify;
4
+ /**
5
+ * Contract verification — supports Etherscan, Blockscout (Mapo), and TronScan.
6
+ * Auto-routes based on network name.
7
+ */
8
+ const tronHelper_1 = require("./tronHelper");
9
+ // TronScan API endpoints by chainId
10
+ const TRONSCAN_API = {
11
+ 728126428: "https://apilist.tronscan.org/api/solidity/contract/verify", // mainnet
12
+ 3448148188: "https://nile.tronscan.org/api/solidity/contract/verify", // nile testnet
13
+ };
14
+ /**
15
+ * Unified contract verification — auto-routes to EVM (hardhat verify) or Tron (TronScan API)
16
+ */
17
+ async function verify(hre, opts) {
18
+ const network = hre.network.name;
19
+ if ((0, tronHelper_1.isTronNetwork)(network)) {
20
+ await verifyTron(hre, opts);
21
+ }
22
+ else {
23
+ await verifyEvm(hre, opts);
24
+ }
25
+ }
26
+ // ============================================================
27
+ // EVM verification via hardhat-verify
28
+ // ============================================================
29
+ async function verifyEvm(hre, opts) {
30
+ const contractPath = opts.contractPath || `contracts/${opts.contractName}.sol:${opts.contractName}`;
31
+ console.log(`verifying ${opts.contractName} at ${opts.address} ...`);
32
+ try {
33
+ await hre.run("verify:verify", {
34
+ contract: contractPath,
35
+ address: opts.address,
36
+ constructorArguments: opts.constructorArgs || [],
37
+ });
38
+ console.log(`${opts.contractName} verified`);
39
+ }
40
+ catch (e) {
41
+ if (e.message?.includes("Already Verified")) {
42
+ console.log(`${opts.contractName} already verified`);
43
+ }
44
+ else {
45
+ console.log(`verification failed: ${e.message || e}`);
46
+ // Print manual command as fallback
47
+ const args = (opts.constructorArgs || []).map((a) => typeof a === "string" ? `'${a}'` : a).join(" ");
48
+ console.log(`manual: npx hardhat verify --network ${hre.network.name} --contract ${contractPath} ${opts.address} ${args}`);
49
+ }
50
+ }
51
+ }
52
+ // ============================================================
53
+ // Tron verification via TronScan API
54
+ // ============================================================
55
+ async function verifyTron(hre, opts) {
56
+ const chainId = hre.network.config.chainId;
57
+ const fs = require("fs");
58
+ const path = require("path");
59
+ // Auto-read compiler settings from hardhat config
60
+ const solcConfig = hre.config?.solidity?.compilers?.[0] || hre.config?.solidity || {};
61
+ const compiler = opts.compiler || solcConfig.version || "0.8.25";
62
+ const optimizer = opts.optimizer ?? solcConfig.settings?.optimizer?.enabled ?? true;
63
+ const optimizerRuns = opts.optimizerRuns ?? solcConfig.settings?.optimizer?.runs ?? 200;
64
+ const evmVersion = solcConfig.settings?.evmVersion || "london";
65
+ const viaIR = solcConfig.settings?.viaIR ? "1" : "0";
66
+ // Convert address to Tron format
67
+ let address = opts.address;
68
+ if (address.startsWith("0x")) {
69
+ address = (0, tronHelper_1.tronFromHex)(address);
70
+ }
71
+ // Generate flattened source
72
+ const outputDir = path.join(process.cwd(), "verify-output");
73
+ const flattenPath = path.join(outputDir, `${opts.contractName}_flatten.sol`);
74
+ console.log(`generating flatten for ${opts.contractName}...`);
75
+ try {
76
+ // Find actual source path from artifact
77
+ const artifact = await hre.artifacts.readArtifact(opts.contractName);
78
+ const sourcePath = artifact.sourceName; // e.g. "contracts/factory/Create2Factory.sol"
79
+ let flattenedSource = await hre.run("flatten:get-flattened-sources", {
80
+ files: [sourcePath],
81
+ });
82
+ flattenedSource = removeDuplicateSPDX(flattenedSource);
83
+ fs.mkdirSync(outputDir, { recursive: true });
84
+ fs.writeFileSync(flattenPath, flattenedSource);
85
+ console.log(`flatten saved to: ${flattenPath}`);
86
+ }
87
+ catch (e) {
88
+ if (fs.existsSync(flattenPath)) {
89
+ console.log(`using existing flatten: ${flattenPath}`);
90
+ }
91
+ else {
92
+ console.log(`flatten failed, generate manually: forge flatten contracts/${opts.contractName}.sol`);
93
+ return;
94
+ }
95
+ }
96
+ // Submit to TronScan API
97
+ const apiUrl = TRONSCAN_API[chainId];
98
+ if (!apiUrl) {
99
+ console.log(`no TronScan API for chainId ${chainId}, printing manual instructions instead`);
100
+ printTronVerifyInfo(address, opts.contractName, compiler, optimizer, optimizerRuns, evmVersion, flattenPath, chainId);
101
+ return;
102
+ }
103
+ console.log(`submitting verification to TronScan for ${address}...`);
104
+ try {
105
+ const FormData = require("form-data");
106
+ const form = new FormData();
107
+ form.append("contractAddress", address);
108
+ form.append("contractName", opts.contractName);
109
+ // Read full compiler version from build-info (includes commit hash)
110
+ let fullCompiler = `v${compiler}`;
111
+ try {
112
+ const buildInfoDir = path.join(process.cwd(), "artifacts/build-info");
113
+ const buildInfoFiles = fs.readdirSync(buildInfoDir);
114
+ if (buildInfoFiles.length > 0) {
115
+ const buildInfo = JSON.parse(fs.readFileSync(path.join(buildInfoDir, buildInfoFiles[0]), "utf-8"));
116
+ if (buildInfo.solcLongVersion) {
117
+ fullCompiler = `v${buildInfo.solcLongVersion}`;
118
+ }
119
+ }
120
+ }
121
+ catch (e) {
122
+ console.log(`[warn] could not read build-info: ${e.message}`);
123
+ }
124
+ form.append("compiler", fullCompiler);
125
+ form.append("license", "3"); // MIT
126
+ form.append("optimizer", optimizer ? "1" : "0");
127
+ form.append("runs", String(optimizerRuns));
128
+ form.append("viaIR", viaIR);
129
+ form.append("evmVersion", evmVersion);
130
+ // Encode constructor params from ABI if not pre-encoded
131
+ let constructorParams = opts.constructorParams || "";
132
+ if (!constructorParams && opts.constructorArgs && opts.constructorArgs.length > 0) {
133
+ const { Interface } = require("ethers");
134
+ const artifact = await hre.artifacts.readArtifact(opts.contractName);
135
+ const iface = new Interface(artifact.abi);
136
+ const encoded = iface.encodeDeploy(opts.constructorArgs);
137
+ constructorParams = encoded.slice(2); // remove 0x
138
+ }
139
+ form.append("constructorParams", constructorParams);
140
+ form.append("files", fs.createReadStream(flattenPath), {
141
+ filename: `${opts.contractName}.flat.tron.sol`,
142
+ contentType: "application/octet-stream",
143
+ });
144
+ const result = await new Promise((resolve, reject) => {
145
+ const url = new URL(apiUrl);
146
+ const https = require("https");
147
+ const req = https.request({
148
+ hostname: url.hostname,
149
+ path: url.pathname,
150
+ method: "POST",
151
+ headers: form.getHeaders(),
152
+ }, (res) => {
153
+ let data = "";
154
+ res.on("data", (chunk) => data += chunk);
155
+ res.on("end", () => {
156
+ try {
157
+ resolve(JSON.parse(data));
158
+ }
159
+ catch {
160
+ resolve({ code: -1, errmsg: data });
161
+ }
162
+ });
163
+ });
164
+ req.on("error", reject);
165
+ form.pipe(req);
166
+ });
167
+ if (result.code === 200 || result.success) {
168
+ console.log(`${opts.contractName} verified on TronScan: ${result.data?.message || "success"}`);
169
+ }
170
+ else {
171
+ console.log(`TronScan response:`, JSON.stringify(result, null, 2));
172
+ printTronVerifyInfo(address, opts.contractName, fullCompiler, optimizer, optimizerRuns, evmVersion, flattenPath, chainId);
173
+ }
174
+ }
175
+ catch (e) {
176
+ console.log(`TronScan API error: ${e.message || e}`);
177
+ printTronVerifyInfo(address, opts.contractName, `v${compiler}`, optimizer, optimizerRuns, evmVersion, flattenPath, chainId);
178
+ }
179
+ }
180
+ function printTronVerifyInfo(address, contractName, compiler, optimizer, runs, evmVersion, flattenPath, chainId) {
181
+ const isMainnet = chainId === 728126428;
182
+ const tronscanUrl = isMainnet
183
+ ? `https://tronscan.org/#/contract/${address}/code`
184
+ : `https://nile.tronscan.org/#/contract/${address}/code`;
185
+ console.log(`\n=== Verify manually on TronScan ===`);
186
+ console.log(`Address: ${address}`);
187
+ console.log(`Contract: ${contractName}`);
188
+ console.log(`Compiler: ${compiler}`);
189
+ console.log(`Optimizer: ${optimizer ? "enabled" : "disabled"}, runs: ${runs}`);
190
+ console.log(`EVM: ${evmVersion}`);
191
+ console.log(`Flatten: ${flattenPath}`);
192
+ console.log(`URL: ${tronscanUrl}`);
193
+ console.log(`=====================================\n`);
194
+ }
195
+ function removeDuplicateSPDX(source) {
196
+ let found = false;
197
+ return source.split("\n").filter(line => {
198
+ if (line.includes("SPDX-License-Identifier")) {
199
+ if (found)
200
+ return false;
201
+ found = true;
202
+ }
203
+ return true;
204
+ }).join("\n");
205
+ }