Definição de Retornos em Funções
Em Solidity, a definição de valores de retorno ocorre na assinatura da função utilizando a palavra-chave returns, anquanto a entrega efetiva dos dados é feita através do comando return dentro do corpo da função. Existe também a possibilidade de usar retornos nomeados, onde as variáveis são declaradas na assinatura e atribuídas diretamente no corpo, sendo retornadas implicitamente ao final da execução.
Para consumir esses valores, especialmente quando há múltiplos retornos, pode-se utilizar a desestruturação de tuplas, permitindo selecionar apenas os dados relevantes.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
contract OperacoesContract {
int public valorInicial = 5;
// Retorno padrão usando tupla
function calcular(int _entrada) public view returns (int, int) {
return (valorInicial - _entrada, valorInicial * _entrada);
}
// Retorno utilizando variáveis nomeadas
function calcularNomeado(int _entrada) public view returns (int resultadoA, int resultadoB) {
resultadoA = valorInicial - _entrada;
resultadoB = valorInicial * _entrada;
// Retorna implicitamente resultadoA e resultadoB
}
// Captura de retornos via desestruturação
function obterResultados() public view {
int _val1;
int _val2;
// Captura todos os valores
(_val1, _val2) = calcular(2);
// Captura apenas o primeiro valor, ignorando o segundo
(_val1, ) = calcular(3);
}
}
Locais de Armazenamento e Custos de Gas
A especificação do local de dados é obrigatória para tipos complexos como arrays, structs e mappings. A escolha entre storage, memory e calldata impacta diretamente o custo de execução (gas) e a persistência dos dados.
| Local | Características |
|---|---|
| storage | Persistente na blockchain. Custo de gas elevado. |
| memory | Temporário, existe apanas durante a execução. Custo de gas menor. |
| caldata | Temporário e não modificável. Usado geralmente em parâmetros externos. Custo mínimo. |
Por padrão, variáveis de estado utilizam storage. Parâmetros de funções e retornos usam calldata (para externos) ou memory. Variáveis locais dentro de funções são memory por padrão.
O
calldatanão é armazenado na memória convencional do contrato. É uma área de dados especial, somente leitura, contendo os argumentos da chamada externa. Ela não persiste após a execução da função e não altera o estado do contrato.
A atribuição de tipos de referência cria um ponteiro para o dados originais. Modificar o ponteiro afeta o original se estiver em storage, mas não se for uma cópia em memory.
contract TesteArmazenamento {
uint256[] public vetorEstado = [10, 20, 30];
// Modifica o estado permanente pois usa referência storage
function alterarPersistente() public {
uint256[] storage refArmazenamento = vetorEstado;
refArmazenamento[0] = 500;
}
// Cria uma cópia em memória, o estado permanente não é afetado
function alterarTemporario() public view {
uint256[] memory refMemoria = vetorEstado;
refMemoria[0] = 500;
}
// Demonstra referência em memória: ambas variáveis apontam para o mesmo local
function verificarReferencia() public view returns (uint256) {
uint256[] memory copiaA = vetorEstado;
uint256[] memory copiaB = copiaA;
copiaB[0] = 500;
return copiaA[0]; // Retornará 500 devido à referência compartilhada
}
}
Escopo de Variáveis e Globais
As variáveis em Solidity são classificadas conforme sua visibilidade e local de armazenamento:
- Variáveis de Estado: Persistem na chain, acessíveis por todas as funções, alto custo de gas.
- Variáveis Locais: Existem apenas durante a execução da função, armazenadas em memória, baixo custo.
- Variáveis Globais: Palavras-chave reservadas fornecidas pelo ambiente de execução.
Exemplos comuns de variáveis globais incluem msg.sender (endereço do chamador), msg.data (payload da chamada) e block.number (número do bloco atual).
Denominações de Ether
Para facilitar a leitura e escrita de valores monetários, a Solidity oferece sufixos que representam múltiplos de Wei, a menor unidade da rede Ethereum.
wei: Unidade base (1).gwei: Equivale a 10^9 wei.ether: Equivale a 10^18 wei (1 ETH).
contract ValidadorMoeda {
function confirmarValores() public pure returns (bool) {
bool checkWei = 1 wei == 1;
bool checkGwei = 1 gwei == 1e9;
bool checkEther = 1 ether == 1e18;
return checkWei && checkGwei && checkEther;
}
}
Unidades de Tempo
Contratos inteligentes frequentemente necessitam de lógica baseada em tempo, como prazos de execução ou validação de janelas temporais. A linguagem fornece modificadores literais para representar segundos, minutos, horas, dias e semanas.
pragma solidity ^0.8.0;
contract TarefaComPrazo {
uint public limiteTempo;
constructor() {
// Define o prazo para 2 dias a partir do momento da implantação
limiteTempo = block.timestamp + 2 days;
}
function realizarTarefa() public {
require(block.timestamp <= limiteTempo, "O prazo para esta ação expirou");
// Lógica da operação vai aqui
}
}
No exemplo acima, o construtor calcula um timestamp futuro somando unidades de tempo ao timestamp atual. A função de execução valida se o tempo corrente ainda está dentro do limite permitido antes de prosseguir.
As unidades de tempo disponíveis são convertidas internamente para segundos:
seconds: 1minutes: 60hours: 3600days: 86400weeks: 604800