sevm
Version:
A Symbolic Ethereum Virtual Machine (EVM) bytecode decompiler & analyzer library & CLI
124 lines (110 loc) • 3.67 kB
text/typescript
import { type Expr, Tag } from '.';
import type { Type } from '../abi';
/**
* Represents a global Solidity built-in property.
*
* @see https://docs.soliditylang.org/en/develop/units-and-global-variables.html#special-variables-and-functions
*/
export class Prop extends Tag {
readonly tag = 'Prop';
constructor(readonly symbol: string, override readonly type: Type) {
super(0, 1);
}
eval(): Expr {
return this;
}
}
const prop = <S extends string>(prop: readonly [S, Type]) =>
[prop[0], new Prop(prop[0], prop[1])] as const;
const applyPrefix = <const S extends string, const O extends string>(
props: readonly (readonly [S, Type])[], obj: O
) => props.map(([field, type]) => [`${obj}.${field}`, type] as const);
/**
* A collection of _Block and Transaction Properties_ defined as `Prop`.
*
* @see https://docs.soliditylang.org/en/develop/units-and-global-variables.html#block-and-transaction-properties
* @see {@link Prop}
*/
export const Props = Object.assign(
Object.fromEntries(([
['address(this)', 'address'],
['codesize()', 'uint'],
['returndatasize()', 'uint'],
['address(this).balance', 'uint'],
['gasleft()', 'uint'],
] as const).map(prop)),
Object.fromEntries(applyPrefix([
['basefee', 'uint'],
['coinbase', 'address payable'],
['timestamp', 'uint'],
['number', 'uint'],
['difficulty', 'uint'],
['gaslimit', 'uint'],
['chainid', 'uint'],
['prevrandao', 'uint'],
] as const, 'block').map(prop)),
Object.fromEntries(applyPrefix([
['sender', 'address'],
['data.length', 'uint'],
] as const, 'msg').map(prop)),
Object.fromEntries(applyPrefix([
['origin', 'address'],
['gasprice', 'uint'],
] as const, 'tx').map(prop)),
);
export const FNS = {
BALANCE: [(address: string) => `${address}.balance`, 'uint256', 0x31],
EXTCODESIZE: [(address: string) => `address(${address}).code.length`, 'uint256', 0x3b],
EXTCODEHASH: [(address: string) => `keccak256(address(${address}).code)`, 'bytes32', 0x3f],
BLOCKHASH: [(blockNumber: string) => `blockhash(${blockNumber})`, 'bytes32', 0x40],
} as const;
export class Fn extends Tag {
readonly tag = 'Fn';
constructor(readonly mnemonic: keyof typeof FNS, readonly value: Expr) {
super(value.depth + 1, value.count + 1);
this.type = FNS[mnemonic][1];
}
eval(): Expr {
return new Fn(this.mnemonic, this.value.eval());
}
}
export class DataCopy extends Tag {
readonly tag = 'DataCopy';
constructor(
readonly kind: 'calldatacopy' | 'codecopy' | 'extcodecopy' | 'returndatacopy',
readonly offset: Expr,
readonly size: Expr,
readonly address?: Expr,
readonly bytecode?: Uint8Array,
) {
super(
Math.max(offset.depth, size.depth, address?.depth ?? 0) + 1,
offset.count + size.count + (address?.count ?? 0) + 1);
}
eval(): this {
return this;
}
}
/**
* Get deposited value by the instruction/transaction responsible for this execution.
*/
export class CallValue extends Tag {
readonly tag = 'CallValue';
constructor() {
super(0, 1);
}
eval(): Expr {
return this;
}
}
export class CallDataLoad extends Tag {
readonly tag = 'CallDataLoad';
constructor(public location: Expr) {
super(location.depth + 1, location.count + 1);
}
eval(): Expr {
return new CallDataLoad(this.location.eval());
// this.location = this.location.eval();
// return this;
}
}