sevm
Version:
A Symbolic Ethereum Virtual Machine (EVM) bytecode decompiler & analyzer library & CLI
133 lines • 4.84 kB
JavaScript
;
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