Pular para o conteúdo
0%

Como a memória funciona em Yul?

Mark Jonathas headshot

Escrito por Mark Jonathas

Publicado em 28 de julho de 20235 min de leitura

Yul é uma linguagem de programação intermediária que pode ser usada para escrever uma forma de linguagem assembly dentro de contratos inteligentes. Depois de aprender sobre o storage do Yul e como ler e escrever variáveis de storage compactadas (packed), é importante aprender como a memória funciona em smart contracts com Yul.

Como a memória funciona em yul?

A memória se comporta de forma diferente do storage. A memória não é persistente, o que significa que, assim que a função termina de executar, todas as variáveis são limpas.

A memória é comparável ao heap em outras linguagens, mas não há garbage collector.

A memória é bem mais barata que o storage. O custo das primeiras 22 words de memória é calculado de forma linear, mas cuidado, pois depois disso o custo de memória se torna quadrático.

A memória é organizada em sequências de 32 bytes. Vamos entender melhor isso mais adiante, mas por enquanto entenda que 0x00 - 0x20 é uma sequência (pode pensar nisso como um slot, se ajudar, mas são coisas diferentes).

O Solidity reserva 0x00 - 0x40 como scratch space. Não há garantia de que essa área de memória esteja vazia, e ela é usada em certas operações.

0x40 - 0x60 armazena a localização do chamado free memory pointer, usado para escrever algo novo na memória.

0x60 - 0x80 é deixado vazio como um espaço (gap).

0x80 é onde começamos nossas operações.

A memória não compacta valores. Valores recuperados do storage são armazenados em sua própria sequência de 32 bytes (ex: 0x80-0xa0).

Quais operações usam memória em yul?

A memória é usada nas seguintes operações:

  • Valores de retorno para chamadas externas
  • Definir valores de função para chamadas externas
  • Obter valores de chamadas externas
  • Reverter com uma mensagem de erro
  • Registrar mensagens (log)
  • Fazer hash com keccak256()
  • Criar outros smart contracts

Aqui estão algumas instruções úteis de Yul para memória:

Instruction
Explanation

mload(p)

Similar to sload(), but we are saying load the next 32 bytes after p

mstore(p, v)

Similar to sstore(), but we are saying store value v in p plus 32 bytes

mstore8(p, v)

Similar to mstore(), but only for a single byte

msize()

Returns the largest accessed memory index

pop(x)

Discard value x

return(p, s)

End execution, and return data from memory locations p - v

revert(p, s)

End execution without saving state changes, and return data from memory locations p - v

Vamos ver mais algumas data structures!

Como usar structs e memória em yul

Structs e arrays fixos se comportam da mesma forma, mas como já vimos arrays fixos na seção de storage, vamos ver structs aqui. Observe a seguinte struct.

Nada de incomum aqui, apenas uma struct simples.

Agora vamos ver algum código!

Aqui estamos definindo s.subVar1 na localização de memória 0x80 - 0xa0 e s.subVar2 na localização de memória 0xa0 - 0xc0. É por isso que estamos retornando 0x80 - 0xc0. Aqui está uma tabela do layout de memória logo antes do fim da transação.

Memory Location
Value Stored

0x00

Scratch Space (Empty)

0x20

Scratch Space (Empty)

0x40

0xc0 (Free Memory Pointer)

0x60

Empty

0x80

s.subVar1: 0x20 (32 in decimal)

0xa0

s.subVar1: 0x40 (64 in decimal)

0xc0

New Free Memory Pointer. This is what msize() would return. (Empty)

O que aprender com isso:

  • 0x00 - 0x40 fica vazio para scratch space
  • 0x40 nos dá o free memory pointer
  • O Solidity deixa um gap para 0x60
  • 0x80 e 0xa0 são usados para armazenar os valores da struct
  • 0xc0 é o novo free memory pointer

Nesta última parte da seção de memória, quero mostrar como arrays dinâmicos funcionam em memória. Vamos passar [0, 1, 2, 3] como o parâmetro arr para este exemplo. Como um bônus extra para este exemplo, vamos adicionar um elemento a mais ao array. Tenha cuidado ao fazer isso em produção, pois você pode sobrescrever uma variável de memória diferente.

Aqui está o código:

Aqui está o que está acontecendo:

  • Obter onde o array está armazenado na memória
  • Obter o length do array, que é armazenado na primeira localização de memória do array
  • Adicionar 32 bytes à localização (pular o length do array) para ver a próxima localização disponível
  • Multiplicar o length do array por 32 bytes para avançar para a próxima localização de memória
  • Armazenar nosso novo valor (4)
  • Atualizar o length do array em um
  • Atualizar o free memory pointer
  • Retornar o array

Vamos ver o layout de memória mais uma vez.

Memory Location
Value Stored

0x00

Scratch Space (Empty)

0x20

Scratch Space (Empty)

0x40

0x140 (Free Memory Pointer)

0x60

Empty

0x80

New length of the array (6)

0xa0

arr[0] (0)

0xc0

arr[1] (1)

0xe0

arr[2] (2)

0x100

arr[3] (3)

0x120

arr[4] (4)

0x140

Free Memory Pointer (Empty)

Isso conclui a seção sobre memória!

A seguir, aprenda como chamar smart contracts usando Yul ou volte para aprender a ler e escrever variáveis de storage compactadas!

Background gradient

Construa magia blockchain

A Alchemy combina os produtos e ferramentas de desenvolvimento Web3 mais poderosos com recursos, comunidade e suporte lendário.