@matterlabs/hardhat-zksync-verify
Version:
Hardhat plugin to verify smart contracts for the ZKsync network
107 lines • 4.95 kB
JavaScript
;
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