Advance EVM - Opcodes, low-level calls and instructions

posted Originally published at dly.to 2 min read

EVM compatible, opcodes and calling

  • The EVM basically represents all the instructions a computer needs to be able to read.
  • Any language that can compile down to bytecode with these opcodes is considered EVM compatible'
  1. Data in Transactions:
    • When we send a transaction, it is compiled down to bytecode
    • The EVM processes this data to determine which function to call and what inputs to provide.
  1. Bytecode:
    • When a contract is deployed, it is compiled into bytecode understood by the EVM.
    • This bytecode represents exactly the low level instructions to make our contract happen.
    • This bytecode consist of opcodes
  1. Opcodes:
    • Each opcode is a 2-character hexadecimal, represents some special instruction
    • This opcode reader is sometimes abstractly called the EVM
  1. Encoding Data:
    • Now, ABI encoding will convert data into bytes
    • Ex: abi.encodePacked() || abi.encode()
  1. Decoding data:

    • Decoding is the process of taking the raw bytes and reconstructing the original data
    • Ex: abi.decode()

  2. call

     - How we call functions to change the state variable of the blockchain
     ```js
     function stateChange(address _address) public {
         s_state[_address] = 12;
     }
     ```
    
  1. staticcall
    - This is how (at a low level) we do our "view" or "pure" function calls  
     ```js
     function getState() public view returns(uint256){
         return s_state;
     }
     ```
    

Send TNX that call functions with just data field populated (EVM Signature Selector)

  • In order to call a function using only the data field of call, we need to encode:
    1. function name
    2. parameters we want to add
  • Now each contract assigns each function a function ID/Method ID:

    1. Function selector is the first 4 bytes of the function signature
    2. Function signature a string that defines the function name &
      parameters
      **Ex :  transfer(address,uint256)**
      

Lets assume, we need to call transfer(address,uint256) function but by filling the data field!!!

  1. getFunctionSelector:

    - By this method we will get function selector of the calling function.
    

    `js
    function getSelector() public pure returns (bytes4 selector) {

     selector = bytes4(keccak256(bytes("transfer(address,uint256)")));
    

    }
    `

  1. call Transfer Function with selector:

    • Will use abi.encodeWithSelector(bytes4 selector, args1, args2)

    `js
    // Here, using EVM cheatcodes we can directly call the transfer() function using data-field

    function callTransferData(address _address, uint256 _amount) public returns (bytes4 ,bool) {

     (bool success, bytes memory data) = address(this).call(
         abi.encodeWithSelector(getSelector(), _address,_amount)
     );
     return (bytes4(data), success);
    

    }
    `

  2. call Transfer Function with signature:

    • Will use abi.encodeWithSignature(string functSignature, args1, args2)

    `js
    // Here, we will not use function selector. Rather, we will use function signature to call our transfer() function by populating data field.

    function callTransferDataSig(address _address, uint256 _amount) public returns (bytes4 ,bool) {

     (bool success, bytes memory data) = address(this).call(
         abi.encodeWithSignature("transfer(address,uint256)", _address,_amount)
     );
     return (bytes4(data), success);
    

    }
    `

Note: If there is anything wrong or if you want to share more concepts or if have any doubts. Pls comment below!!!

If you read this far, tweet to the author to show them you care. Tweet a Thanks

Nice breakdown of EVM, opcodes, and function calls — really helpful for grasping how low-level contract interactions work! Just a heads-up though: some of the code snippets aren’t rendering properly, so a quick fix there would make the article even smoother to follow. Also, curious — how would this approach change if calling an external contract instead of address(this)

Thank you for reading the article and for your kind words! Some of the code snippets are simplified examples meant to give readers a general idea. Calling a function using a function signature on an external contract works the same way as when calling it on address(this).

More Posts

What are Price Oracle Manipulation Attacks in Blockchain contracts and EVM???

abiEncode - Jul 5

Private variables are not really private on EVM

abiEncode - Jul 8

Introduction to solidity smart contracts storage layout -- What are risks in manipulating storage???

abiEncode - Jun 30

Blockchain Devops

abiEncode - Jun 29

What is StarkNet and How Does It Differ from zkSync?

Web3Dev - Feb 26
chevron_left