@ethereum-sourcify/bytecode-utils
Version:
Decode the CBOR encoded data at the end of an Ethereum contract's bytecode.
282 lines • 19.4 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.splitAuxdata = exports.decode = exports.AuxdataStyle = void 0;
const bytes_1 = require("@ethersproject/bytes");
const bs58_1 = __importDefault(require("bs58"));
const CBOR = __importStar(require("cbor-x"));
const semver_1 = __importDefault(require("semver"));
var AuxdataStyle;
(function (AuxdataStyle) {
AuxdataStyle["SOLIDITY"] = "solidity";
AuxdataStyle["VYPER"] = "vyper";
AuxdataStyle["VYPER_LT_0_3_10"] = "vyper_lt_0_3_10";
AuxdataStyle["VYPER_LT_0_3_5"] = "vyper_lt_0_3_5";
})(AuxdataStyle || (exports.AuxdataStyle = AuxdataStyle = {}));
/**
* Decode contract's bytecode
* @param bytecode - hex of the bytecode with 0x prefix
* @param auxdataStyle - The style of auxdata, check AuxdataStyle enum for more info
* @returns Object describing the contract
*/
const decode = (bytecode, auxdataStyle) => {
if (bytecode.length === 0) {
throw Error('Bytecode cannot be null');
}
if (bytecode.substring(0, 2) !== '0x') {
bytecode = '0x' + bytecode;
}
// split auxdata
const [, auxdata] = (0, exports.splitAuxdata)(bytecode, auxdataStyle);
if (!auxdata) {
throw Error('Auxdata is not in the bytecode');
}
// See more here: https://github.com/vyperlang/vyper/pull/3010
if (auxdataStyle === AuxdataStyle.VYPER) {
// cbor decode the object and get a json
const cborDecodedObject = CBOR.decode((0, bytes_1.arrayify)(`0x${auxdata}`));
// Starting with version 0.3.10, Vyper stores the auxdata as an array
// after 0.3.10: [runtimesize, datasize,immutablesize,version_cbor_object]
// after 0.4.1: [integrity,runtimesize, datasize,immutablesize,version_cbor_object]
// See more here: https://github.com/vyperlang/vyper/pull/3584
if (cborDecodedObject instanceof Array) {
// read the last element from array, it contains the compiler version
const compilerVersion = cborDecodedObject[cborDecodedObject.length - 1].vyper.join('.');
if (semver_1.default.gte(compilerVersion, '0.4.1')) {
// Starting with version 0.4.1 Vyper added the integrity field
// See more here: https://github.com/vyperlang/vyper/pull/4234
return {
integrity: cborDecodedObject[0],
runtimeSize: cborDecodedObject[1],
dataSizes: cborDecodedObject[2],
immutableSize: cborDecodedObject[3],
vyperVersion: compilerVersion,
};
}
else if (semver_1.default.gte(compilerVersion, '0.3.10')) {
return {
runtimeSize: cborDecodedObject[0],
dataSizes: cborDecodedObject[1],
immutableSize: cborDecodedObject[2],
vyperVersion: compilerVersion,
};
}
}
throw Error('This version of Vyper is not supported');
}
else if (auxdataStyle === AuxdataStyle.VYPER_LT_0_3_10 ||
auxdataStyle === AuxdataStyle.VYPER_LT_0_3_5) {
// cbor decode the object and get a json
const cborDecodedObject = CBOR.decode((0, bytes_1.arrayify)(`0x${auxdata}`));
return {
vyperVersion: cborDecodedObject.vyper.join('.'),
};
}
else if (auxdataStyle === AuxdataStyle.SOLIDITY) {
// cbor decode the object and get a json
const cborDecodedObject = CBOR.decode((0, bytes_1.arrayify)(`0x${auxdata}`));
const result = {};
// Decode all the parameters from the json
Object.keys(cborDecodedObject).forEach((key) => {
switch (key) {
case 'ipfs': {
const ipfsCID = bs58_1.default.encode(cborDecodedObject.ipfs);
result.ipfs = ipfsCID;
break;
}
case 'solc': {
// nightly builds are string encoded
if (typeof cborDecodedObject.solc === 'string') {
result.solcVersion = cborDecodedObject.solc;
}
else {
result.solcVersion = cborDecodedObject.solc.join('.');
}
break;
}
case 'experimental': {
result.experimental = cborDecodedObject.experimental;
break;
}
case 'bzzr0':
case 'bzzr1':
default: {
result[key] = (0, bytes_1.hexlify)(cborDecodedObject[key]);
break;
}
}
});
return result;
}
else {
throw Error('Invalid auxdata style');
}
};
exports.decode = decode;
/**
* Splits the bytecode into execution bytecode and auxdata.
* If the bytecode does not contain CBOR-encoded auxdata, returns the whole bytecode.
*
* @param bytecode - Hex string of the bytecode with 0x prefix
* @param auxdataStyle - The style of auxdata (Solidity or Vyper)
* @returns An array containing execution bytecode and optionally auxdata and its length
*/
const splitAuxdata = (bytecode, auxdataStyle) => {
validateBytecode(bytecode);
bytecode = ensureHexPrefix(bytecode);
const bytesLength = 4;
const cborBytesLength = getCborBytesLength(bytecode, auxdataStyle, bytesLength);
if (isCborLengthInvalid(bytecode, cborBytesLength, bytesLength)) {
return [bytecode];
}
const auxdata = extractAuxdata(bytecode, auxdataStyle, cborBytesLength, bytesLength);
const executionBytecode = extractExecutionBytecode(bytecode, cborBytesLength, bytesLength);
if (isCborEncoded(auxdata)) {
const cborLengthHex = getCborLengthHex(bytecode, auxdataStyle, bytesLength);
return [executionBytecode, auxdata, cborLengthHex];
}
return [bytecode];
};
exports.splitAuxdata = splitAuxdata;
/**
* Validates that the bytecode is not empty.
*
* @param bytecode - The bytecode string to validate
*/
const validateBytecode = (bytecode) => {
if (bytecode.length === 0) {
throw Error('Bytecode cannot be null');
}
};
/**
* Ensures the bytecode string starts with '0x'.
*
* @param bytecode - The bytecode string
* @returns The bytecode string with '0x' prefix
*/
const ensureHexPrefix = (bytecode) => {
return bytecode.startsWith('0x') ? bytecode : `0x${bytecode}`;
};
/**
* Determines the length of the CBOR auxdata in bytes.
*
* @param bytecode - The complete bytecode string
* @param auxdataStyle - The style of auxdata
* @param bytesLength - The length of bytes used to encode the CBOR length
* @returns An object containing the CBOR bytes length and a flag for legacy Vyper
*/
const getCborBytesLength = (bytecode, auxdataStyle, bytesLength) => {
if (auxdataStyle === AuxdataStyle.VYPER_LT_0_3_5) {
return 22;
}
const cborLengthHex = bytecode.slice(-bytesLength);
return parseInt(cborLengthHex, 16) * 2;
};
/**
* Checks if the CBOR length is invalid based on the bytecode length.
*
* @param bytecode - The complete bytecode string
* @param cborBytesLength - The length of CBOR auxdata in bytes
* @param bytesLength - The length of bytes used to encode the CBOR length
* @returns True if the CBOR length is invalid, otherwise false
*/
const isCborLengthInvalid = (bytecode, cborBytesLength, bytesLength) => {
return bytecode.length - bytesLength - cborBytesLength <= 0;
};
/**
* Extracts the auxdata from the bytecode based on the auxdata style.
*
* @param bytecode - The complete bytecode string
* @param auxdataStyle - The style of auxdata
* @param cborBytesLength - The length of CBOR auxdata in bytes
* @param bytesLength - The length of bytes used to encode the CBOR length
* @returns The extracted auxdata as a hex string
*/
const extractAuxdata = (bytecode, auxdataStyle, cborBytesLength, bytesLength) => {
switch (auxdataStyle) {
case AuxdataStyle.VYPER_LT_0_3_10:
case AuxdataStyle.SOLIDITY:
return bytecode.substring(bytecode.length - bytesLength - cborBytesLength, bytecode.length - bytesLength);
case AuxdataStyle.VYPER:
return bytecode.substring(bytecode.length - cborBytesLength, bytecode.length - bytesLength);
case AuxdataStyle.VYPER_LT_0_3_5:
return bytecode.substring(bytecode.length - 22, bytecode.length);
default:
throw Error('Unsupported auxdata style');
}
};
/**
* Extracts the execution bytecode from the complete bytecode string.
*
* @param bytecode - The complete bytecode string
* @param cborBytesLength - The length of CBOR auxdata in bytes
* @param bytesLength - The length of bytes used to encode the CBOR length
* @returns The execution bytecode as a hex string
*/
const extractExecutionBytecode = (bytecode, cborBytesLength, bytesLength) => {
return bytecode.substring(0, bytecode.length - bytesLength - cborBytesLength);
};
/**
* Attempts to decode the auxdata to verify if it's CBOR-encoded.
*
* @param auxdata - The auxdata string to decode
* @returns True if auxdata is CBOR-encoded, otherwise false
*/
const isCborEncoded = (auxdata) => {
try {
CBOR.decode((0, bytes_1.arrayify)(`0x${auxdata}`));
return true;
}
catch (_a) {
return false;
}
};
/**
* Retrieves the CBOR length from the bytecode based on the auxdata style.
*
* @param bytecode - The complete bytecode string
* @param auxdataStyle - The style of auxdata
* @param bytesLength - The length of bytes used to encode the CBOR length
* @returns The CBOR length as a hex string
*/
const getCborLengthHex = (bytecode, auxdataStyle, bytesLength) => {
if (auxdataStyle === AuxdataStyle.VYPER_LT_0_3_5)
return '';
return bytecode.slice(-bytesLength);
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnl0ZWNvZGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL2J5dGVjb2RlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLGdEQUF5RDtBQUN6RCxnREFBd0I7QUFDeEIsNkNBQStCO0FBQy9CLG9EQUE0QjtBQTBCNUIsSUFBWSxZQUtYO0FBTEQsV0FBWSxZQUFZO0lBQ3RCLHFDQUFxQixDQUFBO0lBQ3JCLCtCQUFlLENBQUE7SUFDZixtREFBbUMsQ0FBQTtJQUNuQyxpREFBaUMsQ0FBQTtBQUNuQyxDQUFDLEVBTFcsWUFBWSw0QkFBWixZQUFZLFFBS3ZCO0FBRUQ7Ozs7O0dBS0c7QUFDSSxNQUFNLE1BQU0sR0FBRyxDQUNwQixRQUFnQixFQUNoQixZQUFlLEVBR00sRUFBRTtJQUN2QixJQUFJLFFBQVEsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDMUIsTUFBTSxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBQ0QsSUFBSSxRQUFRLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUN0QyxRQUFRLEdBQUcsSUFBSSxHQUFHLFFBQVEsQ0FBQztJQUM3QixDQUFDO0lBRUQsZ0JBQWdCO0lBQ2hCLE1BQU0sQ0FBQyxFQUFFLE9BQU8sQ0FBQyxHQUFHLElBQUEsb0JBQVksRUFBQyxRQUFRLEVBQUUsWUFBWSxDQUFDLENBQUM7SUFDekQsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2IsTUFBTSxLQUFLLENBQUMsZ0NBQWdDLENBQUMsQ0FBQztJQUNoRCxDQUFDO0lBRUQsOERBQThEO0lBQzlELElBQUksWUFBWSxLQUFLLFlBQVksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUN4Qyx3Q0FBd0M7UUFDeEMsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLENBQUMsTUFB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
;