Você pode ter encontrado algumas dicas do Solidity para melhorar suas habilidades de código a fim de economizar algum gás, mas hoje quero me concentrar mais em como a compreensão da Máquina Virtual Ethereum pode efetivamente economizar custos de gás em seus contratos inteligentes.
Já que vamos mergulhar no Ethereum, vou deixar aqui o trecho do seu Yellow Paper que especifica os custos do gás dos opcodes, e durante o artigo estaremos nos referindo a eles.
Dica nº 1: acesso frio VS acesso quente
Gcoldsload: 2100 gás
Gwarmaccess: 100 gás
Aí temos nossos primeiros OPCODES, o primeiro especifica quanto custa acessar uma variável pela primeira vez (ou acesso frio) enquanto o segundo especifica quanto custa acessar a variável uma segunda vez e mais (acesso quente ). Como você pode ver a diferença de preço é muito grande, então entender isso pode fazer uma grande diferença nos custos das transações do seu contrato inteligente. Vejamos um exemplo.
Armazenar os dados em cache dentro de uma função no Solidity pode resultar em menor uso de gás, mesmo que precise de mais linhas de código. Nesse caso, é trocando a localização do array e, em vez de usá-lo no armazenamento e, portanto, acessá-lo a frio todas as vezes no loop, ele armazena o array na memória, onde é mais barato acessá-lo.
Dica nº 2: valores zero versus diferentes de zero e reembolsos de gás
Gsset = 20.000 gás
Rsclear = {desconto no preço de execução}
Alterar um valor de 0 para diferente de zero no blockchain Ethereum é caro, como vemos no preço do Gsset, mas alterar um valor de diferente de zero para 0 pode gerar um reembolso no valor do gás conforme o opcode Rsclear. Para não usufruir do reembolso, fica estabelecido que você só poderá ser reembolsado em até no máximo 20% do custo total da transação.
Você pode encontrar tal cenário em um cenário muito comum no blockchain, que é a atualização do saldo de endereços em contratos inteligentes. Vejamos um exemplo de cada:
No primeiro exemplo de contrato ZeroToNonZero, diferente de zero a diferente de zero (5.000 gás*) + zero a diferente de zero (20.000 gás) = 25.000 gás
No segundo exemplo de contrato NonZeroToZero, Diferente de zero a zero (5.000 gás*) + zero a diferente de zero (20.000 gás) — Reembolso (4.800 gás) = 21.200 gás
*2.100 (Gcolssload) + 2.900 (Gsreset) = 5.000 gás
Dica nº 3: a ordem das variáveis de estado é importante
O armazenamento é como uma estrutura de dados de valor-chave que contém os valores das variáveis de estado de um contrato inteligente do Solidity.
Você pode pensar no armazenamento como uma matriz que ajudará a visualizar isso. Cada espaço neste “array” de armazenamento é chamado de slot e contém 32 bytes (256 bits) de dados e cada variável de estado declarada no contrato inteligente ocupará um slot dependendo de sua posição de declaração e seu tipo.
Nem todos os tipos de dados ocupam todos os 32 bytes de cada slot, pois existem alguns tipos de dados (bool, uint8, endereço…) que ocupam menos que isso.
O truque aqui é que se duas/três ou mais variáveis juntas tiverem 32 bytes ou menos, o compilador do solidity tentará agrupá-las em um único slot, mas essas variáveis precisam ser definidas uma ao lado da outra.
Aqui estamos usando os tipos de dados bool (1 byte), endereço (20 bytes) e uint256 (32 bytes). Então, conhecendo o tamanho dessas variáveis você pode entender facilmente que no primeiro exemplo do contrato TwoSlots como temos bool e endereço juntos (1 + 20 = 21 bytes, que é menor que 32 bytes) elas ocuparão um slot. No contrato ThreeSlots, como bool e uint256 não podem estar no mesmo slot (1 + 32 = 33 bytes, que é maior que a capacidade do slot), no total usaremos três slots.
Agora, por que isso é tão importante?
O código de operação SLOAD custa 2.100 gás e é usado para ler slots de armazenamento. Portanto, se você puder armazenar as variáveis em menos slots, acabará economizando algum gás.
Dica nº 4: uint256 é mais barato que uint8
Aprendemos na dica nº 3 que uint256 (256 bits = 32 bytes) ocupa sozinho um slot e aprendemos também que uint8 tem menos de 32 bytes. Então, embora seja bastante claro que 8 bits são menores que 256 bits, por que uint256 é mais barato?
Para entender isso é importante saber que se uma variável não preencher sozinha todo o slot e se esse slot não for preenchido por nenhuma outra variável, o EVM irá preencher o restante dos bits restantes com “0”s para poder manipulá-lo.
Essa adição de “0” realizada pelo EVM custará gás, o que significa que para economizar gás de transação, é melhor usar uint256 em vez de uint8.
__________________
Esperançosamente, ao descobrir essas dicas para reduzir os custos do gás em seus contratos inteligentes, você também aprendeu um pouco como funciona o EVM.
__________________
Twitter @TheBlockChainer para encontrar mais atualizações diárias sobre contratos inteligentes, segurança Web3, solidez, auditoria de contratos inteligentes e muito mais.
__________________