🚩 Nesse tutorial vamos criar, testar e "deployar" um contrato escrito em Vyper
usando o framework ApeWorx
.
Vyper
: linguagem para criar o contratoPyteste
: framework de testes para linguagemPython
ApeWorX
: framework de "workflow" para construir contratos usando a linguagemPython
Solidity
está para oVyper
assim como oHardHat
está para oApeWorX
Jest
está para oPytest
assim como oJavaScript
está para oPython
Brincadeiras a partes, o workflow para escrever contratos é mesmo independente da stack:
- Escrever o contrato.
- Testar de forma automatizada.
- Fazer deploy local >> testnet >> mainnet.
O contrato consiste apenas em salvar um variável "boolena" que tem a possibilidade de trocar de estato ou seja "flipar" quando alguém chama uma função do contrato. Portanto nosso contrato terá apenas uma variável um evento e uma função.
🚨 Instale o Python antes 🚨 Instale também o Ganache
Instale o ApeWorX
:
pip install eth-ape
ou
poetry add eth-ape
E o plugin do Vyper
:
ape plugins install vyper
Crie um pasta vazia e "rode":
ape init
O terminal vai te pedir o nome do projeto, em seguida "dê enter".
O ape
vai criar uma estrutura de diretórios pra você:
contracts/
interfaces/
scripts/
tests/
ape-config.yaml
Por fim precisamos adicionar um plugin para que o ape
baixe o compilador do Vyper
, por padrão ele tem apenas o compilador do Solidity
.
Abra o arquivo ape-config.yaml
e adicione o seguinte:
name: flipper
plugins:
- name: vyper
Crie um arquivo dentro do diretório contracts
chamado Flipper.vy
.
🚨 Você pode trocar os nomes, se souber o que está fazendo
flip: public(bool)
- As variáveis globais do contrato são declaradas no início e depois acessadas com
self.nome_da_variável
, nesse caso:self.flip
. - O
Vyper
gera umgetter
automaticamente quando a declaração da variável usa opublic
.
event Fliped:
state: bool
- Evento é um recurso que possibilita o envio de dados em tempo real apartir de um full-node para um cliente qualquer.
- Aqui ele será usado para notificar quando o estato do
flip
for alterado.
@external
def __init__():
self.flip = True
- O decorator
external
restringe a função a ser chamada apenas via transação ou por outro contrato e não pode ser chamada internamente. __init__
é o construtor default do Python.self
aqui indica que a variável "é do próprio contrato",self
é o mesmo quethis
em outras linguagens.- Um contrato em
Vyper
é como criar uma classe emPython
.
@external
def flipping() -> bool:
self.flip = not self.flip
log Fliped(self.flip)
return self.flip
- A função
flipping
acessa oflip
por meio doself
e "flipa" o valor dela, se fortrue
"vira"false
e o inverso. - Depois envia o evento
Fliped
para notificar que o estado do contrato foi alterado. - Por fim retorna o novo estado do contrato.
# @version ^0.3.0
flip: public(bool)
event Fliped:
state: bool
@external
def __init__():
self.flip = True
@external
def flipping() -> bool:
self.flip = not self.flip
log Fliped(self.flip)
return self.flip
Agora vamos testar o contrato pra saber se ele se comporta como esparamos, pra isso vamos usar o Ape
, Pytest
e Python
.
🚨 Você pode usar outro framework, se souber o que está fazendo
Python
é a linguagem de programação, assim como oJavaScript
.Pytest
é o mais maduro framework de teste do ambientePython
, assim como oJest
.ApeWorX
é um framework escrito emPython
para compilar, testar, e implantar contratos, assim como oHardHat
.
Dentro do diretório tests
crie dois arquivos:
tests/
conftest.py
test_flipper.py
conftest.py
é o primeiro arquivo que oPytest
vai procurar para iniciar os testes, nele vamos configurar nossas fixtures, não mude o nome desse arquivo.test_flipper.py
é onde vamos escrever os testes do contrato, pode trocar por qualquer nome que comece comtest_*.py
ou termine com*_test.py
.
from pytest import fixture
@fixture
def owner(accounts):
return accounts[0]
@fixture
def another(accounts):
return accounts[1]
@fixture
def flipper(project, owner):
return owner.deploy(project.Flipper)
Fixture
é um tema massa por si só, de forma simples, são funções que vão ser executadas antes dos testes pricipais e que podemos usar seu retorno dentro dos testes.owner
gera um endereço aleatório que vamos usar para "deployar" o contrato.another
gera um endereço aleatório que vamos usar "chamar" com o contrato.flipper
é a instancia do contrato depois de "deployar".
✅ Teste o estado inicial do contrato
def test_flipper_initial(flipper):
assert flipper.flip() == True
- Aqui vamos validar se o contrato realmente inicia com o valor de
flip
comotrue
.
✅ Teste o estado do contrato depois de um flipping
def test_change_flip(flipper, another):
flipper.flipping(sender=another)
assert flipper.flip() == False
- Depois de
another
"flipar" nosso contrato o estato deve inverter parafalse
.
✅ Teste se o contrato emite um evento depois de ser "flipado"
def test_get_fliped_event(flipper, another):
tx = flipper.flipping(sender=another)
event_list = tx.decode_logs(flipper.Fliped)
event = event_list[0].event_arguments
assert event == {"state": False}
- Depois de
another
"flipar" nosso contrato armazenamos o retorno emtx
. tx
contém os logs do momento que a transaçãoflipping
foi feita.- Então precisamos fazer o decode do nosso evento.
- Por fim, pegamos o primeiro evento e comparamos se ele tem a chave
state
e o valorfalse
.
Por fim vamos fazer o deploy em uma rede local usando o ganache
.
Coloque no arquivo ape-config.yaml
:
geth:
ethereum:
mainnet:
uri: http://127.0.0.1:8545
Agora precisamos de uma carteira que tenha ETH para fazer o deploy.
Inicie o ganache
que ele irá gerar algumas.
ganache -s "SEED"
-s
podemos passar um valor qualquer para que o ganache crie carteiras de forma determinística
Pegue uma chave privada do ganache
e ape
:
ape accounts import my_wallet
Enter Private Key: COLE_A_CHAVE_PRIVADA_AQUI
Create Passphrase: CRIE_UMA_SENHA
Repeat for confirmation: CRIE_UMA_SENHA
SUCCESS:
A new account 'SEU_ENDEREÇO_AQUI'
has been added with the id 'my_wallet'
Por último coloque no arquivo scripts/deploy.py
o conteúdo do deploy:
def main():
from ape import project, accounts
owner = accounts.load("my_wallet")
owner.deploy(project.Flipper)
Para "deployar" o contrato "rode" no terminal:
ape run deploy --network ::geth
ganache
precisa estar "rodando"
- Interagir com seu contrato pelo console.
- Lançar o contrato em uma testnet.