Transferências de Ether

Solidity oferece várias formas de enviar Ether de um contrato para um endereço externo, cada uma com suas próprias implicações em termos de segurança, gás e comportamento em caso de falha. Essas formas são transfer, send e chamadas de baixo nível (call).

Transfer

O método transfer é uma forma segura de enviar Ether, pois reverte automaticamente toda a transação se a transferência falhar por qualquer motivo. É a maneira recomendada de enviar Ether quando se deseja que toda a transação falhe caso a transferência de Ether não possa ser concluída.

address payable receiver = payable(0x123...);

receiver.transfer(amount);

Se a transferência falhar, a execução é interrompida e revertida.

Send

O método send é semelhante ao transfer, mas em vez de reverter automaticamente, retorna um valor booleano (true ou false) indicando o sucesso ou falha da operação. Isso permite que o contrato lide com a falha de transferência de uma maneira mais flexível.

address payable receiver = payable(0x123...);

bool sent = receiver.send(amount);

if (!sent) {
// Lidar com a falha
}

O send é menos utilizado devido à necessidade de lidar manualmente com o caso de falha, mas pode ser útil quando se deseja uma lógica específica para tratar erros.

Call

O método call é ainda mais flexível e é recomendado nas versões mais recentes do Solidity para enviar Ether. call retorna um valor booleano indicando sucesso ou falha e permite especificar dados adicionais para a chamada, tornando-o compatível com a execução de funções no contrato receptor de Ether. No entanto, essa flexibilidade traz a responsabilidade de lidar corretamente com a segurança.

(address payable receiver).call{value: amount}("");

É importante tomar precauções de segurança ao usar call para transferir Ether, especialmente para evitar reentrância, um tipo de vulnerabilidade em que um atacante pode forçar o contrato a executar certas funções de forma recursiva.

Considerações de Segurança

  • Prevenção de Reentrância: Ao transferir Ether, especialmente usando call, é crucial proteger o contrato contra ataques de reentrância. Padrões como o de checagem-efeitos-interação e o uso de modificadores de reentrância podem ajudar a mitigar esse risco.

  • Verificar o Sucesso da Transferência: Sempre verifique o resultado de uma transferência de Ether e trate adequadamente o caso de falha, especialmente ao usar send e call.

  • Gás para Chamadas Externas: Ao enviar Ether, é fornecida uma quantidade fixa de gás (2300 de gás ao usar transfer ou send), o que é suficiente para eventos de log, mas não para executar código no contrato receptor. Com call, é possível especificar uma quantidade maior de gás, mas isso deve ser feito com cuidado.

Exemplo

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract ReceiveEther {
    

    // Função para receber Ether. msg.data deve estar vazio.
    receive() external payable {}

    // Função Fallback é chamada quando msg.data não é vazio.
    fallback() external payable {}

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

contract SendEther {
    function sendViaTransfer(address payable _to) public payable {
        // Essa função não é mais recomendada para enviar Ether.
        _to.transfer(msg.value);
    }

    function sendViaSend(address payable _to) public payable {
        // Função retorna uma booleana indicando successo ou falha.
        // Essa função não é mais recomendada para enviar Ether.
        bool sent = _to.send(msg.value);
        require(sent, "Failed to send Ether");
    }

    function sendViaCall(address payable _to) public payable {
        // Função retorna uma booleana indicando successo ou falha.
        // Esse é o método recomendado para enviar Ether.
        (bool sent, bytes memory data) = _to.call{value: msg.value}("");
        require(sent, "Failed to send Ether");
    }
}

Last updated