sevm
Version:
A Symbolic Ethereum Virtual Machine (EVM) bytecode decompiler & analyzer library & CLI
227 lines • 7.63 kB
JavaScript
;
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _Tokens_tokens;
Object.defineProperty(exports, "__esModule", { value: true });
exports.fnsig = exports.sighash = exports.parseSig = exports.isElemType = void 0;
/**
* Determine whether the given `type` is a valid elementary Solidity type.
*
* @see {@link Type} definition for more info on `Type`.
*
* @param type value to check if it is a valid elementary type.
* @returns
*/
function isElemType(type) {
return ELEM_TYPES.includes(type);
}
exports.isElemType = isElemType;
function getCanonicalType(type) {
switch (type) {
case 'uint': return 'uint256';
case 'int': return 'int256';
case 'fixed': return 'fixed128x18';
case 'ufixed': return 'ufixed128x18';
default: return type;
}
}
/**
*
*/
const BITS = [...Array(32).keys()].map(n => (n + 1) * 8);
/**
*
*/
const BYTES = [...Array(32).keys()].map(n => n + 1);
/**
*
*/
const ELEM_TYPES = [
'address',
'address payable',
'bool',
'uint',
...BITS.map(n => `uint${n}`),
'int',
...BITS.map(n => `int${n}`),
'bytes',
...BYTES.map(n => `bytes${n}`),
'string',
'function',
];
class Tokenizer {
constructor(input) {
this.input = input;
this.ID_REGEX = /^\w+\b/;
this.position = 0;
}
next() {
while (this.input[this.position] === ' ')
this.position++;
const i = this.position;
const matchType = this.input.slice(i).match(this.ID_REGEX);
if (matchType) {
this.position += matchType[0].length;
const kind = isElemType(matchType[0])
? 'TYPE'
: !Number.isNaN(Number(matchType[0]))
? 'LIT'
: 'ID';
return [i, kind, matchType[0]];
}
switch (this.input[i]) {
case '(':
case ')':
case ',':
case '[':
case ']':
this.position++;
return [i, 'OP', this.input[i]];
}
return [i, null, null];
}
*[Symbol.iterator]() {
for (let [pos, kind, token] = this.next(); token !== null; [pos, kind, token] = this.next()) {
yield [pos + 1, kind, token];
}
}
}
class Tokens {
constructor(sig) {
this.sig = sig;
_Tokens_tokens.set(this, []);
__classPrivateFieldSet(this, _Tokens_tokens, [...new Tokenizer(sig)], "f");
}
peek() {
if (__classPrivateFieldGet(this, _Tokens_tokens, "f").length === 0)
throw new Error('peek: reached end of input');
return __classPrivateFieldGet(this, _Tokens_tokens, "f")[0];
}
next() {
if (__classPrivateFieldGet(this, _Tokens_tokens, "f").length === 0)
throw new Error('next: reached end of input');
return __classPrivateFieldGet(this, _Tokens_tokens, "f").shift();
}
consume(value) {
const [, , next] = this.next();
if (next !== value)
throw new Error(`Expected '${value}' but got '${next}'`);
}
}
_Tokens_tokens = new WeakMap();
function parseSig(sig) {
const tokens = new Tokens(sig);
let [pos, kind, name] = tokens.next();
if (name === 'function')
[pos, kind, name] = tokens.next();
if (kind !== 'ID')
throw new Error(`Expected function name, found '${name}':${pos}`);
const inputs = [];
tokens.consume('(');
let [, , n] = tokens.peek();
while (n !== ')') {
inputs.push(parseParam());
[, , n] = tokens.peek();
if (n === ',')
tokens.consume(',');
}
tokens.consume(')');
return { name, inputs };
function parseParam() {
const ty = parseType();
const [, kind, name] = tokens.peek();
if (kind !== 'ID')
return { ...ty };
tokens.consume(name);
return { ...ty, name };
}
function parseType() {
const baseType = function () {
// eslint-disable-next-line prefer-const
let [pos, , ty] = tokens.peek();
if (ty === '(') {
const components = [];
tokens.consume('(');
let tupTy;
[, , ty] = tokens.peek();
while (ty !== ')') {
tupTy = parseType();
components.push(tupTy);
[, , ty] = tokens.peek();
if (ty === ',')
tokens.consume(',');
}
tokens.consume(')');
return { type: 'tuple', components };
}
else if (isElemType(ty)) {
tokens.next();
return { type: getCanonicalType(ty) };
}
else {
throw new Error(`Invalid elementary type found: \`${ty}\` at position ${pos} in \`${sig}\``);
}
}();
const dims = [];
let [, , array] = tokens.peek();
while (array === '[') {
tokens.consume('[');
const [, kind, size] = tokens.peek();
if (kind === 'LIT') {
tokens.consume(size);
tokens.consume(']');
dims.push(Number(size));
}
else {
dims.push(null);
tokens.consume(']');
}
[, , array] = tokens.peek();
}
return dims.reduce((ty, size, i) => {
return {
type: `${baseType.type}${dims.slice(0, i + 1).map(size => `[${size === null ? '' : size}]`).join('')}`,
// baseType: 'array',
arrayType: ty,
arrayLength: size
};
}, baseType);
}
}
exports.parseSig = parseSig;
/**
*
* https://docs.soliditylang.org/en/latest/abi-spec.html#handling-tuple-types
*
* @param member
*/
function sighash(member) {
return `${member.name}(${member.inputs.map(sighashType).join(',')})`;
}
exports.sighash = sighash;
function sighashType(ty) {
if (ty.arrayType !== undefined) {
const len = ty.arrayLength === null ? '' : ty.arrayLength;
return `${sighashType(ty.arrayType)}[${len}]`;
}
else if (ty.type === 'tuple') {
return `(${ty.components.map(sighashType).join(',')})`;
}
else {
return ty.type;
}
}
function fnsig(member) {
return `${member.name}(${member.inputs.map((param, i) => `${sighashType(param)} ${param.name ?? '_arg' + i}`).join(', ')})`;
}
exports.fnsig = fnsig;
//# sourceMappingURL=abi.js.map