WEB3DEV

Cover image for Crie um Formador de Mercado Automatizado (AMM) na Avalanche
Panegali
Panegali

Posted on

Crie um Formador de Mercado Automatizado (AMM) na Avalanche

Aprenda a construir um AMM básico com as funções Provide (Fornecer), Withdraw (Retirar) e Swap (Trocar)

Introdução

Neste tutorial, aprenderemos como construir um AMM muito básico com recursos como Fornecer, Retirar e Trocar sem nenhum mecanismo de incentivo, como taxas de negociação. Além disso, não negociaremos com tokens ERC20, manteremos nosso próprio mapeamento armazenando o saldo das contas para manter as coisas simples! Construiremos o contrato inteligente em Solidity e o frontend de nosso aplicativo com a ajuda do ReactJS.

Pré-requisitos

Requisitos

O que é um AMM?

O Formador de Mercado Automatizado (AMM) é um tipo de troca descentralizada, baseada em uma fórmula matemática de preços de ativos. Ele permite que ativos digitais sejam negociados sem necessidade de permissão e automaticamente usando pools de liquidez em vez de compradores e vendedores tradicionais que usam um livro de pedidos usado na bolsa (exchange) tradicional, aqui os ativos são precificados de acordo com um algoritmo de precificação.

Por exemplo, Uniswap usa p * q = k, onde p é o valor de um token no pool de liquidez e q é o valor do outro. Aqui “k” é uma constante fixa, o que significa que a liquidez total da carteira sempre deve permanecer a mesma. Para mais explicações, vamos dar um exemplo, se um AMM tiver moeda A e moeda B, dois ativos voláteis, toda vez que A é comprado, o preço de A sobe, pois há menos A no pool do que antes da compra. Por outro lado, o preço de B diminui à medida que há mais B no pool. O pool fica em equilíbrio constante, onde o valor total de A no pool será sempre igual ao valor total de B no pool. O tamanho aumentará apenas quando novos provedores de liquidez ingressarem no pool.

Implementando o contrato inteligente

Vamos começar com o código padronizado. Criamos um contrato nomeado AMM e importamos a biblioteca SafeMath do OpenZeppelin para realizar operações matemáticas com as devidas verificações.

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract AMM {
    using SafeMath for uint256;
}
Enter fullscreen mode Exit fullscreen mode

Em seguida, definimos as variáveis ​​de estado necessárias para operar o AMM. Estaremos usando a mesma fórmula matemática usada pela Uniswap para determinar o preço dos ativos (K = totalToken1 * totalToken2). Para simplificar, estamos mantendo nosso próprio mapeamento de saldo interno (token1Balance e token2Balance) em vez de lidar com os tokens ERC-20. Como o Solidity não suporta números de ponto flutuante, reservaremos os seis primeiros dígitos de um valor inteiro para representar o valor decimal após o ponto. Isso é obtido dimensionando os números por um fator de 10^6 (PRECISÃO).

uint256 totalShares;  // Armazena o montante total de ações emitidas para o pool
uint256 totalToken1;  // Armazena o montante do Token1 bloqueado no pool
uint256 totalToken2;  // Armazena o montante do Token2 bloqueado no pool
uint256 K;            // Constante algorítmica usada para determinar o preço (K = totalToken1 * totalToken2)

uint256 constant PRECISION = 1_000_000;  // Precisão de 6 casas decimais

mapping(address => uint256) shares;  // Armazena a participação acionária de cada provedor

mapping(address => uint256) token1Balance;  // Armazena o saldo disponível de usuários fora do AMM
mapping(address => uint256) token2Balance;
Enter fullscreen mode Exit fullscreen mode

Agora vamos definir modificadores que serão usados ​​para verificar a validade dos parâmetros passados ​​para as funções e restringir determinadas atividades quando o pool estiver vazio.

// Garante que o _qty não é zero e que o usuário tem saldo suficiente
modifier validAmountCheck(mapping(address => uint256) storage _balance, uint256 _qty) {
    require(_qty > 0, "Amount cannot be zero!");
    require(_qty <= _balance[msg.sender], "Insufficient amount");
    _;
}

// Restringe a retirada, o recurso de troca até que a liquidez seja adicionada ao pool
modifier activePool() {
    require(totalShares > 0, "Zero Liquidity");
    _;
}
Enter fullscreen mode Exit fullscreen mode

As seguintes funções são usadas para obter o estado atual do contrato inteligente

// Retorna o saldo do usuário
function getMyHoldings() external view returns(uint256 amountToken1, uint256 amountToken2, uint256 myShare) {
    amountToken1 = token1Balance[msg.sender];
    amountToken2 = token2Balance[msg.sender];
    myShare = shares[msg.sender];
}

// Retorna a quantidade total de tokens bloqueados no pool e o total de ações emitidas correspondentes a ele
function getPoolDetails() external view returns(uint256, uint256, uint256) {
    return (totalToken1, totalToken2, totalShares);
}
Enter fullscreen mode Exit fullscreen mode

Como não estamos usando os tokens ERC-20 e, em vez disso, mantemos um registro do saldo; precisamos de uma maneira de alocar tokens para os novos usuários para que eles possam interagir com o dApp. Os usuários podem chamar a função torneira (faucet) para obter alguns tokens para jogar!

// Envia token(s) grátis para o invocador
function faucet(uint256 _amountToken1, uint256 _amountToken2) external {
    token1Balance[msg.sender] = token1Balance[msg.sender].add(_amountToken1);
    token2Balance[msg.sender] = token2Balance[msg.sender].add(_amountToken2);
}
Enter fullscreen mode Exit fullscreen mode

Agora vamos começar a implementar as três funcionalidades principais - Provide, Withdraw e Swap.

Provide (Fornecer)

A função provide recebe dois parâmetros - quantidade do token1 e quantidade do token2 que o usuário deseja bloquear no pool. Se o pool estiver inicialmente vazio, a taxa de equivalência será definida como _amountToken1 : _amountToken2 e o usuário receberá 100 compartilhamentos para ele. Caso contrário, verifica-se se os dois valores informados pelo usuário possuem valor equivalente ou não. Isso é feito verificando se os dois valores estão em proporção igual ao número total de seus respectivos tokens bloqueados no pool, ou seja, _amountToken1 : totalToken1 :: _amountToken2 : totalToken2 deve manter.

// Adicionando nova liquidez ao pool
// Retorna o montante de ações emitidas para o bloqueio de determinados ativos
function provide(uint256 _amountToken1, uint256 _amountToken2) external validAmountCheck(token1Balance, _amountToken1) validAmountCheck(token2Balance, _amountToken2) returns(uint256 share) {
    if(totalShares == 0) { // Liquidez Gênesis é emitida 100 Ações
        share = 100*PRECISION;
    } else{
        uint256 share1 = totalShares.mul(_amountToken1).div(totalToken1);
        uint256 share2 = totalShares.mul(_amountToken2).div(totalToken2);
        require(share1 == share2, "Equivalent value of tokens not provided...");
        share = share1;
    }

    require(share > 0, "Asset value less than threshold for contribution!");
    token1Balance[msg.sender] -= _amountToken1;
    token2Balance[msg.sender] -= _amountToken2;

    totalToken1 += _amountToken1;
    totalToken2 += _amountToken2;
    K = totalToken1.mul(totalToken2);

    totalShares += share;
    shares[msg.sender] += share;
}
Enter fullscreen mode Exit fullscreen mode

Observe com atenção a ordem de atualização de saldo que estamos realizando na função acima. Primeiro deduzimos os tokens da conta dos usuários e, na última etapa, atualizamos o saldo de suas ações. Isso é feito para evitar um ataque de reentrância.

As funções fornecidas ajudam o usuário a obter uma estimativa da quantidade do segundo token que eles precisam bloquear para o valor do token fornecido. Aqui, novamente, usamos a proporção _amountToken1 : totalToken1 :: _amountToken2 : totalToken2 para determinar o montante de token1 necessário se desejarmos bloquear determinada quantidade de token2 e vice-versa.

// Retorna o montante do Token1 necessário ao fornecer liquidez com a quantidade do Token2 _amountToken2 
function getEquivalentToken1Estimate(uint256 _amountToken2) public view activePool returns(uint256 reqToken1) {
    reqToken1 = totalToken1.mul(_amountToken2).div(totalToken2);
}

// Retorna o montante de Token2 necessário ao fornecer liquidez com a quantidade de Token1 _amountToken1
function getEquivalentToken2Estimate(uint256 _amountToken1) public view activePool returns(uint256 reqToken2) {
    reqToken2 = totalToken2.mul(_amountToken1).div(totalToken1);
}
Enter fullscreen mode Exit fullscreen mode

Withdraw (Retirar)

Withdraw é utilizado quando um usuário deseja queimar um determinado montante de ações para recuperar seus tokens. Token1 e Token2 são liberados do pool proporcionalmente as ações queimadas em relação ao total de ações emitidas, ou seja, share : totalShare :: amountTokenX : totalTokenX.

// Retorna a estimativa de Token1 e Token2 que serão liberados na queima de _share
function getWithdrawEstimate(uint256 _share) public view activePool returns(uint256 amountToken1, uint256 amountToken2) {
    require(_share <= totalShares, "Share should be less than totalShare");
    amountToken1 = _share.mul(totalToken1).div(totalShares);
    amountToken2 = _share.mul(totalToken2).div(totalShares);
}

// Remove a liquidez do pool e libera Token1 e Token2 correspondentes ao sacador
function withdraw(uint256 _share) external activePool validAmountCheck(shares, _share) returns(uint256 amountToken1, uint256 amountToken2) {
    (amountToken1, amountToken2) = getWithdrawEstimate(_share);

    shares[msg.sender] -= _share;
    totalShares -= _share;

    totalToken1 -= amountToken1;
    totalToken2 -= amountToken2;
    K = totalToken1.mul(totalToken2);

    token1Balance[msg.sender] += amountToken1;
    token2Balance[msg.sender] += amountToken2;
}
Enter fullscreen mode Exit fullscreen mode

Swap (Trocar)

Para trocar de Token1 para Token2, implementaremos três funções - getSwapToken1Estimate, getSwapToken1EstimateGivenToken2e swapToken1. As duas primeiras funções apenas determinam os valores de troca para fins de estimativa enquanto a última faz a conversão.

getSwapToken1Estimate retorna o montante de token2 que o usuário receberá ao depositar um determinado montante de token1. O montante de token2 é obtido a partir da equação K = totalToken1 * totalToken2 onde o K deve permanecer o mesmo antes/depois da operação. Isso nos dá K = (totalToken1 + amountToken1) * (totalToken2 - amountToken2) e obtemos o valor amountToken2 resolvendo esta equação. Na última linha, estamos garantindo que o pool nunca seja totalmente drenado de nenhum dos lados, o que tornaria a equação indefinida.

// Retorna o montante de Token2 que o usuário receberá ao trocar um determinado montante de Token1 por Token2
function getSwapToken1Estimate(uint256 _amountToken1) public view activePool returns(uint256 amountToken2) {
    uint256 token1After = totalToken1.add(_amountToken1);
    uint256 token2After = K.div(token1After);
    amountToken2 = totalToken2.sub(token2After);

    // Para garantir que o pool do Token2 não se esgote completamente, levando à proporção inf:0
    if(amountToken2 == totalToken2) amountToken2--;
}
Enter fullscreen mode Exit fullscreen mode

getSwapToken1EstimateGivenToken2 retorna o montante de token1 que o usuário deve depositar para obter uma determinada quantidade de token2. O montante de token1 é obtido de forma semelhante resolvendo a seguinte equação K = (totalToken1 + amountToken1) * (totalToken2 -mountToken2).

// Retorna o montante de Token1 que o usuário deve trocar para obter _amountToken2 em troca
function getSwapToken1EstimateGivenToken2(uint256 _amountToken2) public view activePool returns(uint256 amountToken1) {
    require(_amountToken2 < totalToken2, "Insufficient pool balance");
    uint256 token2After = totalToken2.sub(_amountToken2);
    uint256 token1After = K.div(token2After);
    amountToken1 = token1After.sub(totalToken1);
}
Enter fullscreen mode Exit fullscreen mode

swapToken1 na verdade troca o valor em vez de apenas dar uma estimativa.

// Troca o montante de Token1 por Token2 usando a determinação de preço algorítmico
function swapToken1(uint256 _amountToken1) external activePool validAmountCheck(token1Balance, _amountToken1) returns(uint256 amountToken2) {
    amountToken2 = getSwapToken1Estimate(_amountToken1);

    token1Balance[msg.sender] -= _amountToken1;
    totalToken1 += _amountToken1;
    totalToken2 -= amountToken2;
    token2Balance[msg.sender] += amountToken2;
}
Enter fullscreen mode Exit fullscreen mode

Da mesma forma, para a troca do Token2 para o Token1, implementamos as três funções - getSwapToken2Estimate, getSwapToken2EstimateGivenToken1 e swapToken2 conforme abaixo.

// Retorna o montante de Token2 que o usuário receberá ao trocar um determinado montante de Token1 por Token2
function getSwapToken2Estimate(uint256 _amountToken2) public view activePool returns(uint256 amountToken1) {
    uint256 token2After = totalToken2.add(_amountToken2);
    uint256 token1After = K.div(token2After);
    amountToken1 = totalToken1.sub(token1After);

    // Para garantir que o pool do Token1 não se esgote completamente, levando à proporção inf:0
    if(amountToken1 == totalToken1) amountToken1--;
}

// Retorna o montante de Token2 que o usuário deve trocar para obter _amountToken1 em troca
function getSwapToken2EstimateGivenToken1(uint256 _amountToken1) public view activePool returns(uint256 amountToken2) {
    require(_amountToken1 < totalToken1, "Insufficient pool balance");
    uint256 token1After = totalToken1.sub(_amountToken1);
    uint256 token2After = K.div(token1After);
    amountToken2 = token2After.sub(totalToken2);
}

// Troca o montante de Token2 por Token1 usando a determinação de preço algorítmico
function swapToken2(uint256 _amountToken2) external activePool validAmountCheck(token2Balance, _amountToken2) returns(uint256 amountToken1) {
    amountToken1 = getSwapToken2Estimate(_amountToken2);

    token2Balance[msg.sender] -= _amountToken2;
    totalToken2 += _amountToken2;
    totalToken1 -= amountToken1;
    token1Balance[msg.sender] += amountToken1;
}
Enter fullscreen mode Exit fullscreen mode

Isso conclui a parte de implementação do contrato inteligente. Agora vamos implantá-lo na rede de teste Fuji C-Chain.

Implantando o contrato inteligente

Configurando a Metamask

Faça login na MetaMask, então clique no menu suspenso Network (Rede), depois selecione Custom RPC (RPC personalizado)

1

Configurações da rede de teste (Testnet) FUJI:

Financie seu endereço a partir da torneira indicada.

Implantar usando o Remix

Abra o Remix -> Selecione Solidity

2

Crie um arquivo AMM.sol no explorador de arquivos Remix e cole o seguinte código:

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract AMM {
    using SafeMath for uint256;
    uint256 totalShares;  // Armazena o montante total de ações emitidas para o pool
    uint256 totalToken1;  // Armazena o montante de Token1 bloqueado no pool
    uint256 totalToken2;  // Armazena o montante de Token2 bloqueado no pool
    uint256 K;            // Constante algorítmica usada para determinar o preço

    uint256 constant PRECISION = 1_000_000;  // Precisão de 6 dígitos

    mapping(address => uint256) shares;  // Armazena a participação acionária de cada provedor

    // Armazena o saldo disponível de usuários fora do AMM
    // Para simplificar, estamos mantendo nosso próprio 
    // mapeamento interno de saldo em vez de lidar com tokens ERC-20
    mapping(address => uint256) token1Balance;
    mapping(address => uint256) token2Balance;

    // Garante que o _qty não é zero e que o usuário tem saldo suficiente
    modifier validAmountCheck(mapping(address => uint256) storage _balance, uint256 _qty) {
        require(_qty > 0, "Amount cannot be zero!");
        require(_qty <= _balance[msg.sender], "Insufficient amount");
        _;
    }

    // Restrições de retirada, recurso de troca até que a liquidez seja adicionada ao pool
    modifier activePool() {
        require(totalShares > 0, "Zero Liquidity");
        _;
    }

    // Envia token(s) gratuito(s) ao invocador
    function faucet(uint256 _amountToken1, uint256 _amountToken2) external {
        token1Balance[msg.sender] = token1Balance[msg.sender].add(_amountToken1);
        token2Balance[msg.sender] = token2Balance[msg.sender].add(_amountToken2);
    }

    // Retorna o saldo do usuário
    function getMyHoldings() external view returns(uint256 amountToken1, uint256 amountToken2, uint256 myShare) {
        amountToken1 = token1Balance[msg.sender];
        amountToken2 = token2Balance[msg.sender];
        myShare = shares[msg.sender];
    }

    // Retorna o montante total de tokens bloqueados no pool e o total de ações emitidas correspondentes a ele
    function getPoolDetails() external view returns(uint256, uint256, uint256) {
        return (totalToken1, totalToken2, totalShares);
    }

    // Retorna o montante de Token1 necessário ao fornecer liquidez com o montante de Token2 _amountToken2 
    function getEquivalentToken1Estimate(uint256 _amountToken2) public view activePool returns(uint256 reqToken1) {
        reqToken1 = totalToken1.mul(_amountToken2).div(totalToken2);
    }

    // Retorna o montante de Token2 necessário ao fornecer liquidez com o montante de Token1 _amountToken1
    function getEquivalentToken2Estimate(uint256 _amountToken1) public view activePool returns(uint256 reqToken2) {
        reqToken2 = totalToken2.mul(_amountToken1).div(totalToken1);
    }

    // Adicionando nova liquidez no pool
    // Retorna o montante de ações emitidas para o bloqueio de determinados ativos
    function provide(uint256 _amountToken1, uint256 _amountToken2) external validAmountCheck(token1Balance, _amountToken1) validAmountCheck(token2Balance, _amountToken2) returns(uint256 share) {
        if(totalShares == 0) { // É emitida 100 ações de liquidez Genesis
            share = 100*PRECISION;
        } else{
            uint256 share1 = totalShares.mul(_amountToken1).div(totalToken1);
            uint256 share2 = totalShares.mul(_amountToken2).div(totalToken2);
            require(share1 == share2, "Equivalent value of tokens not provided...");
            share = share1;
        }

        require(share > 0, "Asset value less than threshold for contribution!");
        token1Balance[msg.sender] -= _amountToken1;
        token2Balance[msg.sender] -= _amountToken2;

        totalToken1 += _amountToken1;
        totalToken2 += _amountToken2;
        K = totalToken1.mul(totalToken2);

        totalShares += share;
        shares[msg.sender] += share;
    }

    // Retorna a estimativa de Token1 e Token2 que será liberado ao ser queimado dado _share
    function getWithdrawEstimate(uint256 _share) public view activePool returns(uint256 amountToken1, uint256 amountToken2) {
        require(_share <= totalShares, "Share should be less than totalShare");
        amountToken1 = _share.mul(totalToken1).div(totalShares);
        amountToken2 = _share.mul(totalToken2).div(totalShares);
    }

    // Remove liquidez do pool e libera o Token1 e Token2 correspondente para o sacador
    function withdraw(uint256 _share) external activePool validAmountCheck(shares, _share) returns(uint256 amountToken1, uint256 amountToken2) {
        (amountToken1, amountToken2) = getWithdrawEstimate(_share);

        shares[msg.sender] -= _share;
        totalShares -= _share;

        totalToken1 -= amountToken1;
        totalToken2 -= amountToken2;
        K = totalToken1.mul(totalToken2);

        token1Balance[msg.sender] += amountToken1;
        token2Balance[msg.sender] += amountToken2;
    }

    // Retorna o montante de Token2 que o usuário receberá ao trocar um determinado montante de Token1 por Token2
    function getSwapToken1Estimate(uint256 _amountToken1) public view activePool returns(uint256 amountToken2) {
        uint256 token1After = totalToken1.add(_amountToken1);
        uint256 token2After = K.div(token1After);
        amountToken2 = totalToken2.sub(token2After);

        // Para garantir que o pool do Token2 não se esgote completamente, levando à proporção inf:0
        if(amountToken2 == totalToken2) amountToken2--;
    }

    // Retorna o montante de Token1 que o usuário deve trocar para obter _amountToken2 em troca
    function getSwapToken1EstimateGivenToken2(uint256 _amountToken2) public view activePool returns(uint256 amountToken1) {
        require(_amountToken2 < totalToken2, "Insufficient pool balance");
        uint256 token2After = totalToken2.sub(_amountToken2);
        uint256 token1After = K.div(token2After);
        amountToken1 = token1After.sub(totalToken1);
    }

    // Troca o montante de Token1 por Token2 usando a determinação de preço algorítmico
    function swapToken1(uint256 _amountToken1) external activePool validAmountCheck(token1Balance, _amountToken1) returns(uint256 amountToken2) {
        amountToken2 = getSwapToken1Estimate(_amountToken1);

        token1Balance[msg.sender] -= _amountToken1;
        totalToken1 += _amountToken1;
        totalToken2 -= amountToken2;
        token2Balance[msg.sender] += amountToken2;
    }

    // Retorna o montante de Token2 que o usuário receberá ao trocar um determinado montante de Token1 por Token2
    function getSwapToken2Estimate(uint256 _amountToken2) public view activePool returns(uint256 amountToken1) {
        uint256 token2After = totalToken2.add(_amountToken2);
        uint256 token1After = K.div(token2After);
        amountToken1 = totalToken1.sub(token1After);

        // Para garantir que o pool do Token1 não se esgote completamente, levando à proporção inf:0
        if(amountToken1 == totalToken1) amountToken1--;
    }

    // Retorna o montante de Token2 que o usuário deve trocar para obter _amountToken1 em troca
    function getSwapToken2EstimateGivenToken1(uint256 _amountToken1) public view activePool returns(uint256 amountToken2) {
        require(_amountToken1 < totalToken1, "Insufficient pool balance");
        uint256 token1After = totalToken1.sub(_amountToken1);
        uint256 token2After = K.div(token1After);
        amountToken2 = token2After.sub(totalToken2);
    }

    // Troca o montante de Token2 por Token1 usando a determinação de preço algorítmico
    function swapToken2(uint256 _amountToken2) external activePool validAmountCheck(token2Balance, _amountToken2) returns(uint256 amountToken1) {
        amountToken1 = getSwapToken2Estimate(_amountToken2);

        token2Balance[msg.sender] -= _amountToken2;
        totalToken2 += _amountToken2;
        totalToken1 -= amountToken1;
        token1Balance[msg.sender] += amountToken1;
    }
}
Enter fullscreen mode Exit fullscreen mode

Navegue até a guia do compilador Solidity na barra de navegação do lado esquerdo e clique no botão azul para compilar o contrato AMM.sol. Anote o ABI que será necessário na próxima seção.

Navegue até a guia Deploy e abra o menu suspenso "ENVIRONMENT". Selecione "Injected Web3" (certifique-se de que a Metamask esteja carregada) e clique no botão "Deploy".

Aprove a transação na interface pop-up da Metamask. Depois que nosso contrato for implantado com sucesso, anote o contract address.

Uma interface binária de aplicação (ABI) é um objeto JSON que armazena os metadados sobre os métodos de um contrato, como tipo de dados de parâmetros de entrada, tipo de dados de retorno e propriedade do método, como pagável, exibição, puro, etc. Você pode aprender mais sobre a ABI na documentação do Solidity.

Criando um front-end no React

Agora, vamos criar um aplicativo React e configurar o front-end do aplicativo. No frontend, representamos token1 e token2 como KAR e KOTHI, respectivamente.

Abra um terminal e navegue até o diretório onde criaremos o aplicativo.

cd /path/to/directory
Enter fullscreen mode Exit fullscreen mode

Agora clone o repositório github, vá para o novo diretório avalance-amm e instale todas as dependências.

git clone https://github.com/SayanKar/avalanche-amm.git
cd avalanche-amm
npm install
Enter fullscreen mode Exit fullscreen mode

Em nosso aplicativo React, mantemos todos os componentes React no diretório src/components.

  • BoxTemplate

Ele renderiza a caixa contendo o campo de entrada, seu cabeçalho e o elemento à direita da caixa, que pode ser um nome de token, um botão ou estar vazio.

  • FaucetComponent

Toma a quantidade de token1 (KAR) e token2 (KOTHI) como entrada e financia o endereço do usuário com essa quantia.

  • ProvideComponent

Pega o valor de um token (KAR ou KOTHI), preenche o valor estimado do outro token e ajuda a fornecer liquidez ao pool.

  • SwapComponent

Ajuda a trocar um token por outro. Ele pega a quantidade de token no campo de entrada From e estima a quantidade de token no campo de entrada To e vice-versa.

  • WithdrawComponent

Ajuda a retirar a ação que se tem. Também permite retirar ao seu limite máximo.

  • ContainerComponent

Este componente renderiza o corpo principal de nosso aplicativo, que contém a caixa central contendo as guias para alternar entre os quatro componentes: Swap, Provide, Faucet, Withdraw. E também renderiza os detalhes da conta e os detalhes do pool.

Agora é hora de executar nosso aplicativo React. Use o seguinte comando para iniciar o aplicativo React.

npm start
Enter fullscreen mode Exit fullscreen mode

Passo a passo

  • Visite http://localhost:3000 para interagir com o AMM.
  • Obtendo fundos da torneira para interagir com o AMM

fdsdf

  • Adicionando liquidez no pool

fsd

  • Trocando tokens

hgf

  • Retirado liquidez do pool

jgh

Conclusão

Parabéns! Desenvolvemos com sucesso um modelo AMM funcional onde os usuários podem trocar tokens, fornecer e retirar liquidez. Como próximo passo, você pode brincar com a fórmula de preço, integrar o padrão ERC20, introduzir taxas como um mecanismo de incentivo para provedores ou adicionar proteção contra “escorregão” (slippage) e muito mais...

Solução de problemas

Falha na Transação

  • Verifique se sua conta tem saldo suficiente no fuji block-explorer. Você pode financiar seu endereço na torneira fornecida.

    54

  • Certifique-se de ter selecionado a conta correta na Metamask se tiver mais de uma conta conectada ao site.

78

Sobre os autores)

O tutorial foi criado por Sayan Kar, Yash Kothari e Nimish Agrawal e traduzido por Marcelo Panegali. Você pode contatá-los no Forum Figment para qualquer consulta sobre o tutorial.

Referências

Top comments (0)