Base

Notes on base


Starting from scratch

What is it?

  • It is a programming language created on 2014 that thourgh high-level language, is then compiled to byte code that can be interpreted by the EVM.
  • In solidty, every action the program executes costs gas, so it every innnefiency on the code, costs users gas, which makes apps more costly, thus less appealing.
  • In EVM it is cheaper to compute data than to store it.
  • One must be wary of the gas costs of a program, as some libraries try to calculate agas limit on predictable paths but can fail. However, the gas is still paid.
  • Programs have just 24KiB of size limit, which is why it is important to keep the code size small (this is approximately 300-500 lines of code). However, contracts can expose their functions to other contracts (although it has additional gas costs). although there are much more complex patterns to consider like EIP-2535 (Diamond Proxy)here
  • EVM stack can hold up to 1024 values but just access the top 16 -> EVM limits functions to a total of 16 variables that are input, output or initialized by the function. Otherwise, we might se the Stack too deep error.
  • SPDX-License-Identifier on the top of the contract file indicates the license of the code. Solve problems with copyright

Function visibility

  • Public: Internal and externally
  • External: Just externally visiable
  • Internal: Only internally visible
  • Private: Only for the currect contract

Function modifiers

  • Pure: No state changes or accesses
  • view: No state changes but can read
  • payable: Allow to receive Ethereum
  • virtual: Allows function behaviour to be overriden in derived contracts
  • override: States thta state variable changes the bahavior of function or modifier in base contract

Variable specifiers

  • constant: Dissallows modification (except initialization) and does not occupy storage slot
  • immutable: Assignment at construction time and stored in code.

Event specifiers

  • anonymous: Does not store event signature as topic
  • indexed: Store paramenter as topic

Basic Types

In Solidity, types must always have value and can never be undefined, null or none, Each type always has a default value. If a variable is defined without a value, it will be set to its default value.

uint defaultValue;
uint explicitValue = 0;

// (defaultValue == explicitValue) <-- true

Values can be casted from types but there is no Overflow/Underflow protection

uint256 first = 256;
int8 second = int8(int256(first)); // <- The value stored in second is 0
  • Boolean: true or false, not truthy nor falsy concept exists. non-boolean values cannot be casted to booleans by design. Logical operators apply to booleans and can be used for short circuit and gas asvings

  • Numbers: There are types for signed and unsigned integers. These should not be ignopred as much as could mean potential gas savings when storing smaller numbers. Floating point numbers are not supported To know max and min values for each type we can run type(<type>).min / type(<type>).max. This is important because and overflow or underflow will cause the tx to be reverted.

It is a common practice in Solidity to use uint over int when the value is known to be non-negative. This is because uint is more gas efficient than int. Uint can be used for non-negative values like array indexes or account balances and int for valeus taht need to be negative.

  • Addresses: Represent a unique type for a wallet or contract address, 20-byte. We can also find address payable, that allows to use transfer and send methods. This distinction is important bc it prevents us from sending tokens to a contract not designed to receive them. As well, addresses contain a number of functions like balance or transfer (only payable)

  • Byte arrays: Can be fixed or dynamic size holding a sequence of butes. Strings in solidity are arrays, not strings themselves so they cannot be concatenated using + but rather string.concat. Casting to string can be a bit tricky

  • Enums: Alow to have human readable labels to a list of unsigned intergers. They are limited to 256 elements.

enum Flavors { Vanilla, Chocolate, Strawberry, Coffee }

Flavors chosenFlavor = Flavors.Coffee;
  • Constants: declared at file or contract level. At compilation, the compiler replaces every instance of the constant by its value

  • Inmutable: Used to declare varaibles set once within the constructor and never changed

Control Structures

  • Supports if, else, else if
  • supports while, for and do as well as continue to skip the loop and start the next iteration, break to terminate execution and return to exit the function and return the value.

Error handling

Use revert to reverse exectuion. It must be paired with a custom error. Should be used for logic valid operations that do not match the business logic.

error OddNumberSubmitted(uint _first, uint _second);
function onlyAddEvenNumbers(uint _first, uint _second) public pure returns (uint) {
    if(_first % 2 != 0 || _second % 2 != 0) {
        revert OddNumberSubmitted(_first, _second);
    }
    return _first + _second;
}

When triggered, the error provides the values on the paremeters provided. Useful for debugging.

We can also find require that is almost deprecated, it takes a condition and reverts with an string if not met.

As well there exists assert that is used to check for invariants and reverts if not met. It is a bit more expensive than require but it is more flexible as it can be used for any condition. Mostly used for terning internal errors that should never be triggered by normal operations.

In Solidity also, smaller to bigger sizes uint8 to uint256 are supported for gas savings

Storage in Solidity

  • Smart contract data storage is done through a key-value data store model, each piece of data is identified by an unique key and accompanied by a value. Data is stored in 32-byte chunks for storage optimization and efficient data location calculation.

There are three types of storage in Solidity:

  • Storage: Persisten, thus expensive. The data stored persists across executions and is accesible to any function in the contract. It is visiable in the blockchain and ca be read by external sources.
  • Memory: Temporary and more affordable. Used to save data during single transaction execution. Once executed, it is wiped clean. This memory is not visible for any external source.
  • Stack: It is another form of memory data storage. Used for function argument, local variables and intermediate values. It follows a LIFO structure. It is mostly used by the EVM for computation during transaction execution. Devs cannot directly interact with the stack but can optimize the code to minimize the amount of gas used during tx execution. It has a limited size of 1024 values but only the top 16 values are accesible at any given point in time.

Variable storage

Variable Packing

Storage footprint of a contract can substantially reduce gas costs so, to make storage even more efficient, ETH employs variable packing

It is the process of packing smaller variablesi nto a single storage slot, thus optimizing storage usage. A storage slotis a fixed-sized container that can contain up to 32 bytes of data. The compiler automatically packs smaller variables together to save storage space.

As well, when declaring variables, the order impact on the contract's gas usage due to this optimization. Defining together variables that have similar sizes together can optimize such storage

Keywords like storage memory or calldata are required when declaring a new reference type variable. This determines the data location and how long it will be persisted.

storage is used to assign state variables that will be part of the public storage of the contract, they will remain for the lifetime of the contract. It is expensive and requires a lot of gas to initialize although not that much to update.

memory used create temporary variables that only exists in the scope they are created. calldata is where function arguments are stored. It is non-modificable and shoyld be used where possible to avoid unnecesary copies as it can't be modified.

Arrays in Solidity

These behave differentky based on their location.

  • Assignments between storage and memory or from calldata create an independent copy.
  • memory to memory just creates a reference
  • storage to local storage creates a reference
  • Assignments to storage always create a copy.
  • Variables created at class level are always storage by default.
Storage Arrays
  • Cannot be used as function paramenters

Links

Base EVM Architecture