O objetivo deste artigo é criar um passo a passo para programadores com experiência, de forma que possam aprender rapidamente a incorporar em seu ambiente de desenvolvimento a utilização de Smart Contracts do Ethereum.
Por uma questão de brevidade, não vou explicar aqui conceitos de programação ou conceitos básico sobre Blockchain e suas tecnologias relacionadas. A intenção é explicar esses tópicos em um outro artigo de perspectiva mais executiva e abstrata.
O ambiente que vamos montar é focado no desenvolvimento de back-end e não inclui desenvolvimento de front-end ou mobile, que possuem necessidades específicas. Isso não impede que esse roteiro ajude a suportar estes outros tipos de aplicações através de chamadas a API.
Apesar de minha preferência pessoal por Go e deste artigo usar como exemplo o oompa
, um sistema de processamento de demandas que fiz nessa linguagem por motivos pessoais, isso é incidental. O artigo é agnóstico à linguagem e o ambiente montado será facilmente integrado com linguagens modernas de programação.
A intenção aqui é explicar como preparar a oficina para “sujar a mão na graxa”, portanto vou abordar aqui somente as ferramentas e o “encanamento”: o que precisa estar instalado no computador do desenvolvedor para que comece a incluir Smart Contracts nos seus projetos e como as partes se relacionam. O processo de desenvolvimento eventualmente será aprofundado em artigos próprios.
Caminho e Destino
Como toda jornada precisa de um destino, precisamos definir o nosso.
Nossa origem neste texto é sair de uma situação onde temos um projeto já em andamento, mas precisamos incluir suporte a Blockchain nele, de forma que possamos desenvolver Smart Contracts e testá-los adequadamente.
Saberemos quando chegamos a nosso destino quando tivermos cumprido os seguintes itens:
- Compilar um Smart Contract;
- Publicar o Smart Contract em uma rede de testes Ethereum;
- Criar um teste que represente uma mudança necessária no Smart Contract;
- Fazer a alteração necessária no Smart Contract;
- Publicar a nova versão do Smart Contract;
- Armazenar dados no Smart Contract que foi publicado;
- Alterar dados no Smart Contract publicado por múltiplas contas;
- Tomar uma cerveja ao sol, para comemorar o feito;
Caixa de Ferramentas
Existem algumas ferramentas que você precisa ter instaladas no seu ambiente para o desenvolvimento de Smart Contracts. Felizmente essas ferramentas estão disponíveis graças a equipe do Thruffle Suite. Existem outras ferramentas que podem ser utilizadas, mas minha preferência é pelo Truffle por vários motivos. Em especial, pelo fato que o ferramental que eles distribuem é open source, utilizado por grandes nomes como a Microsoft, Airbus, Amazon e Ernst Young, além de possuir uma versão comercial produtizada chamada Thruffle Teams, focada em desenvolvimento de grandes projetos através da colaboração de equipes de desenvolvedores. A documentação das ferramentas é extensiva, o software é bastante maduro e a comunidade de desenvolvedores, forte e ativa.
O suite possui três ferramentas principais, mas somente iremos abordar duas delas aqui, chamadas Ganache e Thruffle. A terceira, chamada Drizzle, não será abordada porque se destina ao desenvolvimentos de front-end, o que não é o caso. No futuro escreverei um artigo para abordar esse tipo de desenvolvimento.
Um projeto em andamento
Como comentei, vamos partir de um projeto já existente chamado oompa
. Ele já possui código fonte do software atual, pasta de assets e arquivos referentes ao versionamento git como .git
e gitignore
, assim como outros.
Um Nó Ethereum
A primeira coisa que precisaremos para desenvolver em Ethereum é, obviamente, uma rede Ethereum onde possamos publicar nossos contratos. Mas não qualquer rede Ethereum, porque processamento na rede do Ethereum custa dinheiro “de verdade”. É aí que entra o Ganache.
Existem alternativas, como Göerli, Kovan ou Rinkeby, entre outras, mas prefiro Ganache porque é simples, rápido, descomplicado e facilmente integrável no ambiente local sem dependência de servidores externos. Outros produtos possuem Use Cases para os quais são mais adequados.
Você pode instalar o Ganache em vários sabores. Para esse tutorial sugiro que instale o Ganache Desktop a partir de sua página offical em https://www.trufflesuite.com/ganache . Por razão de sua interface gráfica, será mais fácil de você entender o que está acontecendo.
O instalador instalará o Ganache globalmente no seu computador. Isso é coveniente porque você não precisa de um nó de Ethereum para cada um de seus projetos que utilizam Smart Contracts.
Depois de tomar familiaridade com o que está acontecendo, você pode passar a utilizar o Ganache CLI, instalável através de https://github.com/trufflesuite/ganache-cli . Ele faz a mesma coisa que a versão desktop, mas como sua interface não é visual, ele demanda menos recursos e pode ser rodado com facilidade em processos de CI/CD no servidor remoto.
Um Framework Adequado
O segundo componente de nossa lista é o Thruffle. Ele é um framework especialmente desenvolvido para dar suporte ao ciclo de desenvolvimento de Smart Contracts.
Assim como o Ganache, ele não é “the only kid on the block”, mas certamente é o mais popular. Projetos como Populus ou Embark são menos conhecidos e possuem Use Cases um pouco diferentes. Enquanto Populos é focado em Python, Embark possui nativamente suporte a outros ferramentais como IPFS.
Thruffle é basicamente Node.js com esteróides, o que o torna muito adequado ao uso da maioria dos programadores, já que Javascript de tornou praticamente “o Esperanto das linguagens de programação”.
Instale o Thruffle globalmente com:
npm install truffle -g
Em grande medida, ele irá se comportar como o node.js, com direito a prompt interativo e tudo mais.
Dando uma volta de Ganache
Ótimo, já temos o que precisamos no sistema. Agora vamos dar uma olhada nos novos brinquedos.
Abra o Ganache e escolha a opção Quickstart na tela inicial. Ele abre imediatamente numa tela onde mostra 10 endereços, com 100 Ether cada um. Estes são endereços de Wallets criados automaticamente, que utilizaremos para fazer operações como publicar Smart Contracts, chamar funções e todo tipo de ações necessárias para interagir com contratos e o envio de valores entre estas contas. Estas contas podem ser endereçadas de accounts[0]
a accounts[9]
a partir do Thruffle.
Repare que na barra superior existe uma URL como http://127.0.0.1:8545 . Este é o endereço onde o nó implementado pelo Ganache pode ser acessado.
Na aba Blocks, podemos examinar os blocos que já foram mineranos nesse Blockchain, assim como na aba Transactions podemos auditar as transações conforme elas forem ocorrendo. Com a opção Automine ligada, o Ganache pode ser configurado para efetuar imediatamente a mineração dos blocos, conforme as transações forem solicitadas. Quando desligada, essa opção permite que a demora de mineração de cada bloco possa ser ajustada em segundos. Outras configurações importantes são o preço do GAS e seu limite, assim como os níveis de verbosidade dos logs do próprio Ganache.
Contracts é a área onde podemos examinar os contratos que já foram publicados no nó e Events é onde podemos auditar os eventos disparados por estes contratos.
Evidentemente essa não é uma explicação exaustiva do Ganache, mas é um bom começo. Isso é o que nos importa nesse momento.
Colocando o Encanamento
Volte a linha de comando e chame o Thruffle. Ele deve responder com algo do tipo:
Whisper:~ goldberg$ truffle
Truffle v4.1.17 - a development framework for Ethereum
Usage: truffle <command> [options]
Commands:
init Initialize new and empty Ethereum project
compile Compile contract source files
migrate Run migrations to deploy contracts
deploy (alias for migrate)
build Execute build pipeline (if configuration present)
test Run JavaScript and Solidity tests
debug Interactively debug any transaction on the blockchain (experimental)
opcode Print the compiled opcodes for a given contract
console Run a console with contract abstractions and commands available
develop Open a console with a local development blockchain
create Helper to create new contracts, migrations and tests
install Install a package from the Ethereum Package Registry
publish Publish a package to the Ethereum Package Registry
networks Show addresses for deployed contracts on each network
watch Watch filesystem for changes and rebuild the project automatically
serve Serve the build directory on localhost and watch for changes
exec Execute a JS module within this Truffle environment
unbox Download a Truffle Box, a pre-built Truffle project
version Show version number and exit
See more at http://truffleframework.com/docs
Whisper:~ goldberg$
Caso não o faça, certifique-se que ele está no PATH
e revise sua configuração do npm
.
Crie uma pasta temporária em algum lugar e inicialize ela para que trabalhe com o Truffle:
Whisper:~ goldberg$ cd ~/temp/
Whisper:temp goldberg$ mkdir truffle_files
Whisper:temp goldberg$ cd truffle_files/
Whisper:truffle_files goldberg$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Whisper:~ goldberg$
Perceba que o passo acima é necessário porque por uma questão de segurança. O Truffle se negará a criar sua estrutura diretamente em uma pasta que já contenha arquivos.
Tomando o cuidado de não sobrescrever nada, copie o conteúdo dessa pasta para o seu projeto já existente. Caso seja necessário, renomeie as pastas do Truffle antes de movê-las. Seus nomes podem ser configurados mais tarde no arquivo truffle-config.js , só tome o cuidado de escolher um nome alternativo que indique o que a pasta contém.
A estrutura é bastante clara:
Whisper:truffle_files goldberg$ ls -la
total 16
drwxr-xr-x 6 goldberg staff 192B Mar 2 19:08 .
drwxr-xr-x 6 goldberg staff 192B Mar 2 19:07 ..
drwxr-xr-x 3 goldberg staff 96B Mar 2 19:08 contracts
drwxr-xr-x 3 goldberg staff 96B Mar 2 19:08 migrations
drwxr-xr-x 2 goldberg staff 64B Mar 2 19:08 test
-rw-r--r-- 1 goldberg staff 4.1K Mar 2 19:08 truffle-config.js
Whisper:~ goldberg$
A pasta contracts
é onde residem os Smart Contracts que são desenvolvidos, migrations
possui os scripts que farão a publicação desses contratos no nó do Ganache. Os testes moram na pasta test
, obviamente.
Movidos os arquivos, precisamos abrir o arquivo truffle-config.js
e revisar sua configuração. Note que ele é bastante curto, somente com algumas importantes opções: networks
, mocha
e compiler
. Nem todas as configurações estão representadas aqui e para uma discussão mais extensiva, você pode verificar a documentação oficial.
Para nosso uso, é importante observar que networks
é a configuração que aponta para o nó Ethereum que pretendemos usar. Thruffle possui suporte a várias redes, de forma que possamos escolher através das opções de linha de comando qual ele conectará a cada momento. Podemos, por exemplo, definir uma rede chamada TronCity e durante nosso ciclo de desenvolvimento configurá-lo para utilizar essa rede com comandos como:
truffle migrate --network TronCity
Isso permite a configuração de IDEs e processos de build e compilação de sua linguagem que utilizem redes diferentes, conforme o ambiente atual.
Se for omitido, o Truffle utiliza por default a rede development. Descomente as configurações desta rede no truffle-config.js
. Se você não tocou nas configurações, ele vai funcionar automaticamente com o Ganache. Caso necessário, configure conforme os dados presentes no topo do Ganache Desktop.
Ainda é possível customizar o Mocha, o framework de testes que o Truffle utiliza ou o solc
, que é o compilador da linguagem Solidity. Não mexa nisso, no momento, enquanto não conhecer melhor a linguagem.
Finalmente como disse, é possível customizar as pastas onde residem contratos, migrações e build com as chaves contracts_directory
, migrations_directory
e contracts_build_directory
respectivamente, entre outras. Verifique as referências sobre estas configurações, caso necessário.
Ciclo de Desenvolvimento
Ok, sei que é uma longa jornada, mas estamos chegando. Finalmente está na hora de criarmos nosso primeiro contrato. Vamos usar o Thruffle para isso:
truffle create contract HelloWorld
Perceba como ele inclui um arquivo chamado Helloworld.sol
na pasta contracts
. Solidity em muito se parece com Javascript e portanto é relativamente simples entender o que está acontecendo neste arquivo. Vamos compilá-lo para executarmos a primeira de nossas oito metas:
Whisper:oompa goldberg$ truffle compile
Error parsing /Users/goldberg/oompa/contracts/HelloWorld.sol:
ParsedContract.sol:1:1: ParserError: Source file requires different compiler version (current compiler is 0.5.16+commit.9c3226ce.Emscripten.clang - note that nightly builds are considered to be strictly less than the released versionpragma solidity ^0.4.22;
Compilation failed. See above.
O erro trata de uma diferença entre as versões do solc
e do Smart Contract sendo compilado, que espera ser processado pela versão 0.4.22 . Isso é definido na primeira linha do código fonte HelloWorld.sol
:
pragma solidity ^0.4.22;
Ele se recusa a compilar por causa da politica de versões, já que solc
que tenho instalado é o 0.5.6. A correção mais rápida, já que nosso caso, não é critico, é ajustar a versão do pragma no contrato, mudando ela para:
pragma solidity ^0.5.6;
Para nossos testes, certifique-se que a versão de seu compilador Solidity e seu contrato possuam a mesma versão. Em outro artigo, irei abordar a origem dessa situação e como corrigi-la adequadamente.
Whisper:oompa goldberg$ truffle compile
Compiling ./contracts/HelloWorld.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts
Whisper:oompa goldberg$
E finalmente completamos nossa primeira meta!
Perceba que o solc
compilou 2 arquivos e colocou as versões compiladas na pasta ./build/contracts
. Tratam-se de arquivos json com uma série de mapeamentos e bytecodes que representam um Smart Contract pronto para ser registrado em um bloco do Ethereum.
Migrations.sol
é um Smart Contract que controla as migrações e não precisa de atenção.
Vamos dar mais um passo e publicar contrato no nosso Ganache:
truffle create migration HelloWorld
Isso vai criar um arquivo de migration na pasta correspondente, de nome hello_world.js
prefixado com o timestamp atual. É ele que utilizaremos para fazer deploy de nosso contrato. Ele é, literalmente, um arquivo Javascript. Ajuste-o:
const HelloWorld = artifacts.require("HelloWorld");
module.exports = function(deployer) {
deployer.deploy(HelloWorld);
};
Existem algumas coisas interessantes acontecendo aqui.
Um arquivo de migração do Truffle recebe artifacts
e deployer
. Artifacts
representa as versões compiladas dos arquivos presentes na pasta ./build/contracts
e deployer
é encarregado de fornecer acesso ao Ganache.
Dessa forma, é possivel carregar o Smart Contract compilado em uma constante e submeter ela ao nó do Ethereum com a função deployer.deploy()
. Por padrão, esse deployment irá acontecer utilizando a Wallet do Ganache chamada accounts[0]
. Esse comportamento e outros podem ser alterados, conforme necessidade. Consulte a documentação sobre migrations sobre as possibilidades.
Efetue agora as migrations:
Whisper:oompa goldberg$ truffle migrate
Using network 'development'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0x4c53df710719d0e2d8de89d5f5a3a70a061c0ad7d726bba22f8b301287cf72e0
Migrations: 0xb72eee3382ad24013ed1af7be7f9049792d32414
Saving artifacts...
Running migration: 1614713995_hello_world.js
Deploying HelloWorld...
... 0xf6dac5d2b5d2034dd54e69ef482d0f99df5f8c93c5b46f8461a72cc3ef6af653
HelloWorld: 0x8dce9d1cd94f341a2dade2dd5c4e565da677ae87
Saving artifacts...
Whisper:oompa goldberg$
Verifique agora seu Ganache e perceberá que foi criado um bloco e duas transações. As transanções possuem detalhes como o endereço que as solicitou (FROM ADDRESS) , seu tipo (CONTRACT CREATION) e o endereço do contrato criado (CREATE CONTRACT ADDRESS). O bloco inclui informações como a representação binária dos dados registrados (TX DATA) e a quantidade de GAS consumido (GAS USED), assim como o número do bloco no blockchain (MINED IN BLOCK).
…e completamos nossa meta 2! Faltam 6.
Pessoas que tem um problema para cada solução
Nosso Smart Contract está publicado, mas não é exatamente útil. Vamos fazer ele servir para alguma coisa. Vamos definir alguns desejos sobre o que queremos dele:
truffle create test HelloWorld
…e temos um novo arquivo chamado hello_world.js
na pasta test
. Ele é bastante típico de frameworks de teste como o Mocha, com direito a callback e tudo mais:
contract('HelloWorld', function(accounts) {
it("should assert true", function(done) {
var hello_world = HelloWorld.deployed();
assert.isTrue(true);
done();
});
});
O que vale mencionar aqui é accounts
, que expõem as contas existentes no Ganache, como nossa agora conhecida accounts[0]
.
Repare que se você tentar rodar os testes agora, esse teste fracassará:
Whisper:oompa goldberg$ truffle teste
Using network 'development'.
Contract: HelloWorld
1) should assert true
> No events were emitted
0 passing (66ms)
1 failing
1) Contract: HelloWorld
should assert true:
ReferenceError: HelloWorld is not defined
at Context.<anonymous> (test/hello_world.js:3:23)
at /usr/local/lib/node_modules/truffle/build/webpack:/packages/truffle-core/lib/testing/testrunner.js:135:1
at /usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/property.js:119:1
at /usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/requestmanager.js:89:1
at /usr/local/lib/node_modules/truffle/build/webpack:/packages/truffle-provider/wrapper.js:134:1
at XMLHttpRequest.request.onreadystatechange (/usr/local/lib/node_modules/truffle/build/webpack:/~/web3/lib/web3/httpprovider.js:128:1)
at XMLHttpRequest.dispatchEvent (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:76:1)
at XMLHttpRequest._setReadyState (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:422:1)
at XMLHttpRequest._onHttpResponseEnd (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:615:1)
at IncomingMessage.<anonymous> (/usr/local/lib/node_modules/truffle/build/webpack:/~/xhr2/lib/xhr2.js:567:1)
at endReadableNT (_stream_readable.js:1204:12)
Whisper:oompa goldberg$
Isso contece porque ainda não temos o contrato disponível no arquivo de teste, já que a variável HelloWorld
não está definida. Defina-a na primeira linha do arquivo, com:
const HelloWorld = artifacts.require("HelloWorld")
contract('HelloWorld', function(accounts) {
it("should assert true", function(done) {
var hello_world = HelloWorld.deployed();
assert.isTrue(true);
done();
});
});
O variável artifacts
fica disponível no arquivo de testes. Ela é a forma como temos acesso versão já compilada do contrato.
A partir da versão compilada, é possível obter acesso ao contrato que foi publicado pela migration no blockchain, através da chamada NomeDoContrato.deployed()
. Note que estamos usando aqui o mesmo mecanismo que utilizamos no arquivo de migração para obter essa referência.
Pronto, o contrato ainda é inútil, mas “desquebrou”:
Whisper:oompa goldberg$ truffle test
Using network 'development'.
Contract: HelloWorld
✓ should assert true
1 passing (31ms)
Whisper:oompa goldberg$
Vamos dar mais um passinho em direção a nossa meta 3. Ajuste o teste para verificar se nosso contrato é capaz de armazenar um número e recuperá-lo. Para maiores informações sobre testes, você pode verificar na sessão da documentação Writting Tests in Javascript.
const HelloWorld = artifacts.require("HelloWorld")
contract('HelloWorld', async accounts => {
it("should store and retreive A", async () => {
var hello_world = await HelloWorld.deployed()
await hello_world.setA(7)
const A = await hello_world.getA()
assert.isTrue(A == 7)
})
});
Vale notar que aproveitei a oportunidade para me livrar do callback done()
e substituí-lo por async/await. É um movimento completamente válido e recomendável.
Obviamente se tentar rodar os testes agora, eles irão falhar. Afinal, essas funções ainda não foram implementadas. Ainda assim, concluímos nossa meta 3!
Vamos resolver o problema do teste novamente quebrado, voltando ao contrato:
pragma solidity ^0.5.6;
contract HelloWorld {
uint A;
constructor() public {
}
function setA(uint _A) public {
A = _A;
}
function getA() public view returns (uint) {
return A;
}
}
“Coisas estranhas” como view
e uint
começam a aparecer, mas não se assuste. Na verdade, são conceitos simples e a esmagadora maioria presentes em outras linguagens. Tomando uint
como exemplo, ele significa Unsigned Integer, ou seja, um número inteiro sem sinal. O comando view
é mais peculiar e quando aplicado a Smart Contracts significa que a função não grava nada no blockchain e portando não custará GAS para ser executada.
Abra o seu Ganache e verifique o saldo da wallet accounts[0]
. Você vai reparar que ela não possui mais os 100 Ether originais. Isso aconteceu porque em geral, a maioria das operações em Ethereum são pagas em Ether pela conta que a solicitou. O custo em processamento de execução é medido em GAS, que é comprado com Ether.
No futuro abordarei detalhes sobre esse assunto, mas por enquanto basta dizer que esse cálculo é feito e cobrado automaticamente pelo blockchain. Esse é um dos motivos pelos quais é importante ter em mente qual wallet solicitou a operação, pois é quem pagará por ela. Também é possivel definir o custo máximo que o solicitante está disposto a pagar pela operação.
Para entender os detalhes sobre Solidity, consulte a documentação da linguagem.
Try. Learn. Repeat.
Agora que você já entendeu o ciclo, fica fácil alcançar a meta 4:
Whisper:oompa goldberg$ truffle compile
Whisper:oompa goldberg$ truffle migrate
Whisper:oompa goldberg$ truffle test
E a nova versão de nosso contrato está no Blockchain! Estamos quase lá.
Interagindo Diretamente
Para a meta 5, vamos fazer de forma diferente, porque quero mostrar outra capacidade do truffle. Tente:
Whisper:oompa goldberg$ truffle console
truffle(development)> HelloWorld
Repare que ele retorna um objeto gigante! Essa é a representação do Smart Contract HelloWorld
que foi compilada pelo comando truffle compile
.
Você pode ter acesso a sua versão persistida no Blockchain e interagir com ela, para propósitos de Debug:
truffle(development)> HelloWorld.deployed().then(function(instance){return instance.setA(42);});
{
tx: '0x354ee48766918c572b5c12a65d2d7af3d5803183e73c83c5d884a8416c349e01',
receipt: {
transactionHash: '0x354ee48766918c572b5c12a65d2d7af3d5803183e73c83c5d884a8416c349e01',
transactionIndex: 0,
blockHash: '0x507b64aa81fc8156d366932b2e922543625556924c20eef44a8b2d9303eb7885',
blockNumber: 8,
from: '0xca2b465f815d77962a92514edb672efe7bdb66a6',
to: '0x1c61074a22c130f8651691fe432d0a837daf86cb',
gasUsed: 22295,
cumulativeGasUsed: 22295,
contractAddress: null,
logs: [],
status: '0x1',
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
},
logs: []
}
Existem algumas coisas acontecendo aqui, mas não se desespere, é só Javascript.
A primeira informação que você precisa é que HelloWorld.deployed()
retorna de forma assincrona a instância do contrato que está publicada no blockchain, por isso que existe aquele primeiro .then
, que aguarda esse retorno.
Ao ser resolvido, o argumento instance
da função passa a possuir esse contrato que foi publicado. Utilizamos ele para chamar a função setA(42)
do contrato, setando a variável conforme conveniente. Essa chamada retorna os dados da transação executada, incluindo o Receipt dela, com maiores detalhes sobre o que aconteceu no blockchain.
Perceba que a partir desse momento, esse dado está persistido em um bloco do blockchain e não mais na memória RAM de sua máquina. Você pode sair para almoçar, desligar a máquina e ainda tomar um café antes de prosseguir, que esse 42
está armazenado para sempre no Blockchain.
Experimente: derrube o truffle com pressionando duas vezes ˆC
e o carregue de novo, lendo a variável:
Whisper:oompa goldberg$ truffle console
truffle(development)> HelloWorld.deployed().then(function(instance){return instance.getA();}).then(function(value){return value.toNumber()});
42
truffle(development)>
Aqui se aplica a mesma lógica: retornamos o contrato que foi publicado através da resolução de uma promise, chamamos a função getA()
desse contrato e convertemos essa resposta para um número antes que seja exibido na tela. E lá está nosso 42
, A resposta!
A conversão para número é importante porque a resposta do blockchain vem em um formado que não é nativo do Javascript.
A boa notícia é que a partir da versão 5 do Truffle, a console passará a reconhecer async/await
nativamente e tornar as interações mais claras e fáceis.
Com isso, completamos nossa meta 6.
Último Degrau
Para nossa meta 7, quero expandir um pouco as funcionalidade de nosso contrato. Primeiro, vamos explicitar o objetivo no arquivo de testes:
const HelloWorld = artifacts.require("HelloWorld")
contract('HelloWorld', async accounts => {
it("should store and retreive A", async () => {
var hello_world = await HelloWorld.deployed()
await hello_world.setA(7)
const A = await hello_world.getA()
assert.isTrue(A == 7)
})
it("should store and retreive B", async () => {
var hello_world = await HelloWorld.deployed()
await hello_world.setA(4)
const A = await hello_world.getA()
assert.isTrue(A == 4)
})
it("should receive A and B from different accounts", async () => {
var hello_world = await HelloWorld.deployed()
await hello_world.setA( 11, { from: accounts[0] })
await hello_world.setB( 5, { from: accounts[1] })
const A = await hello_world.getA()
assert.equal(A.toNumber(), 11)
const B = await hello_world.getB()
assert.equal(B.toNumber(), 5)
})
it("should be able to multiply A and B", async () => {
var hello_world = await HelloWorld.deployed()
const A = await hello_world.getA()
const B = await hello_world.getB()
const X = await hello_world.multiplyBoth()
assert.equal( X.toNumber() , A.toNumber() * B.toNumber())
})
});
Ignore por um momento os primeiros dois testes. Já falamos sobre eles. Os outros possuem algumas coisas interessantes, mas primeiro vamos implementar as novas funcionalidades no contrato:
pragma solidity ^0.5.6;
contract HelloWorld {
uint A;
uint B;
constructor() public {
}
function setA(uint _A) public {
A = _A;
}
function getA() public view returns (uint) {
return A;
}
function setB(uint _B) public {
B = _B;
}
function getB() public view returns (uint) {
return B;
}
function multiplyBoth() public view returns (uint) {
return A * B;
}
}
Rode os testes com nosso agora conhecido:
truffle test
No terceito teste, utilizamos um argumento adicional from:
com accounts
para fazer chamadas às funções do contrato utilizando wallets diferentes, accounts[0]
e accounts[1]
. Cada uma dessas wallets paga pela chamada que fez. Você pode conferir isso no Ganache, tanto no saldo das carteiras, quanto nas informações sobre as transações.
No quarto, simplesmente utilizamos os valores já presentes no contrato para fazer a multiplicação de operação. Com isso, temos nossa meta 7 realizada!
Wrapping up
Não se engane com a aparente simplicidade do que conseguimos realizar depois de tanto trabalho, pois embaixo desse capô existe um universo e implicacões profundas.
A capacidade de ter um ponto em comum e independente das partes, capaz de armazenar informações e executar operações indepententemente dos usuários é um conceito poderoso que permite que estas partes não precisem confiar uma na outra, pois elas confiam no Smart Contract, que é auditável.
Isso permite que as pessoas examinem as regras do jogo antes de concordar em jogar, e que ninguém trapaceie.
No nosso exemplo, utilizamos uma singela operação de multiplicação. Ela não pode ser alterada por nenhuma das partes e sempre executará da mesma forma. As operações de configuração dos fatores A e B no nosso exemplo são públicas e qualquer um pode mudar os valores, mas é possível implementar regras de diversas formas que restrijam quem e em quais condições esses fatores podem ser modificados.
Ao mesmo tempo, não é possível a nenhuma das partes impedir que o contrato entregue o resultado da multiplicação, como definido pelas regras do contrato.
Além disso, Smart Contracts podem armazenar valores financeiros em Ether, que ficarão disponíveis para entrega conforme suas próprias regras definidas internamente.
Essas capacidades singulares permitem o uso de Smart Contracts para diversas aplicações como registro e controle de cadeias produtivas, criação de produtos finaneiros, registro de propriedade e centenas de outros.
Próximos Passos
Até aqui foi uma longa jornada, mas finalmente chegamos ao nosso destino: a cerveja!
Relaxe e realize a meta 8, tomando uma cerveja, vinho, café, suco de caju ou qualquer outra bebida de sua preferência. Você mereceu.
Como pode ver, existe muita coisa a ser aprendida nessa área de blockchains. Espero que esse texto tenha ajudado a colocar uma parte dessa informação no devido lugar.
Em artigos futuros vou abordar outras partes do desenvolvimento de Smart Contracts, mas por hora, chega. É necessário tempo para assimilar tanta informação.
Quando estiver pronto, seguem algumas sugestões sobre por onde continuar suas pesquisas:
- Pessoas e suas Carteiras, Organizações e seus Contratos: Como relações se materializam no Ethereum;
- Desenvolvimento de Front-End com Drizzle;
Fique a vontade para comentar, sugerir, compartilhar, perguntar ou entrar em contato através do Linkedin, Bycoders, meu site pessoal ou onde achar mais conveniente. Gosto de ouvir outras opiniões e aprender e ensinar com isso.
(Anterior) - (Sumário) - (Próximo)
By Jim Bruno Goldberg