@matterlabs/hardhat-zksync-verify
Version:
Hardhat plugin to verify smart contracts for the ZKsync network
78 lines (64 loc) • 3.02 kB
text/typescript
import type cbor from 'cbor';
import debug from 'debug';
import util from 'util';
interface MetadataDescription {
solcVersion: string;
metadataSectionSizeInBytes: number;
}
export const METADATA_LENGTH_SIZE = 2;
export const METADATA_PRESENT_SOLC_NOT_FOUND_VERSION_RANGE = '0.4.7 - 0.5.8';
export const METADATA_ABSENT_VERSION_RANGE = '<0.4.7';
const log = debug('hardhat:hardhat-etherscan:metadata');
export function inferSolcVersion(bytecode: Buffer): MetadataDescription {
let solcMetadata;
let metadataSectionSizeInBytes;
try {
const metadata = decodeSolcMetadata(bytecode);
log(`Metadata decoded: ${util.inspect(metadata.decoded)}`);
metadataSectionSizeInBytes = metadata.metadataSectionSizeInBytes;
solcMetadata = metadata.decoded.solc;
} catch {
// The decoding failed. Unfortunately, our only option is to assume that this bytecode was emitted by an old version.
// This could also mean that contract has keccak metadata instead of ipfs.
log('Could not decode metadata.');
return {
metadataSectionSizeInBytes: 0,
solcVersion: METADATA_ABSENT_VERSION_RANGE,
};
}
if (solcMetadata instanceof Buffer) {
if (solcMetadata.length === 3) {
const [major, minor, patch] = solcMetadata;
const solcVersion = `${major}.${minor}.${patch}`;
log(`Solc version detected in bytecode: ${solcVersion}`);
return { metadataSectionSizeInBytes, solcVersion };
}
log(`Found solc version field with ${solcMetadata.length} elements instead of three!`);
}
// The embedded metadata was successfully decoded but there was no solc version in it.
log(`Could not detect solidity version in metadata.`);
return {
metadataSectionSizeInBytes,
solcVersion: METADATA_PRESENT_SOLC_NOT_FOUND_VERSION_RANGE,
};
}
export function decodeSolcMetadata(bytecode: Buffer) {
const metadataSectionLength = getSolcMetadataSectionLength(bytecode);
// The metadata and its length are in the last few bytes.
const metadataPayload = bytecode.slice(-metadataSectionLength, -METADATA_LENGTH_SIZE);
log(`Read metadata length ${metadataSectionLength}`);
const lastMetadataBytes = metadataPayload.slice(-100);
log(`Last ${lastMetadataBytes.length} bytes of metadata: ${lastMetadataBytes.toString('hex')}`);
const { decodeFirstSync }: typeof cbor = require('cbor');
// The documentation for decodeFirst mentions the `required` option even though
// the type information is missing it.
// See http://hildjj.github.io/node-cbor/Decoder.html#.decodeFirst
const decoded = decodeFirstSync(metadataPayload, { required: true });
return {
decoded,
metadataSectionSizeInBytes: metadataSectionLength,
};
}
export function getSolcMetadataSectionLength(bytecode: Buffer) {
return bytecode.slice(-METADATA_LENGTH_SIZE).readUInt16BE(0) + METADATA_LENGTH_SIZE;
}