Skip to main content

Required EVM knowledge

info

EVM stands for Ethereum Virtual Machine.

This section quickly describes the required knowledge about the EVM you need to understand to extract information from the blockchain using Bloomr.

It is intended for blockchain beginners, and semi-begginers which know how to use it but dont necessarily know its internals.

Solidity & friends

Most of EVM-compatible blockchain transactions are interacting with smart contracts (more on that later) compiled using the Solidity language.

But the EVM is not Solidity. Solidity is only ONE language that compiles to EVM bytecode, like others (Vyper, Huff, ...)

The EVM is just a virtual machine that executes bytecode, regardless of the language that has produced it.

Parallel with other environments

If you're new to the EVM world but know about Java, then:

  • Solidity is to the EVM what Java is the the JVM.
  • Solidity compiles to EVM bytecode, Java compiles to JVM bytecode.
  • Both the EVM and the JVM are virtual machines made to execute their respective bytecodes.

... but there are plenty other languages that also compile to JVM bytecode, like Kotlin for instance. The same is true for the EVM.

The comparison would be the same with C#, which is ONE language (F# would be another) that compiles to CIL (.net bytecode), executed on CLR (the virtual machine).

The EVM is agnostic (see its opcode reference) on how should smart-contracts inter-operate, but Solidity defined a standard that has been widely adopted.

It mostly boils down to conventions about:

  • The layout of calldata that should be sent when calling another contract
  • The layout of data returned by contracts
  • The layout of logs (also often called events, because named like that in Solidity) data and topics.

...more on those below.

👉 Thus, inspecting what happens on an EVM blockchain is often just interpreting opaque data generated by smartcontracts interactions, in the light of what we know about the Solidity source that these contracts have been compiled from.

Wallet and addresses

When you have an EVM blockchain wallet, you effectively have a private key, only known by you.

From this private key, you can compute as many public keys as you see fit, which are called addresses (which are often presented as a 0x followed by 40 hexadecimal characters).

Your private key is how you prove who you are. Sending a transaction from your wallet means sending some kind of instruction to a smart-contract, by signing it with your private key. Thus, the contract will know who you are, and everyone will see this transactions as originating from your address.

Addresses that are owned by a wallet (and which thus are not smart-contracts) are often refered as EOAs.

Ether, or native tokens

They are the only thing which is related to finance which is intrinsically built in the blockchains.

Each EVM-compatible blockchain has its own native token (Ether for Ethereum, Matic for Polygon, etc), which is used to pay for transactions.

Every address (being an EOA or a contract) can own some Ether, and can send some of it by "calling" another contract or address. (more on that below)

What is a smart-contract

A smart-contract is just EVM bytecode, compiled from whatever language, and deployed by someone using his or her wallet.

Deploying a smart-contract means putting it on chain, somewhere out there, for everyone to see and interact with.

Once deployed, a smart-contract will have an address of its own (which depends on things like who deployed it, and its bytecode).

Contrary to the classical Web2 servers where you own your servers, the fact that you deploy a contract with your wallet does not intrinsically mean that you have some kind of power over that contract: Once deployed, you cannot influence it anymore (unless you explicitely implemented logic in it allowing to do so, but others will see this logic - you wont be able to take them by surprise).

Smart-contracts are forever... They cannot be changed once deployed and you will never be able to update them (they may self-destruct, though, but let's not dive into that).

Each smart-contract owns some kind of key-value pair database which is only accessible by itself, called its state.

Unlike native tokens (Ether, Matic, ...) which are hard-coded in the blockchains, every other token or protocol you might interact with are smart-contracts. For instance, tokens as you might know are just smart-contracts that implement some interface defined by a convention (ERC20, mostly) thus making them "a token". They are just small smart-contracts that keep track of who owns what in their state databases.

Calling smart-contracts

Anyone can "call" a smart-contract by sending a transaction "to it". You can see it as a public micro-service, for which you dont necessarily have the source code, and that does what it does.

Smart contract calls are often refered as transactions.

When you call a smart-contract, you might send it:

  • Some quantity of Ether (i.e. the native token of the blockchain you're on)
  • Some data that it will be able to access (often called calldatas)

In response, it will give you:

  • A response data (often called returndata)
  • A boolean to tell if the call suceeded or failed. Failed calls will revert all state changes performed in it, leaving them as found out before the call.

Like with low level languages such as assembly, the concept of functions do not exist in the EVM. It is a convention that Solidity has introduced. To call a Solidity function on a Solidity smart contract, just send to this contract data which contains:

  • the signature of the function to call on the first 5 bytes
  • the arguments of the function after that, encoded using the ABI specification

When executed, all smart-contracts can themselves decide to call other smart-contracts, which is refered as internal transactions.

To date, there are 4 "modes" when calling a smart-contract:

  • CALL, which is a regular call which can "do things" (i.e. it is not readonly)
  • STATICCALL, which is a read-only call. By performing a staticcall, the caller contract knows that the call performed cannot change anything. This is used to get information about other smartcontracts.
  • DELEGATECALL is used run run another contract's code on the current contract's state (its "database"). Schematically, you might see it as a way for a contract to "trust" another contract by giving him some work to do on his database.
  • CALLCODE, which is similar to delegatecall, but deprecated.

Event logs

When not in a read-only context, smart contracts can use log1, log2, ... instructions to emit logs.

In Solidity, logs are called events: They have no impact on the execution logic, but they are used to leave a trace of what happened to the outside world.

For instance the action of sending some token to someone else will, by convention, emit a Transfer Solidity event log that everyone will be able to see to know who transfered what to who.

Each log/event has:

  • an address, which is the contract address that emitted it (or more precisely: the contract owning the state on which the current operation was happening when the event emission occured - see delegatecall)
  • some topics, which are just 256 bits of information. They are useful because they are indexed on the blockchain, making them the only way to request news on the blockchain through traditional methods. But this indexation is a bit irrelevant in Bloomr, which is much more flexible.
  • some arbitrary data

For instance, emitting the Solidity event (which is an ERC20 token transfer event):

event Transfer(address indexed from, address indexed to, uint256 value);

Will as per Solidity's specifications emit an EVM log with 3 topics, and some data:

  • topic0 will be computed from the event signature
  • topic1 will be the from address
  • topic2 will be the to address
  • data will encode all remaining non indexed data: here, only the value parameter.

Gas

If you already have sent transactions, you know that each transaction costs you Ether.

Every EVM operation costs you some gas, which is not ether. It is just a dimentionless quantity (see the minimum gas column on evm.codes).

This quantity translates to Ether when you send the transaction: You will specify a "gas cost", which is how many Ether you're willing to pay for each gas unit. The more you're willing to pay, the fastest your transaction is likely to be executed.

Why is it often difficult to track smart-contracts ?

When you have to track smart-contracts, the classical approach is either to track events that might interest you, or to track all transactions sent to a contract. Often through RPCs (which are a direct connection to blockchain nodes).

But you might hit intrinsic limits:

  • Tracking a lot of events is tedious, and might end up costing you a lot more that it should.
  • The data accessible through RPCs might be tedious to get, and often requires you deploying useless helpers contracts and costs you a lot of RPC requests.
  • When an event occurs, the information it carries might not be the thing you're interested in.
  • Sometimes, there is just no relevant event to track... in which case you're left with little choice else than just downloading all the transactions, and filtering afterwards.
  • And even when downloading all transactions, internal transactions are not easily accessible through an RPC.
  • People often track direct contract calls, but often forget that those calls might happen in internal transactions, thus making them miss important events.
  • There is no efficent way to track an arbitrary large number of different contracts through RPCs.

TLDR: Tracking the blockchain through an RPC or other streaming services is often tedious, inneficient and leads to massive overfetching and overcosts.

👉 Bloomr solves all that. Period.