UNPKG

@matterlabs/hardhat-zksync-verify

Version:
107 lines 4.95 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.normalizeBytecode = exports.compareBytecode = exports.extractMatchingContractInformation = exports.Bytecode = void 0; const metadata_1 = require("./metadata"); class Bytecode { constructor(bytecode) { this._bytecode = bytecode; const { solcVersion, metadataSectionSizeInBytes } = (0, metadata_1.inferSolcVersion)(Buffer.from(bytecode, 'hex')); this._version = solcVersion; this._executableSection = { start: 0, length: bytecode.length - metadataSectionSizeInBytes * 2, }; this._metadataSection = { start: this._executableSection.length, length: metadataSectionSizeInBytes * 2, }; } getInferredSolcVersion() { return this._version; } getExecutableSection() { const { start, length } = this._executableSection; return this._bytecode.slice(start, length); } hasMetadata() { return this._metadataSection.length > 0; } } exports.Bytecode = Bytecode; async function extractMatchingContractInformation(sourceName, contractName, buildInfo, deployedBytecode) { const contract = buildInfo.output.contracts[sourceName][contractName]; if (contract.hasOwnProperty('evm')) { const { bytecode: runtimeBytecodeSymbols } = contract.evm; const analyzedBytecode = runtimeBytecodeSymbols ? await compareBytecode(deployedBytecode, runtimeBytecodeSymbols) : null; if (analyzedBytecode !== null) { return { ...analyzedBytecode, compilerInput: buildInfo.input, contractOutput: buildInfo.output.contracts[sourceName][contractName], solcVersion: buildInfo.solcVersion, solcLongVersion: buildInfo.solcLongVersion, sourceName, contractName, }; } return null; } return null; } exports.extractMatchingContractInformation = extractMatchingContractInformation; async function compareBytecode(deployedBytecode, runtimeBytecodeSymbols) { // We will ignore metadata information when comparing. Etherscan seems to do the same. const deployedExecutableSection = deployedBytecode.getExecutableSection(); const runtimeBytecodeExecutableSectionLength = runtimeBytecodeSymbols.object.length; if (deployedExecutableSection.length !== runtimeBytecodeExecutableSectionLength) { return null; } // Normalize deployed bytecode according to this contract. const { normalizedBytecode } = await normalizeBytecode(deployedExecutableSection, runtimeBytecodeSymbols); const { normalizedBytecode: referenceBytecode } = await normalizeBytecode(runtimeBytecodeSymbols.object, runtimeBytecodeSymbols); // If we don't have metadata detected, it could still have keccak metadata hash. // We cannot check that here, so we will assume that it's present. If not, it will be caught // during verification. // Keccak hash is 32 bytes, but given that we're working with hex strings, it's 64 characters. const bytecodeLength = deployedBytecode.hasMetadata() ? deployedExecutableSection.length : deployedExecutableSection.length > 64 ? deployedExecutableSection.length - 64 : deployedExecutableSection.length; if (normalizedBytecode.slice(0, bytecodeLength) === referenceBytecode.slice(0, bytecodeLength)) { // The bytecode matches return { normalizedBytecode, }; } return null; } exports.compareBytecode = compareBytecode; async function normalizeBytecode(bytecode, symbols) { const nestedSliceReferences = []; // To normalize a library object we need to take into account its call protection mechanism // The runtime code of a library always starts with a push instruction (a zero of 20 bytes at compilation time) // This constant is replaced in memory by the current address and this modified code is stored in the contract const addressSize = 20; const push20OpcodeHex = '73'; const pushPlaceholder = push20OpcodeHex + '0'.repeat(addressSize * 2); if (bytecode.startsWith(pushPlaceholder) && symbols.object.startsWith(push20OpcodeHex)) { nestedSliceReferences.push([{ start: 1, length: addressSize }]); } const sliceReferences = flattenSlices(nestedSliceReferences); const normalizedBytecode = zeroOutSlices(bytecode, sliceReferences); return { normalizedBytecode }; } exports.normalizeBytecode = normalizeBytecode; function flattenSlices(slices) { return [].concat(...slices); } function zeroOutSlices(code, slices) { for (const { start, length } of slices) { code = [code.slice(0, start * 2), '0'.repeat(length * 2), code.slice((start + length) * 2)].join(''); } return code; } //# sourceMappingURL=bytecode.js.map