sol_evm 0.1.2
sol_evm: ^0.1.2 copied to clipboard
EVM opcode table, assembler, bytecode linker, and gas cost model for the sol_pkgs Solidity compiler.
sol_evm #
A pure-Dart Ethereum Virtual Machine — plus the opcode table, two-pass assembler, and
bytecode utilities the sol_pkgs compiler builds on.
No native toolchain, no FFI, no node: deploy a contract, send transactions to it, inspect the resulting state, and persist the whole chain as JSON, all from Dart.
Features #
Execution engine #
Evm— a local chain with a full account model (nonce, balance, code, storage). State accumulates across transactions and survives a process restart.deploy()/call()/staticCall()/simulate()/estimateGas()CREATEandCREATE2, with address derivation matching mainnet- The whole
CALLfamily:CALL,CALLCODE,DELEGATECALL,STATICCALL - Nested frames with journaled snapshot/rollback — a
REVERTdeep in a call tree leaves no trace SELFDESTRUCTunder EIP-6780 rules
WorldState— accounts, storage, EIP-2929 warm/cold access sets, refund accounting, and JSON persistence viaencode()/decode()Interpreter— run one piece of bytecode in isolation, no chain required- Gas: intrinsic transaction cost, memory expansion, EIP-2929 cold/warm access,
EIP-2200 net
SSTOREmetering, EIP-3529 refund caps, the EIP-150 63/64 rule - Tracing: opcode-level
TraceEntrystream for building a step debugger - Structured failures:
HaltReasondistinguishes revert, out-of-gas, invalid opcode, write protection and create failures;Error(string)/Panic(uint256)revert payloads are decoded automatically - Hardforks:
Hardfork.shanghaiandHardfork.cancun
Bytecode tooling #
- Complete
Opcodeenum covering Shanghai + Cancun (includingTLOAD/TSTORE,MCOPY,BLOBHASH,BLOBBASEFEE,PUSH0), each recording byte value, stack consumed/produced, base gas cost and immediate byte count Assemblerfor building bytecode programmatically, with two-pass label resolutionBytecodeLinker, source maps
Usage #
import 'package:sol_evm/sol_evm.dart';
void main() {
final evm = Evm();
final alice = addressFromHex('0x1000000000000000000000000000000000000001');
evm.setBalance(alice, BigInt.from(10).pow(18));
// Deploy. `bytecode` is initcode — the constructor, as solc emits it.
final deployment = evm.deploy(from: alice, bytecode: initCode);
final token = deployment.createdAddress!;
// Send a transaction; state changes stick.
final tx = evm.call(from: alice, to: token, data: transferCalldata);
print('gas ${tx.gasUsed}, ${tx.logs.length} events');
if (!tx.success) print(tx.failureMessage); // e.g. "reverted: insufficient balance"
// Read without spending anything.
final balance = evm.staticCall(to: token, data: balanceOfCalldata);
// Persist the chain, then pick it back up later.
final snapshot = evm.state.encode();
final resumed = Evm(state: WorldState.decode(snapshot));
}
dart run example/main.dart walks the whole loop end to end: fund, deploy, send,
inspect, trace, persist, restore.
Tracing #
final result = evm.call(from: alice, to: contract, tracer: (entry) => print(entry));
for (final step in result.trace) {
print('${step.pc}: ${step.opcode?.name} gas=${step.gasLeft} ${step.stack}');
}
Standalone bytecode #
final code = (Assembler()
..push1(1)
..push1(2)
..add()
..push1(0)
..emit(Opcode.MSTORE)
..push1(32)
..push1(0)
..ret())
.assemble();
final result = const Interpreter().run(code: code, gasLimit: 100000);
print(bytesToBigInt(result.returnData)); // 3
Scope and limitations #
This is an execution engine for local development and testing, not a consensus client.
- No state trie. Accounts live in a map; there are no state or receipt roots, so
block hashes cannot be verified against a real chain.
BLOCKHASHreturns a deterministic pseudo-hash for the last 256 blocks. - Precompiles are partial.
sha256(0x02),identity(0x04) andmodexp(0x05) are implemented.ecrecover,ripemd160, the bn256 operations,blake2fand point evaluation report an explicit "not implemented" failure rather than returning zeroes that would look like a valid answer. - Gas is close, not certified. The schedule follows the Shanghai/Cancun EIPs, but
it has not been validated against the
ethereum/testsGeneralStateTestsvectors. Treat estimates as accurate enough to develop against, not to price a mainnet transaction. - No transaction pool, signatures, or blocks.
Evmapplies transactions directly; signing and RPC live insol_web3.
Dependencies #
sol_support— keccak256 and compiler foundationscrypto— SHA-256, for the 0x02 precompile