9 Prática sobre Ferramentas de Desenvolvimento e Frameworks Ethereum: Introdução ao Web3
Introdução ao Web3
web3, métodos de desenvolvimento e implantação de contratos inteligentes pelo console javascript do cliente Geth e via Remix IDE. Conectamos na Rede Ethereum Privada Local e na Rede Simulada do Ganache via Metamask.
9.1 Introdução
A proposta é explorarmos a biblioteca web3 com o cliente Geth, e os métodos de desenvolvimento, implantação e verificação de contratos inteligentes. Testaremos comandos da API RPC e faremos o deploy de contratos inteligentes utilizando o console Javascript, utilizando comandos da API RPC e a IDE Remix.
A web3 é uma biblioteca JavaScript que pode ser usada na comunicação com um Nó Ethereum expondo métodos que podem ser acessados via RPC.
A interação com a instância de execução do cliente Geth pode ser feita via console Javascript do Geth com a parâmetro attach. Vários métodos de consulta e gerenciamento do blockchain são expostos e comandos podem ser executados neste console.
Uma outra forma de se comunicar com o cliente geth é via RPC. Já vimos muitos comandos providos pela web3, por exemplo, eth.accounts que retorna a lista de contas locais.
9.2 Ambiente de Execução para a Rede Local
Para essa prática iremos utilizar os comandos definidos na Aula 05: Criando uma Rede Ethereum Privada: Ethash.
Minha instalação do cliente geth está em ~/go-ethereum-1.11.6/build/bin/ então lembre-se de colocar o prefixo antes de cada um dos comandos.
- Iniciar
clefem um terminal. O comando para o console doclefpode ser visto no Código 9.I.
[.etherprivate-ethash]$ $HOME/go-ethereum-1.11.6/build/bin/clef --chainid 786 --keystore $HOME/.etherprivate-ethash/keystore --configdir $HOME/.etherprivate-ethash/clef --http- Iniciar o cliente de execução
gethcom suporte aoweb3. O comando para o console dogethpode ser visto no Código 9.II.
[.etherprivate-ethash]$ $HOME/go-ethereum-1.11.6/build/bin/geth --networkid 786 --datadir ~/.etherprivate-ethash/ --syncmode full --allow-insecure-unlock --identity "RAGEtherPrivate" --http --http.addr 127.0.0.1 --http.port 8559 --http.api "eth,net,web3,personal,engine,admin,debug,miner,txpool" --keystore ~/.etherprivate-ethash/keystore --authrpc.addr localhost --authrpc.port 8551 --authrpc.vhosts localhost --authrpc.jwtsecret ~/.etherprivate-ethash/geth/jwtsecret --nodiscover --maxpeers 15 --miner.etherbase=0x2db017e44b03b37755a4b15e14cd799f83de4c13 --signer=$HOME/.etherprivate-ethash/clef/clef.ipcO comando para o console do
prysm: Não precisa! Estamos usando a versão1.11.6-stable-ea9e62caque ainda suporta oethash, algoritmo de consenso Proof-of-Work (PoW).Iniciar um console
JavaScriptpara a interação com a instância de execução dogethconforme o Código 9.III.
[.etherprivate-ethash]$ $HOME/go-ethereum-1.11.6/build/bin/geth attach $HOME/.etherprivate-ethash/geth.ipc
Welcome to the Geth JavaScript console!
instance: Geth/RAGEtherPrivate/v1.11.6-stable-ea9e62ca/linux-amd64/go1.21.1
coinbase: 0x2db017e44b03b37755a4b15e14cd799f83de4c13
at block: 0 (Wed Dec 31 1969 21:00:00 GMT-0300 (-03))
datadir: /home/rogerio/.etherprivate-ethash
modules: admin:1.0 debug:1.0 engine:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 rpc:1.0 txpool:1.0 web3:1.0
To exit, press ctrl-d or type exit
> A Figura 9.I apresenta os comandos executando em cada um dos terminais.
9.3 Explorando Web3 com Geth
Utilize o console Javascript para verificar se os recursos web3 estão disponíveis, digite no console web3. e dê um tab. Escolhendo o comando web3.version como apresentado no Código 9.IV serão apresentadas algumas informações.
> web3.
web3.BigNumber web3.miner
web3._extend web3.net
web3._requestManager web3.padLeft
web3.admin web3.padRight
web3.bzz web3.personal
web3.constructor web3.providers
web3.createBatch web3.reset
web3.currentProvider web3.rpc
web3.db web3.setProvider
web3.debug web3.settings
web3.eth web3.sha3
web3.ethash web3.shh
web3.fromAscii web3.toAscii
web3.fromDecimal web3.toBigNumber
web3.fromICAP web3.toChecksumAddress
web3.fromUtf8 web3.toDecimal
web3.fromWei web3.toHex
web3.isAddress web3.toUtf8
web3.isChecksumAddress web3.toWei
web3.isConnected web3.txpool
web3.isIBAN web3.version
> web3.version
{
api: "0.20.1",
ethereum: undefined,
network: "786",
node: "Geth/RAGEtherPrivate/v1.11.6-stable-ea9e62ca/linux-amd64/go1.21.1",
whisper: undefined,
getEthereum: function(callback),
getNetwork: function(callback),
getNode: function(callback),
getWhisper: function(callback)
}
> Os recursos da biblioteca do Ethereum (eth) que estão disponíveis podem ser verificados no console conforme o Código 9.V.
> web3.eth.
..
web3.eth.accounts
web3.eth.blockNumber
web3.eth.chainId
web3.eth.coinbase
...
web3.eth.gasPrice
web3.eth.getAccounts
web3.eth.getBalance
web3.eth.getBlock
web3.eth.getBlockByHash
web3.eth.getBlockByNumber
web3.eth.getBlockNumber
web3.eth.getCoinbase
web3.eth.getGasPrice
...
web3.eth.getTransaction
web3.eth.getTransactionReceipt
web3.eth.submitTransaction
web3.eth.syncing
> web3.eth.accounts
["0x2db017e44b03b37755a4b15e14cd799f83de4c13", "0x7a7686ad451d2865a2246e239b674aefd4c6c27c", "0x1bba02873cc1c11f369a7b692f5f3de8ff7bbe80", "0x1170bbdc51d3791be6ba31dd6ed04383c146c2ed"]
> web3.eth.chainId
function()
> web3.eth.chainId()
"0x312"
> eval(0x312)
786
>No exemplo do Código 9.V foram recuperadas as contas com web3.eth.accounts e o id da rede em execução com a função web3.eth.chainId().
9.4 Deploy do Contrato via Console Javascript
Faremos o deploy de um contrato usando o console JavaScript. O passo a passo pode ser visto no livro texto e iremos reproduzir aqui a sequência:
- Executar o cliente de execução
getheclef. - Compilar o código do contrato.
- Criar um script de deployment, usando a
ABIe o bytecode, e algum códigoJavaScript. - Faremos o deploy do contrato via linha de comando pelo console
JavaScript. - Interagir com o contrato via um frontend web.
9.4.1 Web3 deployment: Executar o cliente geth e clef
- Executar o
clef. [] - Executar o
geth. [] - Executar o console
JavaScript. []
Executamos esse passo na Seção 9.2.
9.4.2 Web3 deployment: Compilar o código do contrato
O exemplo de contrato que iremos compilar e fazer o deploy é o valueChecker o Código 9.VI.
// SPDX-License-Identifier: Apache-2.0 OR MIT
pragma solidity ^0.8.19;
contract valueChecker {
uint price = 10;
event valueEvent(bool returnValue);
function Matcher (uint8 x) public returns (bool) {
if (x>=price) {
emit valueEvent(true);
return true;
}
}
}
Compile o contrato com o solc ou utilizando o Remix IDE, gerando o binário e a ABI, conforme apresentado no Código 9.VII.
$ solc --bin --abi -o bin ValueChecker.sol
$ ls
bin deploy.js ValueChecker.sol
$ cd bin
$ ls
valueChecker.abi valueChecker.bin
$ cat valueChecker.bin
6080604052600a60005534801561001557600080fd5b5061018b806100256000396000f3fe60806040523480
1561001057600080fd5b506004361061002b5760003560e01c8063f9d55e2114610030575b600080fd5b6100
4a600480360381019061004591906100f2565b610060565b604051610057919061013a565b60405180910390
f35b600080548260ff16106100ae577f3eb1a229ff7995457774a4bd31ef7b13b6f4491ad1ebb8961af120b8
b4b6239c600160405161009d919061013a565b60405180910390a1600190506100af565b5b919050565b6000
80fd5b600060ff82169050919050565b6100cf816100b9565b81146100da57600080fd5b50565b6000813590
506100ec816100c6565b92915050565b600060208284031215610108576101076100b4565b5b600061011684
8285016100dd565b91505092915050565b60008115159050919050565b6101348161011f565b82525050565b
600060208201905061014f600083018461012b565b9291505056fea264697066735822122088a7e63726327b
857c0d0a6d073976f05d5073826c629671c857a375db35d51c64736f6c63430008110033
$ cat valueChecker.abi
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"returnValue",
"type":"bool"}],"name":"valueEvent","type":"event"},{"inputs":[{"internalType":"uint8",
"name":"x","type":"uint8"}],"name":"Matcher","outputs":[{"internalType":"bool",
"name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]Tanto o bytecode, quanto a descrição da ABI serão utilizados na criação do script de deploy.
9.4.3 Web3 deployment: Criar um script de deployment
Iremos preparar o código do script JavaScript com as informações geradas pelo solc. Declarando um contrato utilizando a ABI e criando um novo contrato com o código binário do contrato compilado. Crie um arquivo deploy.js e salve o conteúdo do Código 9.VIII.
var valuecheckerContract = web3.eth.contract([{ "anonymous": false, "inputs": [{ "indexed": false, "internalType": "bool", "name": "returnValue", "type": "bool" }], "name": "valueEvent", "type": "event" }, { "inputs": [{ "internalType": "uint8", "name": "x", "type": "uint8" }], "name": "Matcher", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "nonpayable", "type": "function" }]);
var valuechecker = valuecheckerContract.new({
from: web3.eth.accounts[0],
data: '0x6080604052600a60005534801561001557600080fd5b5061010d806100256000396000f3006
08060405260043610603f576000357c01000000000000000000000000000000000000000000000
00000000000900463ffffffff168063f9d55e21146044575b600080fd5b348015604f57600080f
d5b50606f600480360381019080803560ff1690602001909291905050506089565b60405180821
5151515815260200191505060405180910390f35b600080548260ff1610151560db577f3eb1a22
9ff7995457774a4bd31ef7b13b6f4491ad1ebb8961af120b8b4b6239c600160405180821515151
5815260200191505060405180910390a16001905060dc565b5b9190505600a165627a7a7230582
09ff756514f1ef46f5650d800506c4eb6be2d8d71c0e2c8b0ca50660fde82c7680029', gas: '4700000'
},
function(e, contract) {
console.log(e, contract);
if (!e) {
if (typeof contract.address == 'undefined') {
console.log("Contract transaction send: TransactionHash: " +
contract.transactionHash + " waiting to be mined...");
} else {
console.log("Contract mined! Address: " + contract.address + ', transactionHash: ' + contract.transactionHash);
console.log(contract);
}
}
})9.5 Web3 deployment: Fazendo o deploy pelo console do geth
Para fazer o deploy iremos copiar o código Javascript do arquivo criado e colar no console Javascript, conforme Código 9.IX.
> var valuecheckerContract = web3.eth.contract([{ "anonymous": false, "inputs": [{ "indexed": false, "internalType": "bool", "name": "returnValue", "type": "bool" }], "name": "valueEvent", "type": "event" }, { "inputs": [{ "internalType": "uint8", "name": "x", "type": "uint8" }], "name": "Matcher", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "nonpayable", "type": "function" }]);
undefined
var valuechecker = valuecheckerContract.new({
from: web3.eth.accounts[0],
data: '0x6080604052600a60005534801561001557600080fd5b5061010d806100256000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f9d55e21146044575b600080fd5b348015604f57600080fd5b50606f600480360381019080803560ff1690602001909291905050506089565b604051808215151515815260200191505060405180910390f35b600080548260ff1610151560db577f3eb1a229ff7995457774a4bd31ef7b13b6f4491ad1ebb8961af120b8b4b6239c6001604051808215151515815260200191505060405180910390a16001905060dc565b5b9190505600a165627a7a723058209ff756514f1ef46f5650d800506c4eb6be2d8d71c0e2c8b0ca50660fde82c7680029', gas: '4700000'
},
function(e, contract) {
console.log(e, contract);
if (!e) {
if (typeof contract.address == 'undefined') {
console.log("Contract transaction send: TransactionHash: " +
contract.transactionHash + " waiting to be mined...");
} else {
console.log("Contract mined! Address: " + contract.address + ', transactionHash: ' + contract.transactionHash);
console.log(contract);
}
}
})Atenção pois é necessário confirmar a transação no _console do clef.
Se na execução do deploy der uma mensagem de erro Error: insufficient funds for gas * price + value undefined, é porque a carteira da conta selecionada não tem saldo suficiente. É necessário minerar com miner.start() para gerar algum saldo e repetir o deploy.
Error: insufficient funds for gas * price + value undefined
undefined
>
> miner.start()
null
> miner.stop()
null
> eth.getAccounts()
undefined
> eth.accounts
["0x2db017e44b03b37755a4b15e14cd799f83de4c13", "0x7a7686ad451d2865a2246e239b674aefd4c6c27c", "0x1bba02873cc1c11f369a7b692f5f3de8ff7bbe80", "0x1170bbdc51d3791be6ba31dd6ed04383c146c2ed"]
> eth.getBalance("0x2db017e44b03b37755a4b15e14cd799f83de4c13")
166000000000000300000
Repetindo-se os comandos no console Javascript e após as confirmações no console do clef:
-------------------------------------------
Request context:
NA -> ipc -> NA
Additional HTTP header data, provided by the external caller:
User-Agent: ""
Origin: ""
Approve? [y/N]:
> y
--------- Transaction request-------------
to: <contact creation>
from: 0x2db017E44b03B37755A4b15e14Cd799f83DE4c13 [chksum ok]
value: 0 wei
gas: 0x47b760 (4700000)
gasprice: 1000000000 wei
nonce: 0x0 (0)
chainid: 0x312
data: 0x6080604052600a60005534801561001557600080fd5b5061010d806100256000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f9d55e21146044575b600080fd5b348015604f57600080fd5b50606f600480360381019080803560ff1690602001909291905050506089565b604051808215151515815260200191505060405180910390f35b600080548260ff1610151560db577f3eb1a229ff7995457774a4bd31ef7b13b6f4491ad1ebb8961af120b8b4b6239c6001604051808215151515815260200191505060405180910390a16001905060dc565b5b9190505600a165627a7a723058209ff756514f1ef46f5650d800506c4eb6be2d8d71c0e2c8b0ca50660fde82c7680029
Request context:
NA -> ipc -> NA
Additional HTTP header data, provided by the external caller:
User-Agent: ""
Origin: ""
-------------------------------------------
Approve? [y/N]:
> y
\#\# Account password
Please enter the password for account 0x2db017E44b03B37755A4b15e14Cd799f83DE4c13
>
-----------------------
Transaction signed:
{
"type": "0x0",
"nonce": "0x0",
"gasPrice": "0x3b9aca00",
"maxPriorityFeePerGas": null,
"maxFeePerGas": null,
"gas": "0x47b760",
"value": "0x0",
"input": "0x6080604052600a60005534801561001557600080fd5b5061010d806100256000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f9d55e21146044575b600080fd5b348015604f57600080fd5b50606f600480360381019080803560ff1690602001909291905050506089565b604051808215151515815260200191505060405180910390f35b600080548260ff1610151560db577f3eb1a229ff7995457774a4bd31ef7b13b6f4491ad1ebb8961af120b8b4b6239c6001604051808215151515815260200191505060405180910390a16001905060dc565b5b9190505600a165627a7a723058209ff756514f1ef46f5650d800506c4eb6be2d8d71c0e2c8b0ca50660fde82c7680029",
"v": "0x648",
"r": "0x7085d0359b0cdef9c553d3e6f32a0a0a82dc0a376ed8fa18dae76dc93e274171",
"s": "0x50f8eb26062a35825f0c960856cec0c3d20fab15ed9ab272f9ab85b59bdfedf8",
"to": null,
"hash": "0xd3422f91fc4063682fcaed37cc7eb8b7b438cae9f0c9b56596bc49ededaf2081"
}No terminal de execução geth irá aparecer a mensagem de que o contrato foi submetido.
INFO [10-02|16:32:56.551] Submitted contract creation hash=0xd3422f91fc4063682fcaed37cc7eb8b7b438cae9f0c9b56596bc49ededaf2081 from=0x2db017E44b03B37755A4b15e14Cd799f83DE4c13 nonce=0 contract=0xe0203C7AEE6512789d63b54773dEDaCd84b1d06B value=0E no terminal do console Javascript irá aparecer a mensagem que o contrato está aguardando ser minerado:
null [object Object]
Contract transaction send: TransactionHash: 0xd3422f91fc4063682fcaed37cc7eb8b7b438cae9f0c9b56596bc49ededaf2081 waiting to be mined...
undefined
> Se iniciarmos a mineração novamente o contrato será minerado e o endereço atribuído a ele será apresentado 0xe0203c7aee6512789d63b54773dedacd84b1d06b:
> miner.start()
null
> null [object Object]
Contract mined! Address: 0xe0203c7aee6512789d63b54773dedacd84b1d06b, transactionHash: 0xd3422f91fc4063682fcaed37cc7eb8b7b438cae9f0c9b56596bc49ededaf2081
[object Object]
> miner.stop()9.6 Web3 deployment: Interagindo com o contrato
Após o deploy o contrato estará disponível no console. Podemos interagir com o contrato via console Javascript conforme o Código 9.X.
> valuechecker
valuechecker valuecheckerContract
> valuechecker.
valuechecker.Matcher valuechecker.allEvents
valuechecker._eth valuechecker.constructor
valuechecker.abi valuechecker.transactionHash
valuechecker.address valuechecker.valueEvent
> valuechecker.address
"0xe0203c7aee6512789d63b54773dedacd84b1d06b"
> valuechecker.transactionHash
"0xd3422f91fc4063682fcaed37cc7eb8b7b438cae9f0c9b56596bc49ededaf2081"Percebam o mesmo address e transactionHash que foram devolvidos no processo de deploy.
A ABI do valuechecker está disponível conforme o Código 9.XI.
> valuechecker.abi
[{
anonymous: false,
inputs: [{
indexed: false,
internalType: "bool",
name: "returnValue",
type: "bool"
}],
name: "valueEvent",
type: "event"
}, {
inputs: [{
internalType: "uint8",
name: "x",
type: "uint8"
}],
name: "Matcher",
outputs: [{
internalType: "bool",
name: "",
type: "bool"
}],
stateMutability: "nonpayable",
type: "function"
}]
> O código binário do contrato implantado pode ser recuperado no console javascript. O Código 9.XII apresenta o binário gerado para o Contrato.
> eth.getCode("0xe0203c7aee6512789d63b54773dedacd84b1d06b")
"0x608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f9d55e21146044575b600080fd5b348015604f57600080fd5b50606f600480360381019080803560ff1690602001909291905050506089565b604051808215151515815260200191505060405180910390f35b600080548260ff1610151560db577f3eb1a229ff7995457774a4bd31ef7b13b6f4491ad1ebb8961af120b8b4b6239c6001604051808215151515815260200191505060405180910390a16001905060dc565b5b9190505600a165627a7a723058209ff756514f1ef46f5650d800506c4eb6be2d8d71c0e2c8b0ca50660fde82c7680029"
> A função Matcher pode ser invocada para a verificação de valores conforme mostrado no Código 9.XIII.
> eth.getBalance(valuechecker.address)
0
> valuechecker.Matcher.call(12)
true
> valuechecker.Matcher.call(10)
true
> valuechecker.Matcher.call(5)
false
> Para chamar o contrato via eth.call(...) iremos precisar montar a chamada com os parâmetros. Supondo que queiramos chamar o método Matcher com o valor \(15\).
Serão passados \(68\) bytes no total subdivididos em:
0xf9d55e21: oIDdo método que é derivado dos quatro primeiros bytes do Keccak hash da forma ASCII da assinatura do métodoMatcher(uint8). O keccak hash deMatcher(uint8)é igual a0xf9d55e21d289cb5d19e2c60f6f1087d2d0cf1db6bb4037a139606a23599a61f3que pode ser gerado no consoleJavaScriptou utilizando a string “Matcher(uint8)” no site https://emn178.github.io/online-tools/keccak_256.html.
> web3.sha3('Matcher(uint8)')
"0xf9d55e21d289cb5d19e2c60f6f1087d2d0cf1db6bb4037a139606a23599a61f3"- 0x000000000000000000000000000000000000000000000000000000000000000f: o primeiro parâmetro, um valor
uint32do valor \(15\) padded para \(32\) _bytes$.
Então a chamada pode ser feita para o endereço do contrato implantado, o endereço apresentando no momento do deploy 0xe0203c7aee6512789d63b54773dedacd84b1d06b, pode ser recuperado pelo console Javascrip com o comando valuechecker.address. No campo data passamos a codificação da assinatura do método mais o valor a ser verificado como parâmetro.
No [Código Listing 9.XIV} é feita a chamada para a função Matcher(15) e o resultado 0x0000000000000000000000000000000000000000000000000000000000000001 representa o valor True.
> web3.eth.call({
to: "0xe0203c7aee6512789d63b54773dedacd84b1d06b",
data: "0xf9d55e21000000000000000000000000000000000000000000000000000000000000000F"
});
"0x0000000000000000000000000000000000000000000000000000000000000001"
>No Código 9.XV a mesma chamada é codificado e feita com o valor \(5\) e o resultado é do valor 0x0000000000000000000000000000000000000000000000000000000000000000 que representa \(0\), logo False.
> web3.eth.call({
to: "0xe0203c7aee6512789d63b54773dedacd84b1d06b",
data: "0xf9d55e210000000000000000000000000000000000000000000000000000000000000005"
});
"0x0000000000000000000000000000000000000000000000000000000000000000"No console Javascript ou em um código de um script que possa ser executado em um Navegador:
var result = web3.eth.call({
to: "0xe0203c7aee6512789d63b54773dedacd84b1d06b",
data: "0xf9d55e21000000000000000000000000000000000000000000000000000000000000000F"
});
undefined
> console.log(result);
0x0000000000000000000000000000000000000000000000000000000000000001
null
> result
"0x0000000000000000000000000000000000000000000000000000000000000001"
> 9.7 Deploy de Contrato com Transação de Contract Creation
O Código 9.XVI apresenta o deploy do código binário do contrato via transação.
> src = web3.eth.accounts[0]
"0x2db017e44b03b37755a4b15e14cd799f83de4c13"
> contract_code = "0x608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f9d55e21146044575b600080fd5b348015604f57600080fd5b50606f600480360381019080803560ff1690602001909291905050506089565b604051808215151515815260200191505060405180910390f35b600080548260ff1610151560db577f3eb1a229ff7995457774a4bd31ef7b13b6f4491ad1ebb8961af120b8b4b6239c6001604051808215151515815260200191505060405180910390a16001905060dc565b5b9190505600a165627a7a723058209ff756514f1ef46f5650d800506c4eb6be2d8d71c0e2c8b0ca50660fde82c7680029"
"0x608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f9d55e21146044575b600080fd5b348015604f57600080fd5b50606f600480360381019080803560ff1690602001909291905050506089565b604051808215151515815260200191505060405180910390f35b600080548260ff1610151560db577f3eb1a229ff7995457774a4bd31ef7b13b6f4491ad1ebb8961af120b8b4b6239c6001604051808215151515815260200191505060405180910390a16001905060dc565b5b9190505600a165627a7a723058209ff756514f1ef46f5650d800506c4eb6be2d8d71c0e2c8b0ca50660fde82c7680029"
> web3.eth.sendTransaction({from: src, data: contract_code, gas: 113558, gasPrice: 200000000000})
"0xf374e98fb632d5036f56ad5674e8f304fcb812fcd1be790f370e9e8a9adf806e"
> Approve? [y/N]:
> y
## Account password
Please enter the password for account 0x2db017E44b03B37755A4b15e14Cd799f83DE4c13
>
-----------------------
Transaction signed:
{
"type": "0x0",
"nonce": "0x11",
"gasPrice": "0x2e90edd000",
"maxPriorityFeePerGas": null,
"maxFeePerGas": null,
"gas": "0x1bb96",
"value": "0x0",
"input": "0x608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063f9d55e21146044575b600080fd5b348015604f57600080fd5b50606f600480360381019080803560ff1690602001909291905050506089565b604051808215151515815260200191505060405180910390f35b600080548260ff1610151560db577f3eb1a229ff7995457774a4bd31ef7b13b6f4491ad1ebb8961af120b8b4b6239c6001604051808215151515815260200191505060405180910390a16001905060dc565b5b9190505600a165627a7a723058209ff756514f1ef46f5650d800506c4eb6be2d8d71c0e2c8b0ca50660fde82c7680029",
"v": "0x647",
"r": "0xf323a86945330628a8746a8a079639de0d6d6f5ad65c006c16c09b3c50f3ff5e",
"s": "0x3b0d13eaac7523fea6e22b0df01724232999d7ac22b3993464348bebc8059874",
"to": null,
"hash": "0xf374e98fb632d5036f56ad5674e8f304fcb812fcd1be790f370e9e8a9adf806e"
}INFO [10-23|15:40:23.825] Submitted contract creation hash=0xf374e98fb632d5036f56ad5674e8f304fcb812fcd1be790f370e9e8a9adf806e from=0x2db017E44b03B37755A4b15e14Cd799f83DE4c13 nonce=17 contract=0x2Ec04170e7b52d40D494F149Bd5E29e0Bf0c8Dd8 value=09.8 Interagir com o contrato via um frontend web.
Podemos utilizar qualquer uma das redes locais ou simuladas para testar os comandos RPC. Primeiro utilizaremos a rede simulada do ganache-ui da Aula 07: Simulando uma Rede Ethereum Local com Ganache e Metamask. Executando a versão appmimage, versão atual ganache-2.7.1-linux-x86_64.AppImage, que pode ser executada sem a necessidade de instalação.
Executando o ganache-ui e indo na aba da lista de contas, conforme a Figura 9.II.
O comando RPC para a recuperação de contas via terminal utilizando o curl pode ser visto no Código 9.XVII.
$ curl -X POST --insecure --header "Content-Type: application/json" --location http://localhost:7545 --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[],"id":83}'
{"id":83,"jsonrpc":"2.0","result":["0x94c5ce22ba980e37c0b3a5105fec398ddcba711d",
"0x7fd63437b01daf6674c11f77a0a3e86dd2291221","0x8550abcf33308c521d8cd660a57ea2c9184dd33e",
"0xbb940857f76e328cfb9c36ba4c3a7b301d432f40","0x335c3af881896f490bfabf819c2200a9289eaace",
"0x91c2283b5053111baddc60cdd5f7db44b52614d7","0x3d806bdec35c37e0771bd6d67363c7b2d36eb631",
"0x91b318a4e2ec781404f1334698a897f2aceb3284","0xa7b91fe78a00c2003b49b92a39fb90e2fb67463c",
"0x05660e2dde69604288c632cc7b98e3f29d054316"]}A lista de blocos pode ser vista na Figura 9.III. O número do bloco mais recente presente na lista no meu caso é o \(2\) (hexadecimal \(0x2\)).
O número do último bloco pode ser recuperado utilizando o eth.blockNumber via RPC com o comando:
$ curl -X POST --insecure --header "Content-Type: application/json" --location http://localhost:7545 --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":83}'
{"id":83,"jsonrpc":"2.0","result":"0x2"}Recuperar o recibo de uma transação: link
$ curl -X POST --insecure --header "Content-Type: application/json" --location http://localhost:7545 --data '{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":
["0xc22b72d8469b0dc3676e61dfd09ead8faa3ff08a0f67e622232070587158cc9e"],"id":1}'
{"id":1,"jsonrpc":"2.0","result":{"transactionHash":
"0xc22b72d8469b0dc3676e61dfd09ead8faa3ff08a0f67e622232070587158cc9e",
"transactionIndex":"0x0","blockNumber":"0x2",
"blockHash":"0xf00b1600c68890bbf9a953bdaa87551072ccf1081febb99e2d5f03bf49f11cff",
"from":"0x94c5ce22ba980e37c0b3a5105fec398ddcba711d",
"to":"0x7fd63437b01daf6674c11f77a0a3e86dd2291221",
"cumulativeGasUsed":"0x5208","gasUsed":"0x5208","contractAddress":null,"logs":[],
"logsBloom":"0x0000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status":"0x1","effectiveGasPrice":"0x4a817c800","type":"0x2"}}Mais comandos da API RPC podem ser consultados na documentação JSON-RPC API.
Todos os comandos que vimos até o momento para a execução continuam válidos. Iremos testar o deploy na Rede Privada Local que criamos e com a Rede Simulada do Ganache.
Vimos que é possível interagir com o cliente de execução geth via POST requests utilizando a API JSON RPC sobre o HTTP. Para esse teste utilizaremos o curl. Lembrando que a porta utilizando foi a \(8559\).
Vimos que a lista de contas podem ser recuperadas com o comando:
[.etherprivate]$ curl -X POST --insecure --header "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[], "id":64}' --location http://localhost:8559
{"jsonrpc":"2.0","id":64,"result":["0x2db017e44b03b37755a4b15e14cd799f83de4c13","0x7a7686ad451d2865a2246e239b674aefd4c6c27c","0x1bba02873cc1c11f369a7b692f5f3de8ff7bbe80"]}
[.etherprivate]$ Um objeto JSON é retornado com a lista de contas.
No comando curl, o parâmetro --request é usado para especificar que o comando é uma requisição do tipo POST e --data é usado para especificar os parâmetros e valores. Finalmente, o localhost:8559 é usando para indicar o endereço que o HTTP endpoint do geth está respondendo.
Uma estimativa de custo para a o deploy do contrato poderia ser dada pela função eth_estimateGas via RPC:
$ curl -X POST --insecure --header "Content-Type: application/json" --location http://localhost:8559 --data '{"jsonrpc":"2.0","method": "eth_estimateGas", "params": [{"from": "0x94c5Ce22ba980e37C0B3a5105FEc398dDCbA711d", "data": "0x6060604052341561000f57600080fd5b60eb8061001d6000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa1146044575b600080fd5b3415604e57600080fd5b606260048080359060200190919050506078565b6040518082815260200191505060405180910390f35b60007f24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da600783026040518082815260200191505060405180910390a16007820290509190505600a165627a7a7230582040383f19d9f65246752244189b02f56e8d0980ed44e7a56c0b200458caad20bb0029"}], "id": 5}'
{"jsonrpc":"2.0","id":5,"result":"0x1959e"}
$ echo $((0x1959e))
103838E o deploy do contrato:
$ curl -X POST --insecure --header "Content-Type: application/json" --location http://localhost:7545 --data '{"jsonrpc":"2.0","method": "eth_sendTransaction", "params": [{"from": "0x94c5Ce22ba980e37C0B3a5105FEc398dDCbA711d", "gas": "0x1c31e", "data": "0x6060604052341561000f57600080fd5b60eb8061001d6000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa1146044575b600080fd5b3415604e57600080fd5b606260048080359060200190919050506078565b6040518082815260200191505060405180910390f35b60007f24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da600783026040518082815260200191505060405180910390a16007820290509190505600a165627a7a7230582040383f19d9f65246752244189b02f56e8d0980ed44e7a56c0b200458caad20bb0029"}], "id": 6}'
{"id":6,"jsonrpc":"2.0","result":"0x15d7ed0c9f19e98f6d8d82fd8a658d10fb3c4cdd7b510fbfb577b062f81bf9a7"}$ A transação 0x15d7ed0c9f19e98f6d8d82fd8a658d10fb3c4cdd7b510fbfb577b062f81bf9a7 de Contract Creation foi inserida na lista de transações, conforme Figura 9.IV.
Para chamar o contrato via RPC iremos precisar montar a chamada com os parâmetros. Supondo que queiramos chamar o método Matcher com o valor \(15\).
Serão passados \(68\) bytes no total subdivididos em:
0xf9d55e21: oIDdo método que é derivado dos quatro primeiros bytes do Keccak hash da forma ASCII da assinatura do métodoMatcher(uint8). keccak:0xf9d55e21d289cb5d19e2c60f6f1087d2d0cf1db6bb4037a139606a23599a61f3que pode ser gerado no consoleJavaScriptou utilizando a string “Matcher(uint8)” no site https://emn178.github.io/online-tools/keccak_256.html.
> web3.sha3('Matcher(uint8)')
"0xf9d55e21d289cb5d19e2c60f6f1087d2d0cf1db6bb4037a139606a23599a61f3"- 0x000000000000000000000000000000000000000000000000000000000000000f: o primeiro parâmetro, um valor
uint32do valor \(15\) padded para \(32\) _bytes$.
$ curl --location http://localhost:8559 -X POST --insecure --header "Content-Type: application/json" --data '{"jsonrpc":"2.0", "method":"eth_call", "params":[{"from": "0x2db017e44b03b37755a4b15e14cd799f83de4c13", "to": "0xe0203c7aee6512789d63b54773dedacd84b1d06b", "data": "0xf9d55e21000000000000000000000000000000000000000000000000000000000000000f"}, "latest"], "id":1}'
{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000000000001"}$ curl --location http://localhost:8559 -X POST --insecure --header "Content-Type: application/json" --data '{"jsonrpc":"2.0", "method":"eth_call", "params":[{"from": "0x2db017e44b03b37755a4b15e14cd799f83de4c13", "to": "0xe0203c7aee6512789d63b54773dedacd84b1d06b", "data": "0xf9d55e210000000000000000000000000000000000000000000000000000000000000005"}, "latest"], "id":1}'
{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000000000000"}9.9 Utilizando o REMIX IDE para os deploys
A REMIX é uma IDE que funciona online, sem a necessidade de instalação. A Figura 9.V apresenta a tela inicial da ferramenta que pode ser acessada em https://remix.ethereum.org/.
Na interface do REMIX podemos configurar a versão do compilador solc conforme apresentado na Figura 9.VI, a versão atual apresentada é a 0.8.27+commit.40a35a09, porém por estarmos executando a versão 1.11.6 do geth, a EVM é a Paris, logo para o conjunto de instruções ser compatível é necessário que compilemos com a versão 0.8.19 do solc.
O tipo de ambiente de deploy e a conta principal podem ser escolhidos na tela apresentada na Figura 9.VII.
O ambiente para deploy e execução de transações pode ser escolhido conforme Figura 9.VIII.
Dentre os possíveis temos: Injected Provider - Metamask, Remix VM (Cancun), Remix VM (Mainnet fork), Remix VM (Sepolia fork), Remix VM (Shangai), Dev - Hardhat Provider e Dev - Ganache Provider, entre outros que podem ser inseridos na lista na opção Customize this list….
9.9.1 Executando o geth para aceitar conexão com o REMIX
É necessário que executemos o geth para aceitar conexão com REMIX e nas configurações da IDE indicar a rede que será utilizada.
Para aceitar conexões da IDE REMIX inicie o cliente de execução geth com alguns parâmetros extras -http.corsdomain="https://remix.ethereum.org" --vmdebug, o Código 9.XVIII.
$ $HOME/go-ethereum-1.11.6/build/bin/geth --networkid 786 --datadir ~/.etherprivate-ethash/ --syncmode full --allow-insecure-unlock --identity "RAGEtherPrivate" --http --http.addr 127.0.0.1 --http.port 8559 --http.api "eth,net,web3,personal,engine,admin,debug,miner,txpool" --http.corsdomain="https://remix.ethereum.org" --vmdebug --keystore ~/.etherprivate-ethash/keystore --authrpc.addr localhost --authrpc.port 8551 --authrpc.vhosts localhost --authrpc.jwtsecret ~/.etherprivate-ethash/geth/jwtsecret --nodiscover --maxpeers 15 --miner.etherbase=0x2db017e44b03b37755a4b15e14cd799f83de4c13 --signer=$HOME/.etherprivate-ethash/clef/clef.ipc --dev console9.9.2 Conectando o REMIX ao Metamask
Caso não tenha uma rede conectada ao Metamask acesse a Aula 07: Simulando uma Rede Ethereum Local com Ganache e Metamask.
Na interface do Remix, a conexão com o Metamask pode ser feita utilizando a opção Injected Provider - Metamask, que por sua vez está conectado à Rede Privada Local. Escolha uma conta entre as listadas, a conta \(0\) que é retornada no eth.accounts.

A rede será detectada e podemos escolher uma conta entras as conectadas no Metamask para ser a conta que receberá cobrança pelos deploys, conforme Figura 9.IX.
Agora temos o Remix configurado e conectado à Rede Local via Metamask.
9.10 Fazendo o deploy pelo REMIX
Uma vez conectado à Rede Privada Local via Metamask é possível fazer deploy do contrato. Clicando em Deploy e fazendo as confirmações no Metamask.

A implantação aparecerá como pendente.

O processo de mineração pode ser acelerado, clicando na opção eu salvando.

INFO [10-03|23:17:32.431] Submitted contract creation hash=0xca3fede5fa596e4b93f50002d81fc1d025d6220fc984400d3be94508adbff802 from=0x2db017E44b03B37755A4b15e14Cd799f83DE4c13 nonce=6 contract=0x0c792147d011841783Da2C53815A7Dc9a19Cc9FC value=0Um erro apareceu no console da REMIX.
creation of valueChecker pending...
creation of valueChecker errored: Error occurred: Transaction started at 440 but was not mined within 122 blocks. Please make sure your transaction was properly sent and there no pervious pending transaction for the same account. However, be aware that it might still be mined!
Transaction Hash: 0xc2efdbae4744516ce7c9c17dbe9bf766f21604c4d1b9f39550a487028cf5a100.
Transaction started at 440 but was not mined within 122 blocks. Please make sure your transaction was properly sent and there no pervious pending transaction for the same account. However, be aware that it might still be mined!
Transaction Hash: 0xc2efdbae4744516ce7c9c17dbe9bf766f21604c4d1b9f39550a487028cf5a100
You may want to cautiously increase the gas limit if the transaction went out of gas.
Inicie a mineração e deixe ser acumulado algum saldo na carteira, se o saldo for suficiente o contrato será minerado:
> miner.start()
null
> null [object Object]
Contract mined! address: 0xe0203c7aee6512789d63b54773dedacd84b1d06btransactionHash: 0xd3422f91fc4063682fcaed37cc7eb8b7b438cae9f0c9b56596bc49ededaf2081
> miner.stop()Tentando o deploy novamente, o erro ainda ocorre:
creation of valueChecker pending...
creation of valueChecker errored: Error occurred: Transaction started at 1825 but was not mined within 92 blocks. Please make sure your transaction was properly sent and there no pervious pending transaction for the same account. However, be aware that it might still be mined!
Transaction Hash: 0x5cd14a7785878c22c8eaec8da1124ca574babcdaec44c4b66ecd1a6b33838899.
Transaction started at 1825 but was not mined within 92 blocks. Please make sure your transaction was properly sent and there no pervious pending transaction for the same account. However, be aware that it might still be mined!
Transaction Hash: 0x5cd14a7785878c22c8eaec8da1124ca574babcdaec44c4b66ecd1a6b33838899
You may want to cautiously increase the gas limit if the transaction went out of gas.Enquanto isso você pode acompanhar o status da transação pelo hash.
> eth.getTransaction("0x56482105054dd8d57f0cea35a58544d8327891870347c62d03853456bedb724c")
{
blockHash: null,
blockNumber: null,
chainId: "0x312",
from: "0x2db017e44b03b37755a4b15e14cd799f83de4c13",
gas: 183137,
gasPrice: 1331000000,
hash: "0x56482105054dd8d57f0cea35a58544d8327891870347c62d03853456bedb724c",
input: "0x",
nonce: 4,
r: "0xbd884444908c5f0b97631bbf93e2c0fa451522a9a17d1cced4b8019201168161",
s: "0x5137f387d7c3159b20533fc99c2b3dfad7c231b3e46e0e2bc78c058ebc0145d2",
to: "0x2db017e44b03b37755a4b15e14cd799f83de4c13",
transactionIndex: null,
type: "0x0",
v: "0x647",
value: 0
}
> Quando o contrato for minerado um retorno no console será apresentando:
> miner.start()
null
> null [object Object]
Contract mined! address: 0xe0203c7aee6512789d63b54773dedacd84b1d06btransactionHash: 0xd3422f91fc4063682fcaed37cc7eb8b7b438cae9f0c9b56596bc49ededaf2081
> miner.stop()9.11 Conectando o REMIX ao Ganache
O Remix pode se conectar diretamente ao Ganache através da opção Dev - Ganache Provider, precisa estar executando com o mesmo endereço e porta que o Ganache está respondendo.

Ao fazermos o deploy do contrato irá aparecer na lista de transações como CONTRACT CREATION.

Quando o contrato for implantado vai aparecer no bloco que foi minerado na lista de blocos do Ganache.

Se forem feitas chamadas à função do contrato implantado, essas transações irão aparecer como CONTRACT CALL na lista de transações.

9.12 Interagindo com contratos via frontends web
A interação com smart contracts como parte de uma DApps é normalmente feito usando uma interface web desenvolvida utilizando HTML/JS/CSS. Algumas bibliotecas e frameworks como React, Redux, e Drizzle, podem também ser usadas. A Figura 9.X apresenta uma arquitetura básica de funcionamento de uma aplicação web que se comunica com a blockchain do Ethereum via comandos de cobertura da biblioteca web3.
9.13 Considerações Finais
Nesta prática fizemos o deploy ou implantação de um exemplo de contrato, via console Javascript, via chamada RPC e utilizando a REMIX conectada à Rede Privada Local via Metamask. Todas as redes privadas locais ou simuladas que testamos podem ser utilizadas para a execução de comandos de implantação de contratos.
9.14 Leitura Recomendada
Capítulo 15: Introducing Web3
10 Word Cloud
