Olá, desenvolvedores! No último artigo, abordamos custos operacionais para implantação e uso de contratos inteligentes em blockchains EVM e a interação com o REMIX. Contudo, para uma aplicação descentralizada ou dApp, é essencial explorar outras ferramentas. Hoje, vamos aprender a criar uma aplicação Node.js que se conecta a um nó de blockchain e interage com contratos inteligentes.
Contrato Inteligente de Exemplo: SimpleStorage
Primeiramente implante o seguinte contrato:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract SimpleStorage {
uint storedData;
function calc(uint a, uint b) public pure returns (uint) {
return (a + b);
}
function set(uint x) public {
storedData = x;
}
function get() public view returns (uint) {
return storedData;
}
}
Entendendo Modificadores de Função em Solidity: View e Pure
Adicionei uma nova função com o modificador pure para testarmos. Como já mencionado em outros artigos, o objetivo é destacar pontos essenciais, sendo os modificadores de função em Solidity, como view e pure, particularmente especiais no contexto de blockchain.
- view: Este modificador é utilizado em funções que não modificam o estado do contrato inteligente, mas podem ler dados dele. Uma função marcada como view promete não alterar o estado do contrato de nenhuma maneira – isso inclui modificar variáveis de estado, emitir eventos, criar outros contratos, usar selfdestruct, etc. As funções view podem ser chamadas sem qualquer custo de gas quando são executadas externamente, mas usam gas quando chamadas internamente por outras funções do contrato.
- pure: Este modificador é usado para funções que não lêem nem modificam o estado do contrato. Uma função pure não apenas promete não modificar o estado do contrato, mas também não lê o estado. Por exemplo, uma função que realiza algum cálculo matemático e retorna um valor, sem acessar ou modificar variáveis de estado, seria marcada como pure. Assim como as funções view, as funções pure podem ser chamadas externamente sem custo de gas.
Outros Modificadores Importantes em Solidity: Payable, Nonpayable e External
Ambos os modificadores são cruciais para otimizar contratos inteligentes, pois permitem que os nós da rede compreendam melhor a interação de uma função com o estado do blockchain, otimizando assim a execução dessas funções. Além desses modificadores existem também o payable, nonpayable e external que são relevantes no contexto de otimização e blockchain:
- payable: Este modificador permite que uma função receba Ether (ou o token da rede). Sem este modificador, se uma função for chamada com um valor, a transação será rejeitada. Funções payable são usadas quando o contrato precisa manipular fundos.
- nonpayable (implícito): Este é o comportamento padrão para funções que não têm o modificador payable. Funções nonpayable não podem receber fundos.
- external: Este modificador indica que a função só pode ser chamada de fora do contrato (não pode ser chamada por outras funções dentro do mesmo contrato). Funções external são às vezes mais eficientes em termos de gas quando chamadas externamente, porque os dados são lidos diretamente do calldata.
Níveis de Visibilidade em Solidity: Public, Internal e Private
Além dos níveis de visibilidade comuns em outras linguagens:
- public: Funções public podem ser chamadas internamente (por outras funções no mesmo contrato) e externamente (por outras transações e contratos). Este é o modificador de visibilidade mais aberto.
- internal: Este modificador faz com que a função seja acessível apenas de dentro do contrato em que foi definida ou de seus contratos derivados (contratos herdados). Funções internal não podem ser chamadas por transações externas.
- private: Funções private são as mais restritivas em termos de visibilidade; elas só podem ser chamadas de dentro do próprio contrato em que são definidas e não estão disponíveis para contratos herdados ou para o mundo externo.
Após realizar a implantação, acesse o diretório artifacts dentro do workspace no Remix, abra o arquivo SimpleStorage.json e verifique o nó abi do json:
A ABI (Application Binary Interface) é um padrão que define como as funções de um contrato inteligente devem ser chamadas e como essas chamadas de funções são codificadas. Essencialmente, a ABI é uma interface que permite que diferentes programas e aplicativos, como interfaces de usuário ou outros contratos inteligentes, interajam com um contrato inteligente de maneira padronizada. Ela especifica os métodos e estruturas de dados que um contrato inteligente oferece ao mundo exterior, incluindo os tipos de entrada e saída de cada função. A ABI é crucial para a interoperabilidade entre contratos inteligentes e o código de aplicação externo, permitindo que eles se comuniquem de forma eficiente e segura.
Criando uma Aplicação Node.js para Executar Métodos do Contrato
No exemplo a seguir vamos criar uma aplicação Node.js que execute os métodos get e calc, pois são métodos que não alteram a blockchain e podem ser chamadas sem consumo de tokens da rede. Inicie a aplicação instalando as bibliotecas dotenv e web3:
mkdir solidity-saga-demo-01
cd solidity-saga-demo-01
npm init -y
npm install dotenv web3
Crie um arquivo .env com a seguinte estrutura:
RPC_URL=
CONTRACT_ADDRESS=
A variável RPC_URL deve apontar para o nó conectado à blockchain. No nosso caso, como estamos usando uma blockchain de testes, existe um nó público e gratuito disponível no endereço https://ethereum-holesky.publicnode.com. Para o endereço do contrato, use aquele correspondente ao seu contrato implantado. No meu caso as configurações ficam assim:
RPC_URL=https://ethereum-holesky.publicnode.com
CONTRACT_ADDRESS=0xa20b4f485ad2d2070c76857b3ae690c2671a0a53
Crie um arquivo chamado abi.json com a estrutura gerada pelo Remix e um arquivo index.js com o seguinte conteúdo:
require('dotenv').config();
const { Web3 } = require('web3');
const metadata = require('./abi.json');
const web3 = new Web3(process.env.RPC_URL);
const contract = new web3.eth.Contract(metadata.abi,
process.env.CONTRACT_ADDRESS);
async function execGet() {
try {
const data = await contract.methods.get().call();
console.log('Resultado: ', data);
} catch (error) {
console.error(error);
}
}
async function execCalc(a, b) {
try {
const result = await contract.methods.calc(a, b).call();
console.log('Resultado: ', result);
} catch (error) {
console.error(error);
}
}
async function run() {
await execGet();
await execCalc(1, 2);
}
run();
O bloco de código relevante neste exemplo é aquele que conecta a biblioteca ao nó para consumir os dados da blockchain e cria a instância do contrato com base na ABI:
const web3 = new Web3(process.env.RPC_URL);
const contract = new web3.eth.Contract(metadata.abi,
process.env.CONTRACT_ADDRESS);
Com isso feito podemos acessar os métodos seguindo o modelo contract.methods.[METHOD_NAME]([PARAMS]).call() como no exemplo abaixo:
const result = await contract.methods.calc(a, b).call();
console.log('Resultado: ', result);
Aspectos Arquiteturais de dApps e Contratos Inteligentes
Do ponto de vista de arquitetura gostaria de ressaltar os seguintes pontos:
- Um contrato inteligente é a interface lógica que interage com a blockchain consumindo dados e modificado o estado do contrato e por consequência da blockchain. Para um dApp atua como a camada de backend, a grande diferença é que o código é publico e pode ser executado por qualquer um.
- O nó que prove acesso à blockchain se assemelha ao servidor que hospeda um backend, com a diferença que tem a capacidade de executar qualquer contrato inteligente publicado. Redes novas geralmente provem nós públicos e gratuitos facilitando o desenvolvimento, porém isso adiciona um alto grau de centralização, pois na falha desses nós sua aplicação ficará indisponível (há vários serviços que oferecem soluções pagas).
- A possibilidade de executar esse bloco de código diretamente por uma interface de navegador abre inúmeras oportunidades para aplicações descentralizadas/distribuídas usando blockchain. A MetaMask atua como intermediadora do nó, enquanto a blockchain funciona como um banco de dados, a EVM como ambiente de execução para o backend – representado pelo contrato inteligente – e os custos de escrita são pagos diretamente pelo usuário.
Visão de Futuro: Blockchain, Descentralização e IPFS
Esses aspectos devem ajudá-lo a perceber o desenvolvimento baseado em contratos inteligentes como um novo paradigma para aplicações tolerantes a falhas e censura. Associado ao IPFS, é possível criar uma estrutura distribuída com baixo custo de armazenamento e elevado grau de descentralização.
Por esses aspectos não visualizo os tokens de redes baseadas em EVM como investimento para mim, e sim uma commodity para desenvolvimento (não confunda com commodity do ponto de vista de reguladores como CVM ou SEC). Acredito num futuro que possui Ether, por exemplo, para um departamento de tecnologia será como possuir créditos em uma núvem da AWS.
Espero que este guia tenha esclarecido os passos essenciais para desenvolver com Solidity e Node.js. No nosso próximo artigo, avançaremos para a execução do método ‘set’. Até a próxima!
Leave a Reply