A rede Ethereum permite transferências entre diferentes tipos de contas, incluindo dois usuários, dois contratos, um contrato e um usuário, ou um usuário e um contrato. Neste artigo, exploraremos em detalhes o cenário de contrato para contrato e examinaremos os diferentes métodos disponíveis em Solidity para transferir valor entre eles.
Transferência Ethereum de uma conta para outra. freepik.com
Transferir valor na rede Ethereum requer uma cuidadosa consideração e vários passos importantes. Este guia abrange tudo o que você precisa saber sobre a transferência de ether (ETH), a criptomoeda nativa da rede, entre contas usando a linguagem de programação Solidity.
Ao iniciar uma transferência, uma transação é criada e enviada para a rede com informações, incluindo os endereços do remetente e do destinatário, a quantidade de ether a ser transferida, o limite e o preço do gás. Em Solidity, wei é a unidade de ether usada para transferências, com 1018 wei equivalente a 1 ether.
Após a transação ser transmitida para a rede, ela é validada e executada pelos nós, com quaisquer contratos inteligentes associados sendo executados. Uma vez validada, a transação é adicionada ao pool de transações da rede, aguardando para ser minerada por um minerador.
Nota do tradutor: Desde o “The Merge” da Ethereum (15/09/22), as transações da rede não são mais mineradas por mineradores (Mecanismo de Consenso de Prova de Trabalho (PoW)), e sim validadas por validadores (Mecanismo de Consenso de Prova de Participação (PoS)).
Quando um bloco é minerado com sucesso pelo minerador, a transação é executada, e o ether é transferido da conta do remetente para a conta do destinatário. O minerador recebe a taxa de gás associada à transação como recompensa por incluí-la no bloco.
Em resumo, transferir ether na rede Ethereum envolve criar e enviar uma transação, validá-la e executá-la, e pagar uma taxa de gás para os mineradores processarem a transação. É importante considerar todos os fatores envolvidos na transferência, como preços e limites de gás, para garantir uma transferência bem-sucedida e econômica entre as contas desejadas.
Para saber mais sobre enviar e receber ether com o Solidity, visite a documentação oficial do Solidity para obter informações detalhadas.
Transferir valor entre dois contratos inteligentes:
Transferir valor entre contratos inteligentes é uma tarefa comum no Solidity. No entanto, é importante estar ciente dos vários métodos disponíveis e dos possíveis riscos de segurança envolvidos para escrever código seguro e protegido.
Neste artigo, veremos como transferir valor entre dois contratos inteligentes usando o Solidity, incluindo exemplos de uso de transfer
, send
e call
. Também discutiremos as melhores práticas para lidar com erros e gerenciamento de segurança para ajudar a prevenir vulnerabilidades, como ataques DDoS e de reentrância.
Os contratos de amostra:
Para demonstrar a transferência de valor entre dois contratos inteligentes, usaremos dois contratos simples: Sender
(remetente) e Recipient
(destinatário).
Transfer
(2300 de gás, lança um erro):
O contrato Sender
terá uma função que permite transferir ether para o contrato Recipient
. No contrato Sender
, a função transferToRecipient
recebe dois parâmetros: o endereço payable
do contrato Recipient e a quantidade de ether a ser transferida.
O método transfer
é usado para transferir ether do contrato Sender para o contrato Recipient. Se a transferência falhar por qualquer motivo, incluindo a não existência do endereço do destinatário, um erro é lançado e a transferência de ether não será executada, e quaisquer mudanças de estado que foram feitas antes da exceção ser lançada serão revertidas. Melhor ainda, um limite de gás de 2300 é colocado no método de transferência, o que não apenas garante que a operação de transferência seja concluída dentro do limite de gás, mas também atua como uma proteção contra ataques de reentrância.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Contrato do remetente para enviar ether
contract Sender {
function transferToRecipient(address payable recipientAddress, uint amount) public {
require(balance >= amount, "Insufficient balance.");
// Transferir ether para o contrato do Destinatário usando o método transfer
recipientAddress.transfer(amount);
}
}
Payable: funções e endereços declarados
payable
podem receberether
no contrato.
O contrato Recipient
terá a funcionalidade necessária para receber Ether e uma função que permite que ele retire o ether transferido.
pragma solidity ^0.8.17;
// Contrato de destinatário para receber ether
contract Recipient {
//--- Eventos opcionais para auxiliar nossos esforços de depuração.
event ReceivedWithData(address sender, uint256 amount, bytes data);
event ReceivedWithoutData(address sender, uint256 amount);
address payable public owner; // Defina o proprietário como o criador do contrato
constructor() {
owner = payable(msg.sender);
} // Essa função é marcada como payable (pagável) para que o contrato possa receber ether.
// Também é chamado de "receber" porque esta é a função que é chamada quando o ether é enviado para o contrato sem nenhum dado de função.
receive() external payable {
//--- Evite adicionar qualquer código aqui, mas para fins de depuração, vamos emitir um evento:
emit ReceivedWithoutData(msg.sender, msg.value);
} fallback() external payable {
//--- Evite adicionar qualquer código aqui, mas para fins de depuração, vamos emitir um evento:
emit Received(msg.sender, msg.value, msg.data);
} // Esta função permitirá que o proprietário deste contrato transfira o Ether do contrato para sua própria conta.
function withdraw() external {
// Certifique-se de que apenas o proprietário possa chamar esta função
require(msg.sender == owner, "Only the owner can withdraw.");
// Transfira todo o saldo deste contrato para o proprietário
uint256 balance = address(this).balance;
// Certifique-se de que haja um saldo diferente de zero para transferir
require(0 != balance, "There is no Ether to withdraw.");
owner.transfer(balance);
}
}
Um contrato que recebe Ether deve ter pelo menos uma das funções abaixo:
receive() external payable
fallback() external payable
A função receive()
é chamada se o parâmetro msg.data
estiver vazio, caso contrário, a função fallback()
é chamada.
Certifique-se de que seus fundos não fiquem bloqueados indefinidamente em seu contrato inteligente. coingeek.com
Ao desenvolver contratos inteligentes, é importante incluir a funcionalidade necessária para evitar que os fundos fiquem bloqueados no contrato indefinidamente. No caso do contrato Recipient
, adicionamos uma função de “saque” para permitir que o destinatário acesse e use os fundos transferidos para eles. Isso garante que os fundos não sejam permanentemente inacessíveis e fornece flexibilidade para o destinatário usá-los conforme pretendido.
Quando a função transferToRecipient
é chamada, ela primeiro verifica se o contrato destinatário possui uma função pagável que possa receber o ether. Se o contrato destinatário não tiver nenhuma função payable, a transferência falhará.
Portanto, é essencial garantir que o contrato destinatário tenha uma função pagável, como a função receive
ou fallback
, para receber ether enviado do contrato remetente. Caso contrário, a transferência falhará e a transação será revertida.
send
(2300 de gás, retorna bool):
O mesmo contrato Sender
pode ser reescrito usando o método send
:
pragma solidity ^0.8.17;
contract Sender {
function transferToRecipient(address payable recipient, uint256 amount) public {
require(address(this).balance >= amount, "Insufficient balance");
(bool success, ) = recipient.send(amount);
require(success, "Transfer failed");
}
}
Nesta implementação, a função transferToRecipient
usa o método send
para transferir o ether para o endereço do destinatário. Ao contrário do método transfer
, o método send
retorna um valor booleano indicando se a transferência foi bem-sucedida ou não. O método send
também limita a quantidade de gás que pode ser usada em 2300, semelhante ao método transfer
.
Finalmente, a função verifica se a transferência foi bem-sucedida usando o valor booleano retornado pelo método send
e lança um erro se não foi.
A diferença entre os métodos transfer
e send
é que o método transfer
lança um erro se a transferência falhar, enquanto o método send
retorna um valor booleano indicando se a transferência foi bem-sucedida ou não. Independentemente do valor de retorno, quaisquer alterações de estado que foram feitas antes que o método send
fosse chamado ainda serão salvas na blockchain, mas a transferência de ether não será executada se for retornado false
.
O método send
pode ser usado em situações em que as consequências de uma transferência falhada não são graves e onde é desejável ter mais controle sobre o fluxo da transação. No entanto, o método transfer
é preferido em situações em que o fluxo da transação é mais direto e onde é importante garantir que a transferência ocorra com sucesso ou falhe atomicamente.
call
(encaminha todo o gás ou define o gás, retorna bool):
O mesmo contrato Sender
pode ser reescrito usando o método call
:
pragma solidity ^0.8.17;
contract Sender {
function transferToRecipient(address payable recipient, uint256 amount) public {
require(address(this).balance >= amount, "Insufficient balance");
(bool success, bytes memory data) = recipient.call{value: amount}("");
require(success, "Transfer failed");
}
}
Nesta implementação, a função transferToRecipient
usa o método call
para transferir o ether para o endereço do destinatário. O método call
permite mais personalização da transação, incluindo a especificação de limites de gás e a passagem de argumentos para o contrato destinatário. Neste caso, passamos um array de bytes vazio como o parâmetro de dados para indicar que não estamos passando nenhum argumento para o contrato destinatário.
Assim como o método send
, o método call
retorna um valor booleano indicando se a transação foi bem-sucedida ou não. Além disso, ele retorna um array de bytes contendo quaisquer dados que foram retornados pelo contrato destinatário.
Finalmente, a função verifica se a transferência foi bem-sucedida usando o valor booleano retornado pelo método call
e lança um erro se não foi.
Em comparação com o método send
, o método call
permite mais personalização da transação, incluindo a especificação de limites de gás e a passagem de argumentos para o contrato destinatário. No entanto, também é mais complexo de usar e pode ser mais propenso a erros se não for usado com cuidado. Além disso, o custo de gás de uma transação call
pode ser maior do que o de uma transação send
devido às opções de personalização adicionais disponíveis.
Para especificar limites de gás no método call
, podemos usar a seguinte implementação:
(bool success, bytes memory data) = recipient.call{value: amount, gas: 2300}("");
Nesta implementação, adicionamos um parâmetro gas
ao método call
, especificando um limite de gás de 2300. Este é o mesmo limite de gás imposto no método transfer
da Ethereum.
Ao definir um limite de gás de 2300, estamos limitando a quantidade de gás que pode ser usada pelo contrato destinatário ao receber a transferência de ether. Esta é uma medida de segurança para evitar ataques de reentrância, onde um contrato malicioso poderia tentar chamar repetidamente de volta para o contrato Sender antes que a transferência original tenha sido concluída, potencialmente roubando fundos ou causando outros comportamentos inesperados.
É importante observar que definir um limite de gás de 2300 pode não ser apropriado para todos os contratos, e o limite de gás deve ser cuidadosamente escolhido com base nos requisitos específicos do contrato e nos riscos potenciais envolvidos. Além disso, é sempre importante testar completamente os contratos em busca de possíveis vulnerabilidades de segurança, incluindo ataques de reentrância, antes de implantá-los na rede Ethereum.
Em geral, a escolha entre os métodos send
e call
depende dos requisitos específicos do contrato e do nível de personalização e controle necessários sobre o fluxo da transação.
Obrigado pelo seu interesse neste tópico, se você tiver alguma solicitação específica para meus artigos futuros, sinta-se à vontade para me enviar uma mensagem.
Continue sua aprendizagem visitando links úteis como estes…
https://docs.soliditylang.org/en/v0.8.19/
https://medium.com/coinmonks/solidity-transfer-vs-send-vs-call-function-64c92cfc878a
https://solidity-by-example.org/
Obrigado, ChatGPT, por me ajudar a escrever este artigo.
Artigo original publicado por Sam Vishwas. Traduzido por Paulinho Giovannini.
Latest comments (0)