UNPKG

sevm

Version:

A Symbolic Ethereum Virtual Machine (EVM) bytecode decompiler & analyzer library & CLI

133 lines 4.84 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.splitMetadataHash = exports.Metadata = void 0; const cbor_js_1 = __importDefault(require("cbor-js")); const _bytes_1 = require("./.bytes"); /** * https://docs.soliditylang.org/en/v0.5.8/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode * * https://docs.soliditylang.org/en/v0.5.9/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode * https://blog.soliditylang.org/2019/05/28/solidity-0.5.9-release-announcement/ * * v0.6.2 ends with `0x00 0x33` but v0.6.1 ends with `0x00 0x32` * https://blog.soliditylang.org/2019/12/17/solidity-0.6.0-release-announcement/ * https://docs.soliditylang.org/en/v0.6.2/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode */ /** * Represents the metadata hash protocols embedded in bytecode by `solc`. */ class Metadata { constructor() { this.protocol = ''; this.hash = ''; this.solc = ''; } get url() { return `${this.protocol}://${this.hash}`; } get minor() { const field = /^0\.(\d+)\./.exec(this.solc)?.[1]; return field ? parseInt(field) : undefined; } } exports.Metadata = Metadata; /** * Splits the `bytecode` into executable code and embedded metadata hash as * placed by the Solidity compiler, if present in the `bytecode`. * * If `metadata` contains an IPFS hash, it is encoded using base 58.[^1] * * @param bytecode the contract or library `bytecode` to test for metadata hash. * @returns An object where the `bytecode` is the executable code and * `metadata` is the metadata hash when the metadata is present. * * [^1]: https://github.com/pur3miish/base58-js */ function splitMetadataHash(buffer) { const bytecode = (0, _bytes_1.arrayify)(buffer); if (buffer.length <= 2) return { bytecode, metadata: undefined }; const dataLen = (bytecode.at(-2) << 8) + bytecode.at(-1); const data = new Uint8Array(bytecode.subarray(bytecode.length - 2 - dataLen, bytecode.length - 2)); if (data.length !== dataLen) return { bytecode, metadata: undefined }; try { const obj = cbor_js_1.default.decode(data.buffer); const metadata = new Metadata(); if ('ipfs' in obj && obj['ipfs'] instanceof Uint8Array) { metadata.protocol = 'ipfs'; metadata.hash = bs58(obj['ipfs']); delete obj['ipfs']; } else if ('bzzr0' in obj && obj['bzzr0'] instanceof Uint8Array) { metadata.protocol = 'bzzr0'; metadata.hash = (0, _bytes_1.hexlify)(obj['bzzr0']); delete obj['bzzr0']; } else if ('bzzr1' in obj && obj['bzzr1'] instanceof Uint8Array) { metadata.protocol = 'bzzr1'; metadata.hash = (0, _bytes_1.hexlify)(obj['bzzr1']); delete obj['bzzr1']; } if ('solc' in obj && obj['solc'] instanceof Uint8Array) { metadata.solc = obj['solc'].join('.'); delete obj['solc']; } return { bytecode: bytecode.subarray(0, bytecode.length - 2 - dataLen), metadata: Object.assign(metadata, obj) }; } catch { return { bytecode, metadata: undefined }; } } exports.splitMetadataHash = splitMetadataHash; /** * Converts a Uint8Array into a base58 string. * * @param buffer Unsigned integer array to encode. * @returns base58 string representation of the binary array. * @example <caption>Usage.</caption> * * ```js * const str = bs58([15, 239, 64]) * console.log(str) * ``` * * Logged output will be 6MRy. */ function bs58(buffer) { /** Base58 characters include numbers `123456789`, uppercase `ABCDEFGHJKLMNPQRSTUVWXYZ` and lowercase `abcdefghijkmnopqrstuvwxyz` */ const chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; /** Mapping between base58 and ASCII */ const base58Map = Array(256).fill(-1); for (let i = 0; i < chars.length; ++i) { base58Map[chars.charCodeAt(i)] = i; } const result = []; for (const byte of buffer) { let carry = byte; for (let j = 0; j < result.length; ++j) { const x = (base58Map[result[j]] << 8) + carry; result[j] = chars.charCodeAt(x % 58); carry = (x / 58) | 0; } while (carry) { result.push(chars.charCodeAt(carry % 58)); carry = (carry / 58) | 0; } } for (const byte of buffer) { if (byte) break; else result.push('1'.charCodeAt(0)); } result.reverse(); return String.fromCharCode(...result); } //# sourceMappingURL=metadata.js.map