WEB3DEV

Cover image for Negociação em Python e Web3 na 1inch
Panegali
Panegali

Posted on

Negociação em Python e Web3 na 1inch

Este é um olhar técnico sobre como fazer negociações com a DEX 1Inch usando Web3 e Python. Sinta-se à vontade para começar com uma visão geral menos técnica ou pular diretamente para a base de código aqui. Se você estiver interessado em um guia semelhante que use Javascript, dê uma olhada aqui!.

Parte 1: DEX 1inch utilizando Python e Web3 - Fazendo chamadas de contrato

Vou mostrar um exemplo de como você pode usar o Web3.Py para obter cotações e fazer negócios na Exchange 1inch. No repositório, há exemplos de negociação de ETH <–> DAI|MCD.

Nesta primeira parte do guia, vou percorrer os passos que tomamos para executar uma chamada de contrato contra o contrato 1inch Split e recuperar uma cotação atual para o preço de 1 ETH em DAI na exchange.

Na próxima parte do guia, mostrarei as etapas para fazer uma negociação DAI–>SNX.

Parte 2: Conceitos Chave

Configurando sua conta Ethereum

Como ponto de partida, para executar negociações usando esses scripts, você precisará ter uma conta Ethereum e a chave privada correspondente. Embora pareça provável que qualquer pessoa que esteja lendo isso já entenda os riscos associados ao trabalho direto com chaves privadas, irei em frente e jogarei isso fora de qualquer maneira. Se alguém obtiver acesso a essa chave privada, poderá acessar todas as moedas desta conta.

Usar uma chave privada é uma maneira rápida e fácil de testar e executar transações brutas usando Python e Web3, mas se você for usar esses scripts na produção, certifique-se de ter um sistema seguro para gerenciar suas chaves privadas. Melhor ainda, bifurque o repositório e adicione suporte para outras carteiras.

Dito isso, você pode usar algo como a MyCrypto para gerar uma conta e obter sua chave privada rapidamente. Certifique-se de baixar e verificar o cliente de desktop em vez de fazer qualquer coisa no navegador.

1) Defina sua conta Ethereum

Primeiro, você precisará definir a conta base que usará para negociar. Você pode fazer isso definindo uma variável de ambiente na mesma janela do terminal em que está executando o script:

export BASE_ACCOUNT='0x7777777777777777777777777777777'
Enter fullscreen mode Exit fullscreen mode

2) Defina a chave privada

Para esta parte do guia não é necessário definir sua chave privada!

Como a conta base, você pode definir isso no terminal como uma variável de ambiente no diretório do qual está executando o script:

export PRIVATE_KEY="<sua_chave_privada_vai_aqui>"
Enter fullscreen mode Exit fullscreen mode

Ao adicionar um espaço antes do comando export, ele impedirá que o comando seja salvo em seu histórico do bash.

A seguir, veremos mais sobre o que é a 1inch, como funciona e como configurar seu provedor Ethereum para interagir com a rede.

Negociando com 1inch

A Exchange 1inch é um agregador DEX. Quando você faz uma negociação na 1inch, os contratos procuram o melhor preço em um número crescente de diferentes DEXs. Embora você possa ajustar a distribuição de seu pedido e quais exchanges ela usará, você pode usar padrões razoáveis. Ter esses recursos é excelente porque, embora seja fácil começar a fazer negócios, os usuários avançados também podem fazer ajustes finos.

A 1inch oferece uma API que permitirá que você faça negociações programáticas rapidamente. Você verá algumas funções para isso no script também. A parte que mais me interessa, no entanto, e no que estamos focados neste guia, é usar o Web3.Py para fazer todas as interações diretamente com os contratos de 1inch on-chain.

Configurando a negociação

A 1inch abstraiu elegantemente grande parte das complexidades e nos deu uma excelente interface on-chain. Você pode encontrar uma cópia do contrato no github e on-chain em 1split.eth.

Mais importante, para o que estamos fazendo, dê uma olhada diretamente nas funções do contrato de interface:

contract IOneSplit is IOneSplitConsts {
    function getExpectedReturn(
        IERC20 fromToken,
        IERC20 toToken,
        uint256 amount,
        uint256 parts,
        uint256 disableFlags
    )
        public
        view
        returns(
            uint256 returnAmount,
            uint256[] memory distribution
        );

    function swap(
        IERC20 fromToken,
        IERC20 toToken,
        uint256 amount,
        uint256 minReturn,
        uint256[] memory distribution,
        uint256 disableFlags
    ) public payable;
}
Enter fullscreen mode Exit fullscreen mode

Com base nisso, podemos ver que usaremos dois pontos principais de interação. Primeiro, olhando para o método getExpectedReturn, podemos ver que ele está definido como public e view. View nos permite saber que podemos fazer chamadas de contrato para a função sem realmente gravar nenhum dado na blockchain. Começaremos com isso porque não precisamos nos preocupar em perder Ethereum em transações com falha se cometermos um erro. Como você pode esperar, o objetivo deste método é nos fornecer informações sobre o que podemos esperar que uma determinada negociação retorne!

Antes de podermos fazer a chamada do contrato, asseguraremos que nossa conexão com a rede Ethereum esteja ativa.

3) Estabelecer o Provedor Ethereum

A maneira mais comum de se conectar à rede Ethereum parece ser usando um nó Infura. Embora haja muita discussão sobre como o Infura pode ser um único ponto de falha centralizado, uma digressão completa sobre isso está além do escopo deste guia.

O que direi é que, se você está procurando uma maneira legal de executar um nó Ethereum, confira DAppNode. Estou executando um DAppNode desde 12/2019 e é incrível. De qualquer forma, se você estiver usando o Infura (e foi isso que usei para o guia), basta obter o ID do projeto Infura e defini-lo no script aqui:

eth_provider_url = 'https://mainnet.infura.io/v3/<seu_id_de_projeto_Infura_aqui>'
Enter fullscreen mode Exit fullscreen mode

Quando feito, deve se parecer com isto:

eth_provider_url = 'https://mainnet.infura.io/v3/asdf345dfg435345345'
Enter fullscreen mode Exit fullscreen mode

Agora vamos confirmar que nossa conexão com a rede Ethereum está ativa. Na parte superior do script, há um sinalizador que definiremos para garantir que não enviemos transações acidentalmente:

mude isso:

production = True
Enter fullscreen mode Exit fullscreen mode

para isso:

production = False
Enter fullscreen mode Exit fullscreen mode

Em seguida, você pode executar e testar o script e saber que não fará nenhuma negociação. Por padrão, o script criará uma chamada de contrato para o método getExpectedReturnpara verificar o que podemos esperar de uma negociação de 1 ETH –> DAI.

$ python one_inch_trades.py

2020-04-19 10:31:39,969 - __main__ - INFO - 1 ETH = 176.746129364100033347 DAI on 1 Inch agora mesmo!
Enter fullscreen mode Exit fullscreen mode

Assim, podemos ver que está funcionando. Vamos dar uma olhada exatamente no que fizemos lá.

# obter cotação de preço para 1 ETH em DAI agora mesmo

ethereum_price = one_inch_get_quote(ethereum, mcd_contract_address, Web3.toWei(1, 'ether'))

logger.info("1 ETH = {0} DAI on 1 Inch right now!".format(Web3.fromWei(ethereum_price[0], 'ether')))
Enter fullscreen mode Exit fullscreen mode

Primeiro chamamos a função: one_inch_get_quote com três parâmetros. O primeiro parâmetro representa o endereço _from_token, o segundo é o endereço _to_token e o terceiro é o _amount da moeda que queremos negociar.

O que você notará é que, neste caso, estamos passando a variável ethereumcomo nosso parâmetro _from_token. Anteriormente no script, podemos ver que ethereumestá definido como 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE. O que é isso?

Dentro de um contrato Ethereum que lida com moedas ERC20, geralmente faz mais sentido referenciar tokens diferentes por seu endereço de contrato real (em oposição a uma string ou algo assim). Como a Ethereum é a mãe de todos os tokens ERC20 e não possui um endereço de contrato, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE é comumente usado em contratos para fazer referência à Ethereum. Como veremos em um minuto, esse parâmetro é mapeado para o parâmetro IERC20 fromToken no contrato de divisão da 1inch. Bastante fácil!

Também vale a pena notar, os caras da EthereumDevIO apontaram que você também pode usar 0x0a favor do endereço 0xEeeee.. mais longo.

O segundo parâmetro é o _to_token e será mapeado para o parâmetro de contrato 1inch Split IERC20 toToken. Nesse caso, a parte importante é que estamos lidando com o endereço do contrato ERC20 para o token, não com o nome dos tokens ou algo assim. No script, codifiquei manualmente o endereço do contrato MCD/DAI, como pode ser visto aqui:

mcd_contract_address = Web3.toChecksumAddress('0x6b175474e89094c44da98b954eedeac495271d0f')  # Endereço do contrato do Token DAI
Enter fullscreen mode Exit fullscreen mode

O terceiro parâmetro é _amount ou valor. Tenho certeza de que não devo usar esses termos de forma intercambiável e tentarei esclarecer isso em breve, mas, por enquanto, é isso que você obtém.

O parâmetro _amount é bastante direto, exceto que eu quero apontar que escolhi usar a unidade base de Ether no script, pois é mais legível para mim. Nos níveis mais baixos e dentro dos contratos, a unidade de Wei é usada. Como você pode ver, usamos uma função integrada do Web3 para abstrair a complexidade e quaisquer problemas de estilo de ponto flutuante que possam surgir como este:

Web3.toWei(1, 'ether')
Enter fullscreen mode Exit fullscreen mode

Fazendo uma chamada de contrato

No exemplo, executamos uma chamada de contrato contra a blockchain Ethereum (de graça!) Para obter o preço atual de 1 ETH em DAI em um host de DEX usando 1inch. Vamos dar uma olhada em como fizemos essa chamada e os outros parâmetros que enviamos com a solicitação.

def one_inch_get_quote(_from_token, _to_token, _amount):
    '''
    Obtenha dados de cotação de um contrato de divisão da 1inch usando uma chamada em cadeia
    '''
    # carregar nosso contrato
    one_inch_join = web3.eth.contract(
        address=one_inch_split_contract, abi=one_inch_split_abi)

    # fazer solicitação de chamada para contratar na blockchain Ethereum
    contract_response = one_inch_join.functions.getExpectedReturn(
        _from_token, _to_token, _amount, 100, 0).call({'from': base_account})

    # logger.info("contract response: {0}".format(contract_response))
    return contract_response
Enter fullscreen mode Exit fullscreen mode

Primeiro, temos que carregar os contratos ABI. Vou pular uma explicação mais aprofundada do que está acontecendo por enquanto, mas, de forma mais simples, você pode pensar na ABI como uma maneira do Web3 saber quais funções estão disponíveis em um determinado contrato e quais parâmetros que eles esperam. Como você pode ver no início do script, nós carregamos os diferentes arquivos ABI do contrato assim:

one_inch_split_abi = json.load(open('abi/one_inch_split.json', 'r'))
mcd_abi = json.load(open('abi/mcd_join.json', 'r'))
Enter fullscreen mode Exit fullscreen mode

De dentro do nosso método one_inch_get_quote, podemos usar o Web.Py para carregar os contratos ABI:

one_inch_join = web3.eth.contract(
    address=one_inch_split_contract, abi=one_inch_split_abi)
Enter fullscreen mode Exit fullscreen mode

Usando o parâmetro address, também informamos ao Web3 qual é o endereço do contrato on-chain.

A partir daí, podemos simplesmente fazer a chamada do contrato usando as funções integradas do Web3:

contract_response = one_inch_join.functions.getExpectedReturn(
        _from_token, _to_token, _amount, 100, 0).call({'from': base_account})
Enter fullscreen mode Exit fullscreen mode

Os dois parâmetros finais que podemos ver são 100e 0, eles serão mapeados para os seguintes campos no contrato 1inch Split:

uint256 parts
Enter fullscreen mode Exit fullscreen mode

e

uint256 disableFlags
Enter fullscreen mode Exit fullscreen mode

Para obter mais informações sobre como interagir com contratos usando o Web3, consulte a documentação aqui.

Isso encerra a primeira parte do guia. Na parte dois, vamos nos aprofundar em como criamos as transações brutas e aprovamos o contrato de divisão de 1inch para gastar nossos tokens ERC20!


Artigo escrito por Zak e traduzido por Marcelo Panegali.

Oldest comments (0)