Pular para o conteúdo
0%

Como ler e escrever variáveis de storage compactadas em Yul

Mark Jonathas headshot

Escrito por Mark Jonathas

Publicado em 1 de agosto de 20233 min de leitura

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

Como ler e escrever variáveis de storage empacotadas em yul

Suponha que você queira mudar var5 para 4. Sabemos que var5 está localizada no slot 3, então você pode tentar algo assim:

Usando getValInHex(3) vemos que o slot 3 foi reescrito para:

0x0000000000000000000000000000000000000000000000000000000000000004

Isso é um problema porque agora var4 foi reescrita para 0. Nesta seção vamos ver como ler e escrever variáveis empacotadas, mas primeiro precisamos aprender um pouco mais sobre a sintaxe do Yul.

Se você não estiver familiarizado com essas operações não se preocupe, vamos ver cada uma com exemplos.

Vamos começar com and(). Vamos pegar dois bytes32 e testar o operador and() para ver o que ele retorna.

Se você olhar a saída, vemos:

0x0000000000000000000000009acc1d6aa9b846083e8a497a661853aae07f0f00

O motivo disso é que o and() olha cada bit de ambas as entradas e compara seus valores.

Se ambos os bits forem 1 (pense em termos de binário: ativo ou inativo), mantemos o bit como está.

Caso contrário, ele é definido como 0.

Agora veja o código do or().

Desta vez a saída é:

0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

Isso acontece porque ele verifica se algum dos dois bits está ativo.

Vamos ver o que acontece se mudarmos a variável mask para:

0x00ffffffffffffffffffffff0000000000000000000000000000000000000000

Como você pode ver, a saída muda para:

0x00ffffffffffffffffffffff9acc1d6aa9b846083e8a497a661853aae07f0f00

Note que o primeiro byte é 0x00, porque nenhuma das entradas tem bits ativos no primeiro byte.

xor() é um pouco diferente. Ele exige que um bit esteja ativo (1) e o outro bit esteja inativo (0).

Aqui está uma demonstração em código:

A saída é:

0xffffffffffffffffffffffff0000000000000000000000000000000000000000

A diferença principal fica evidente quando vemos que os únicos bits ativos na saída são onde 0x00 e 0xff estão alinhados.

shl() e shr() funcionam de forma bem parecida entre si. Ambos deslocam o valor de entrada por uma quantidade de bits informada. shl() desloca para a esquerda e shr() desloca para a direita.

Vamos ver um pouco de código!

Saída:

ans1: 0x0000ffff00000000000000000000000000000000000000000000000000000000

ans2: 0x00000000000000000000000000000000000000000000000000000000ffff0000

Vamos começar olhando ans1. Fazemos shr() por 16 bits (2 bytes). Como você pode ver, os últimos dois bytes mudam de 0xffff para 0x0000, e os primeiros dois bytes são deslocados dois bytes para a direita. Sabendo disso, ans2 parece autoexplicativo; tudo que acontece é que os bits são deslocados para a esquerda em vez de para a direita.

Antes de escrever em var5, vamos escrever uma função que lê var4 e var5 primeiro.

A saída é 1 e 2, como esperado.

Para obter var4 precisamos apenas usar uma mask para definir o valor como:

0x0000000000000000000000000000000000000000000000000000000000000001

Então retornamos um uint128 igual a 1.

Ao ler var5, precisamos deslocar var4 para fora, fazendo um shift para a direita.

Isso nos deixa com o seguinte, que podemos retornar:

0x0000000000000000000000000000000000000000000000000000000000000002

É importante notar que às vezes você terá que fazer shift e mask em conjunto para ler um valor que tenha mais de 2 variáveis empacotadas em um slot de storage.

Ok, finalmente estamos prontos para mudar o valor de var5 para 4!

O primeiro passo é carregar o slot de storage 3.

Em seguida, precisamos criar uma mask.

De forma semelhante a quando lemos var4, queremos isolar o valor em:

0x0000000000000000000000000000000000000000000000000000000000000001

O próximo passo é formatar nosso novo valor para que fique na posição do slot de var5, assim:

0x0000000000000000000000000000000400000000000000000000000000000000

Diferente de quando lemos var5, desta vez vamos deslocar nosso valor para a esquerda.

Por fim, vamos usar or() para combinar nossos valores em 32 bytes de hexadecimal, e armazenar esse valor no slot 3.

Podemos conferir nosso trabalho chamando getValInHex(3).

Isso vai retornar isto, , que é o que esperamos ver:

0x0000000000000000000000000000000400000000000000000000000000000001

Ótimo, agora você sabe como ler e escrever em slots de storage empacotados! Em seguida, aprenda como Memory, Storage e Smart Contract Calls funcionam em Yul.

Background gradient

Construa magia blockchain

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