UNPKG

@dydxfoundation/governance

Version:
111 lines (110 loc) 5.93 kB
"use strict"; /* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable global-require */ /* eslint-disable no-unexpected-multiline */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.verifyContract = void 0; const path_1 = __importDefault(require("path")); const ethers_1 = require("ethers"); const lodash_1 = __importDefault(require("lodash")); const hre_1 = require("../hre"); const hex_1 = require("./hex"); const logging_1 = require("./logging"); const ARTIFACTS_DIR = '../../artifacts'; async function verifyContract(contractPath, contractName, deployedAddress, immutableValues = {}) { const hre = (0, hre_1.getHre)(); // Load-in compiled JSON files. const solidityPath = path_1.default.join(contractPath, `${contractName}.sol`); const contractJson = require(path_1.default.join(ARTIFACTS_DIR, `${solidityPath}/${contractName}.json`)); const dbgJson = require(path_1.default.join(ARTIFACTS_DIR, `${solidityPath}/${contractName}.dbg.json`)); const buildInfoJson = require(path_1.default.join(ARTIFACTS_DIR, `build-info/${dbgJson.buildInfo.split('/').pop()}`)); // Get the deployed and expected bytecodes. const deployedBytecodeString = await hre.ethers.provider.getCode(deployedAddress); const deployedBytecode = Buffer.from((0, hex_1.stripHexPrefix)(deployedBytecodeString), 'hex'); const expectedBytecode = Buffer.from((0, hex_1.stripHexPrefix)(contractJson.deployedBytecode), 'hex'); // Find information for immutable values in contract. const immutableReferences = buildInfoJson .output .contracts[solidityPath][contractName] .evm .deployedBytecode .immutableReferences; // For each immutable variable in the contract... Object.keys(immutableReferences).forEach((immutableId) => { var _a; // Find the key associated with this immutableId. // Find an object, `o`, at `sources.*.ast.nodes[*].nodes[*]` where `o.id == immutableId`. // Return `o.name` which is the name of the immutable variable in solidity. const immutableKey = (_a = lodash_1.default.chain(buildInfoJson.output.sources) .values() .map('ast.nodes') .filter() // remove any without `ast.nodes` .flatten() .map('nodes') .filter() .flatten() .find((o) => (o.mutability === 'immutable' && typeof o.id === 'number' && o.id.toString() === immutableId)) .value()) === null || _a === void 0 ? void 0 : _a.name; if (!immutableKey) { throw new Error(`cannot find key for immutableId: ${immutableId}`); } // Get expected value of the immutable. const immutableValue = immutableValues[immutableKey]; if (!immutableValue) { throw new Error(`did not pass-in value for immutableKey: ${immutableKey}`); } // Place the value of the immutable in the expectedBytecode. const bytecodeLocations = immutableReferences[immutableId]; bytecodeLocations.forEach((loc) => { const expectedValueHex = ethers_1.utils.hexValue(immutableValue); const expectedValueHexPadded = ethers_1.utils.hexZeroPad(expectedValueHex, loc.length); const stringifiedValue = Buffer.from((0, hex_1.stripHexPrefix)(expectedValueHexPadded), 'hex'); for (let i = 0; i < loc.length; i += 1) { expectedBytecode[loc.start + i] = stringifiedValue[i]; } }); }); (0, logging_1.log)(`Using network: ${(await hre.ethers.provider.getNetwork()).chainId}\n`); (0, logging_1.log)(`Checking bytecode deployed at ${deployedAddress} against ` + `the locally compiled bytecode for contract '${solidityPath}'.`); if (deployedBytecode.length !== expectedBytecode.length) { throw new Error(`Deployed bytecode length: ${deployedBytecode.length}, ` + `Expected bytecode length: ${expectedBytecode.length}`); } const deployedMetadata = getMetadata(deployedBytecode); const expectedMetadata = getMetadata(expectedBytecode); if (deployedMetadata.length !== expectedMetadata.length) { throw new Error(`Deployed bytecode metadata length: ${deployedMetadata.length}, ` + `Expected bytecode metadata length: ${expectedMetadata.length}`); } const metadataLength = deployedMetadata.length; const codeLength = deployedBytecode.length - metadataLength; const metadataMatch = deployedMetadata.equals(expectedMetadata); const deployedCode = deployedBytecode.slice(0, codeLength); const expectedCode = expectedBytecode.slice(0, codeLength); const bytecodeMatch = deployedCode.equals(expectedCode); (0, logging_1.log)('\n'); (0, logging_1.log)(`Metadata length: ${metadataLength} bytes`); (0, logging_1.log)(`Bytecode length (non-metadata): ${codeLength} bytes`); (0, logging_1.log)(`Metadata match: ${metadataMatch}`); (0, logging_1.log)(`Bytecode match: ${bytecodeMatch}`); // Do not throw error on Metadata mismatch since it may be different depending on the local filesystem. // https://docs.soliditylang.org/en/develop/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode // https://github.com/trufflesuite/truffle-compile/issues/77 if (!bytecodeMatch) { throw new Error('Bytecode mismatch.'); } (0, logging_1.log)('\nPassed all checks.'); } exports.verifyContract = verifyContract; function getMetadata(bytecode) { // The last two bytes should represent the metadata length. const length = bytecode.length; const metadataLength = bytecode[length - 2] * 2 ** 8 + bytecode[length - 1]; return bytecode.slice(length - metadataLength); }