WEB3DEV

Cover image for Entenda Como os Endereços são Usados para Transferências dentro/fora de Contratos Inteligentes
Panegali
Panegali

Posted on

Entenda Como os Endereços são Usados para Transferências dentro/fora de Contratos Inteligentes

Um contrato inteligente é um contrato auto executável com os termos do acordo diretamente escritos em código. Esses contratos são executados em uma rede blockchain e automaticamente aplicam, facilitam ou verificam a negociação ou execução de um contrato, permitindo transações sem necessidade de confiança em terceiros e descentralizadas. Existem muitos artigos que explicam o que é um contrato inteligente e como ele pode ser usado nos negócios do mundo real; consulte Smart Contract — From beginning to engineering (Contrato Inteligente - Do início à engenharia) para obter detalhes.

Neste artigo, não vou explicar os conceitos básicos sobre contratos inteligentes. Em vez disso, vou mostrar algo que confunde as pessoas quando começam a trabalhar com contratos inteligentes, especialmente ao programar em Solidity para transferir eth de um lado para o outro.

Endereço da Conta

Um endereço de conta blockchain, frequentemente referido apenas como "endereço de blockchain" ou "endereço de carteira", é um identificador único usado em uma rede blockchain para enviar, receber e armazenar ativos digitais. Cada endereço corresponde a um usuário ou entidade específica na blockchain. Você verá seu endereço de conta quando se conectar pela primeira vez a uma carteira de criptomoedas como a Metamask, que é criada quando sua conta é criada. Uma parte confusa é que você pode ver que pode alternar para diferentes redes Ethereum via Metamask, mas seu endereço de conta é o mesmo, que é uma longa sequência de caracteres alfanuméricos.

Os endereços de conta blockchain não são iguais em diferentes redes blockchain. Cada blockchain possui seu próprio formato e padrões de endereçamento, e endereços em uma blockchain geralmente não são válidos em outra.

No entanto, os endereços de conta blockchain dentro da rede Ethereum (incluindo suas várias bifurcações e redes compatíveis) geralmente são os mesmos ou compatíveis porque compartilham a mesma tecnologia subjacente, protocolos e métodos de geração de endereços.

No diagrama acima, a mesma conta na Carteira se conecta a 3 redes Ethereum diferentes e obtém o mesmo valor de endereço. O processo de geração de endereço é o mesmo que a Ethereum em todas as cadeias compatíveis com a Ethereum. Então, quando você cria uma conta, a Metamask ou qualquer carteira digital usa a mesma frase-semente para gerar a chave privada e, em seguida, a chave privada gera o mesmo endereço porque o processo é exatamente o mesmo.

Existem alguns casos de uso comuns para o endereço:

  • Enviar e Receber Ether: os endereços Ethereum são usados principalmente para enviar e receber Ether, a criptomoeda da rede Ethereum.
  • Transações de Tokens: os endereços Ethereum também podem ser usados para enviar e receber tokens, incluindo padrões populares como tokens ERC-20 e ERC-721.
  • Contratos Inteligentes: os endereços Ethereum são usados para interagir com contratos inteligentes. Transações para endereços de contratos acionam a execução de código predefinido na Máquina Virtual Ethereum (EVM).
  • Aplicações Descentralizadas (DApps): os usuários frequentemente usam seus endereços Ethereum para acessar e usar aplicações descentralizadas (DApps) na rede Ethereum.

Endereço do Contrato

O endereço de um contrato e um endereço de conta são ambos usados em redes blockchain, mas servem para propósitos diferentes e têm características distintas.

Um endereço de contrato está associado a um contrato inteligente implantado na blockchain. Não está associado a um indivíduo ou entidade como um endereço de conta, mas representa o local onde um contrato inteligente reside na blockchain.

Os endereços de contrato são gerados com base na transação que implanta o contrato inteligente. O endereço é tipicamente derivado do endereço da conta do remetente e do nonce (um número que representa a contagem de transações do remetente). Ele é determinístico, o que significa que o mesmo contrato implantado pelo mesmo remetente sempre resultará no mesmo endereço de contrato.

Como esses endereços são usados no Solidity

Como entendemos que tanto contas individuais quanto contratos inteligentes têm endereços e a diferença entre eles, é hora de ver como usá-los no Solidity.

Você pode encontrar códigos em Solidity como este:


function getContactBalance() public view returns (uint256) {
    return address(this).balance;
}

Enter fullscreen mode Exit fullscreen mode

O address é um tipo no Solidity e aqui ele converte this para um endereço. this é uma variável que representa o contrato atual. Seu tipo é o tipo do contrato. Como qualquer tipo de contrato basicamente herda do tipo de endereço, this é sempre convertível em endereço e, neste caso, contém seu próprio endereço.

O balance do endereço fornece o saldo para um contrato. Sim, é um conceito interessante quando você ouve pela primeira vez que um contrato pode ter ether na blockchain.

Vamos olhar para outro código em Solidity:


function getSenderBalance() public view returns (uint256) {
    return address(msg.sender).balance;
}

Enter fullscreen mode Exit fullscreen mode

No Solidity, existem variáveis e funções especiais que sempre existem globalmente e são usadas principalmente para fornecer informações sobre a blockchain. msg é comumente usado em contratos do Solidity para tarefas como verificação de transações e interação com usuários. Olhando para o código acima, msg.sender nos dá o endereço de quem chama este método getSenderBalance neste contrato. Na verdade, é o endereço da conta do chamador, em vez do endereço do contrato. O saldo é o mesmo que o saldo do contrato, como no exemplo anterior.

Além do sender do objeto msg, existem outros campos úteis de msg que você pode usar em seu contrato:

  1. msg.value: esta propriedade indica a quantidade de Ether (em wei) enviada junto com a transação. Isso permite que os contratos inteligentes acessem o valor (Ether) vinculado à transação.
  2. msg.data: esta propriedade fornece a carga útil de dados da transação, que geralmente inclui o seletor da função e quaisquer argumentos de função para chamadas de função feitas ao contrato.
  3. msg.gas: esta propriedade especifica a quantidade de gás enviada com a transação. O gás é usado para cobrir os custos computacionais e de armazenamento da execução das funções do contrato.
  4. msg.gasprice: esta propriedade indica o preço do gás (em wei por unidade de gás) que o remetente está disposto a pagar pela execução da transação. É usado para calcular a taxa de transação.
  5. msg.sig: esta propriedade contém os primeiros quatro bytes do hash da assinatura da função que foi chamada. Ela pode ser usada para identificar a função chamada dentro do contrato.

Vamos examiná-los em capítulos posteriores, usados em alguns exemplos.

Transferência de entrada/saída de um Contrato Inteligente

No mundo dos contratos inteligentes, não apenas uma conta tem fundos (ether), mas um contrato em si pode armazenar ether para algum propósito, como mostrado no exemplo no capítulo anterior. Por exemplo, contratos inteligentes podem ser usados para facilitar campanhas de financiamento coletivo. Os contribuidores enviam Ether para o contrato inteligente, que mantém os fundos até que um objetivo de financiamento pré-definido seja alcançado ou um prazo específico seja atingido. Se as condições forem cumpridas, os fundos são liberados para o proprietário do projeto; caso contrário, eles são reembolsados aos contribuidores.

Vamos ver o código abaixo como um exemplo para financiamento coletivo:


contract CrowdfundingEscrow {

  ...

  constructor() {
        projectOwner = msg.sender;
  }

  function contribute() public payable goalNotReached afterDeadline {
        require(msg.value > 0, "Contribution amount must be greater than zero");

        contributions[msg.sender] += msg.value;
        totalFunds += msg.value;

        emit FundingReceived(msg.sender, msg.value);

        if (totalFunds >= fundingGoal) {
            fundingGoalReached = true;
            emit FundingGoalReached(totalFunds);
        }
    }

  function withdrawFunds() public onlyOwner goalNotReached {
        uint256 amountToTransfer = totalFunds;
        totalFunds = 0;
        fundingGoalReached = false;

        payable(projectOwner).transfer(amountToTransfer);
        emit FundsTransferred(projectOwner, amountToTransfer);
    }

    function refundContributors() public goalNotReached afterDeadline {
        require(contributions[msg.sender] > 0, "You have not contributed to this project");

        uint256 refundAmount = contributions[msg.sender];
        contributions[msg.sender] = 0;

        payable(msg.sender).transfer(refundAmount);
        emit FundsRefunded(msg.sender, refundAmount);
    }
}

Enter fullscreen mode Exit fullscreen mode

Os contribuidores podem enviar Ether para o contrato usando a função contribute. O contrato acompanha as contribuições e, se o objetivo de financiamento for alcançado, aciona o evento FundingGoalReached. Caso o objetivo de financiamento não seja alcançado até o prazo, os contribuidores podem solicitar um reembolso usando a função refundContributors.

Como você pode ver, o método transfer é usado neste exemplo para transferir fundos para diferentes endereços. No método withdrawFunds, ele chama payable(projectOwner).transfer(amountToTransfer); para transferir do contrato para a conta do proprietário do projeto. No método refundContributors, ele chama payable(msg.sender).transfer(refundAmount); para transferir do contrato para a conta do contribuidor.

No entanto, não há um método transfer chamado no método contribute, que deveria enviar fundos do chamador para o contrato. O valor que o contribuidor deseja enviar também não é especificada nos parâmetros do método. A mágica realmente acontece na msg.value, que representa o valor que o chamador deseja enviar para o contrato. Quando o chamador chama esse método, ele precisa especificar o valor como parte da entrada da transação (consulte este artigo sobre como funciona a transação na rede blockchain).

Para um frontend JavaScript, pode-se usar a biblioteca Web3 para enviar o valor junto com a transação para o contrato, como mostrado no exemplo abaixo:


async function sendEtherToContract() {
    try {
        const contract = new web3.eth.Contract(contractABI, contractAddress);
        // Envia Ether para o contrato
        await contract.methods.contribute().send({
            to: contractAddress,
            value: etherToSend,
        });
    } catch (error) {
        console.error('Error:', error);
    }
}

Enter fullscreen mode Exit fullscreen mode

Como você pode ver, value: etherToSend é o valor enviado ao contrato, que é igual a msg.value e pode ser acessado dentro do contrato inteligente.

Conclusão

Neste artigo, abordei a diferença de endereços entre contas e contratos inteligentes. Também expliquei como usá-los e mostrei como é possível sacar fundos de um contrato e transferir fundos para um contrato em um código simples em Solidity. Espero que isso ajude a esclarecer algumas dúvidas para as pessoas que estão programando contratos inteligentes.

Referências

Blockchain — Fundamental Concepts for beginners

Criptomoedas — Fundamental concepts for beginners

Contrato Inteligente — From beginning to engineering


Artigo escrito por Joey Yi Zhao. Traduzido por Marcelo Panegali.

Oldest comments (0)