Quão legal seria criar seu próprio dapp, que permitisse trocar todos os tipos de tokens sem precisar escrever uma única linha de código de Solidity? Bem, com o Uniswap SDK, você pode usar os contratos inteligentes do Uniswap para trocar um monte de tokens ERC20 com seu próprio front-end personalizado.
Neste tutorial, vamos criar um dApp React que permite trocar ETH por UNI.
O tutorial concentra-se na lógica real e em como se conectar com a interface do usuário com o agente de usuário padrão CSS. Não contém estilos personalizados. No entanto, também criei uma versão estilizada do dApp.
Se você estiver interessado em como isso foi feito, vá até a parte final da página em que vinculei o repositório e o site real.
Troque ETH por UNI usando Uniswap em segundo plano
Para seguir o conhecimento básico do tutorial sobre npm, é necessário React, ethers.js e wagmi.sh.
Primeiro, precisamos criar nosso dApp React. Usaremos o TypeScript para este projeto. Então abra seu terminal e escreva:
npx create-react-app uniswap-dapp --template typescript
Em seguida, altere o diretório para o recém-criado e instale as dependências:
cd uniswap-dapp
npm install ethers wagmi @ uniswap / v3-sdk @ uniswap / sdk-core
Agora inicie o servidor de desenvolvimento.
npm init
Seu navegador da web padrão deve abrir e exibir a infame tela do React.
Em seguida, abra o projeto recém-criado no seu editor de código favorito. Para começar com uma configuração limpa, exclua todos os códigos do boilerplate. Exclua tudo dos arquivos index.css
e App.css
. Exclua o arquivo 'logo.svg' e remova a maioria das coisas dentro do arquivo App.tsx
para que fique assim:
import React from 'react';
import './App.css';
function App() {
return <div></div>;
}
export default App;
Agora podemos começar!
Conexão da carteira
Vamos usar a biblioteca wagmi.sh
para uma conexão fácil com a carteira MetaMask. Se você já sabe como configurar uma conexão de carteira com o MetaMask e o wagmi.sh
, fique à vontade para pular esta seção.
Para conectar nosso aplicativo a um nó Ethereum e poder acessar informações relacionadas ao blockchain de qualquer lugar do nosso aplicativo, precisamos encerrar todo o aplicativo com o provedor WagmiConfig
e passar suas opções de configuração. Siga para App.tsx
e altere o arquivo da seguinte maneira:
import React from 'react';
import { chain, configureChains, WagmiConfig, createClient } from 'wagmi';
import { publicProvider } from 'wagmi/providers/public';
import './App.css';
const { provider, webSocketProvider } = configureChains(
[chain.goerli],
[publicProvider()]
);
const client = createClient({ autoConnect: true, provider, webSocketProvider });
function App() {
return <WagmiConfig client={client}></WagmiConfig>;
}
export default App;
Para esta demonstração, usaremos a testnet Goerli. Também usamos um provedor público, pois não precisamos enviar muitas transações. Se o seu dApp exceder os limites de taxa do provedor público, você precisará usar serviços como Alchemy ou Infura. Com isso, estamos conectados com sucesso a um nó Ethereum e já podemos ler dados da blockchain. No entanto, para fazer transações e alterar o estado blockchain, precisamos conectar nosso dApp a uma carteira.
Implementaremos nosso botão de conexão da carteira e lógica dentro do cabeçalho do dApp. Crie um novo componente Header
dentro de um novo diretório chamado components
com o seguinte conteúdo:
O InjectedConnector
refere-se à extensão MetaMask e garante que a MetaMask use Goerli como sua cadeia, passando a propriedade chainId
com seu valor definido como o ID da Goerli. Agora devemos poder conectar nossa carteira MetaMask ao dApp com este belo botão que aparece na página:
Após uma conexão bem-sucedida, o botão se transforma no endereço da carteira atualmente conectada.
Buscando preços à vista
Agora que a conexão da carteira foi resolvida, podemos nos concentrar no tópico real deste artigo: Como usar o Uniswap para trocar tokens de um front-end personalizado. Começaremos criando um novo custom hook chamado useSwap
em um novo diretório chamado hooks
.
const useSwap = () => {};
export default useSwap;
Para buscar os preços dos tokens, precisamos interagir com o contrato de um pool que inclua esse par de tokens. Como dito no começo, vamos interagir com o pool ETH-UNI. Internamente, o ETH é tratado como Ether ou WETH, porque o Uniswap pode lidar apenas com tokens ERC20 e o próprio ETH não é um token, mas uma moeda. Portanto, o pool que estamos procurando é na verdade um pool WETH-UNI. Vou explicar isso com mais detalhes mais adiante. Por enquanto, obteremos uma referência ao contrato do pool assim:
import { useContract, useProvider } from 'wagmi';
import IUniswapV3PoolArtifact from '@uniswap/v3-core/artifacts/contracts/interfaces/IUniswapV3Pool.sol/IUniswapV3Pool.json';
// WETH - UNI pool com taxa de 0.3%
const POOL_ADDRESS = '0x07A4f63f643fE39261140DF5E613b9469eccEC86';
const useSwap = () => {
const provider = useProvider();
const poolContract = useContract({
address: POOL_ADDRESS,
abi: IUniswapV3PoolArtifact.abi,
signerOrProvider: provider,
});
};
export default useSwap;
Como leremos apenas esse contrato e não faremos transações, basta usar um provedor em vez de um assinante ( um provedor com uma carteira conectada a ele ).
Em seguida, criamos duas interfaces Immutables
e State
modelando os dados do pool na cadeia de que precisamos.
interface Immutables {
token0: string;
token1: string;
fee: number;
}
interface State {
liquidity: ethers.BigNumber;
sqrtPriceX96: ethers.BigNumber;
tick: number;
}
Criamos duas funções getPoolImmutables
e getPoolState
, que buscam e retornam os dados correspondentes à sua respectiva interface.
const getPoolImmutables = async () => {
if (!poolContract)
throw new Error('Pool contract has not been initialized');
const [token0, token1, fee] = await Promise.all([
poolContract.token0(),
poolContract.token1(),
poolContract.fee(),
]);
const immutables: Immutables = {
token0,
token1,
fee,
};
return immutables;
};
const getPoolState = async () => {
if (!poolContract)
throw new Error('Pool contract has not been initialized');
const [liquidity, slot] = await Promise.all([
poolContract.liquidity(),
poolContract.slot0(),
]);
const PoolState: State = {
liquidity,
sqrtPriceX96: slot[0],
tick: slot[1],
};
return PoolState;
};
Com isso, já temos todos os dados de blockchain necessários para fazer uma cotação.
Adicionamos mais duas constantes à parte superior do arquivo.
const WETH_DECIMALS = 18;
const UNI_DECIMALS = 18;
E agora podemos implementar a função getQuote
que retorna a quantidade de UNI que obtemos por uma quantidade específica de ETH.
const getQuote = async (amount: number) => {
const [immutables, state] = await Promise.all([
getPoolImmutables(),
getPoolState(),
]);
const tokenA = new Token(chainId.goerli, immutables.token0, UNI_DECIMALS);
const tokenB = new Token(chainId.goerli, immutables.token1, WETH_DECIMALS);
const pool = new Pool(
tokenA,
tokenB,
immutables.fee,
state.sqrtPriceX96.toString(),
state.liquidity.toString(),
state.tick
);
const outputAmount = amount * parseFloat(pool.token1Price.toFixed(2));
return outputAmount;
};
A função chama as duas outras funções que definimos anteriormente para obter os dados atuais da blockchain. O que é interessante notar aqui é que o token0
do pool é UNI e o token1 do pool é WETH. Depois disso as classes Token
e Pool
da Uniswap são instanciadas com os dados retornados. Finalmente, obtemos a cotação multiplicando a quantidade de ETH pelo preço de UNI em relação ao ETH.
Juntando todas as peças e retornando a função getQuote
do componente seu arquivo agora deve ter a seguinte aparência:
import { chainId, useContract, useProvider } from 'wagmi';
import IUniswapV3PoolArtifact from '@uniswap/v3-core/artifacts/contracts/interfaces/IUniswapV3Pool.sol/IUniswapV3Pool.json';
import { ethers } from 'ethers';
import { Token } from '@uniswap/sdk-core';
import { Pool } from '@uniswap/v3-sdk';
interface Immutables {
token0: string;
token1: string;
fee: number;
}
interface State {
liquidity: ethers.BigNumber;
sqrtPriceX96: ethers.BigNumber;
tick: number;
}
const WETH_DECIMALS = 18;
const UNI_DECIMALS = 18;
// WETH - UNI pool with 0.3% fee
const POOL_ADDRESS = '0x07A4f63f643fE39261140DF5E613b9469eccEC86';
const useSwap = () => {
const provider = useProvider();
const poolContract = useContract({
address: POOL_ADDRESS,
abi: IUniswapV3PoolArtifact.abi,
signerOrProvider: provider,
});
const getQuote = async (amount: number) => {
const [immutables, state] = await Promise.all([
getPoolImmutables(),
getPoolState(),
]);
const tokenA = new Token(chainId.goerli, immutables.token0, UNI_DECIMALS);
const tokenB = new Token(chainId.goerli, immutables.token1, WETH_DECIMALS);
const pool = new Pool(
tokenA,
tokenB,
immutables.fee,
state.sqrtPriceX96.toString(),
state.liquidity.toString(),
state.tick
);
const outputAmount = amount * parseFloat(pool.token1Price.toFixed(2));
return outputAmount;
};
const getPoolImmutables = async () => {
if (!poolContract)
throw new Error('Pool contract has not been initialized');
const [token0, token1, fee] = await Promise.all([
poolContract.token0(),
poolContract.token1(),
poolContract.fee(),
]);
const immutables: Immutables = {
token0,
token1,
fee,
};
return immutables;
};
const getPoolState = async () => {
if (!poolContract)
throw new Error('Pool contract has not been initialized');
const [liquidity, slot] = await Promise.all([
poolContract.liquidity(),
poolContract.slot0(),
]);
const PoolState: State = {
liquidity,
sqrtPriceX96: slot[0],
tick: slot[1],
};
return PoolState;
};
return { getQuote };
};
export default useSwap;
Agora podemos começar a criar a interface do usuário que chama a função e atualiza de acordo com a cotação retornada. Crie um novo componente chamado SwapCard
com o seguinte conteúdo:
Agora, se você digitar qualquer número no campo de entrada para ETH, a quantidade esperada de UNI que você obteria se executasse o swap será exibida no segundo campo de entrada. Seu site agora deve ter a seguinte aparência:
Transforme ETH em WETH e aprove os gastos
Agora, antes que possamos pedir ao roteador Uniswap v3 que troque nosso ETH para UNI, precisamos converter nosso ETH em WETH e permitir que o contrato do roteador use WETH em nosso nome. Como mencionei anteriormente, na verdade não há pool que inclua ETH diretamente. O Uniswap funciona apenas com tokens ERC20, e é por isso que precisamos converter nosso ETH para o token ERC20, que representa ETH que é WETH.
Então começamos criando um novo arquivo chamado useWETH.tsx
que é um custom hook do React, assim como o useSwap
.
const useWETH = () => {};
export default useWETH;
Em seguida, queremos criar a instância de contrato do token WETH. Para isso, precisamos do ABI token. Crie um novo diretório chamado abis
e um novo arquivo chamado WETH.json
no diretório com este conteúdo:
{
"abi": [
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [{ "name": "", "type": "string" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "guy", "type": "address" },
{ "name": "wad", "type": "uint256" }
],
"name": "approve",
"outputs": [{ "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "src", "type": "address" },
{ "name": "dst", "type": "address" },
{ "name": "wad", "type": "uint256" }
],
"name": "transferFrom",
"outputs": [{ "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [{ "name": "wad", "type": "uint256" }],
"name": "withdraw",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [{ "name": "", "type": "uint8" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [{ "name": "", "type": "address" }],
"name": "balanceOf",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [{ "name": "", "type": "string" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{ "name": "dst", "type": "address" },
{ "name": "wad", "type": "uint256" }
],
"name": "transfer",
"outputs": [{ "name": "", "type": "bool" }],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "deposit",
"outputs": [],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": true,
"inputs": [
{ "name": "", "type": "address" },
{ "name": "", "type": "address" }
],
"name": "allowance",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{ "payable": true, "stateMutability": "payable", "type": "fallback" },
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "src", "type": "address" },
{ "indexed": true, "name": "guy", "type": "address" },
{ "indexed": false, "name": "wad", "type": "uint256" }
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "src", "type": "address" },
{ "indexed": true, "name": "dst", "type": "address" },
{ "indexed": false, "name": "wad", "type": "uint256" }
],
"name": "Transfer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "dst", "type": "address" },
{ "indexed": false, "name": "wad", "type": "uint256" }
],
"name": "Deposit",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "name": "src", "type": "address" },
{ "indexed": false, "name": "wad", "type": "uint256" }
],
"name": "Withdrawal",
"type": "event"
}
]
}
Você também pode copiar o ABI diretamente do Etherscan.
Agora estamos prontos para instanciar o contrato assim:
import { useSigner, useContract } from 'wagmi';
import WETHArtifact from '../utils/abis/WETH.json';
const WETH_ADDRESS = '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6';
const useWETH = () => {
const { data: signer } = useSigner();
const WETHContract = useContract({
address: WETH_ADDRESS,
abi: WETHArtifact.abi,
signerOrProvider: signer,
});
};
export default useWETH;
Acrescentamos uma constante adicional na parte superior, que representa o número de casas decimais do token WETH.
const WETH_DECIMALS = 18;
Em seguida, criamos uma função chamada deposit
que analisa o valor da entrada e chama a função de depósito no contrato WETH. Com a ajuda dessa função, convertemos nossa ETH nativa em tokens WETH que podemos usar para troca de tokens.
const deposit = async (amount: number) => {
if (!WETHContract)
throw new Error('WETH contract has not been initialized');
const parsedAmount = ethers.utils.parseUnits(
amount.toString(),
WETH_DECIMALS
);
const txn = await WETHContract.deposit({ value: parsedAmount });
return txn;
};
Agora só precisamos criar mais uma função e temos todos os pré-requisitos para poder executar um swap. Crie a função approve
. Para que o contrato do roteador Uniswap acesse nosso WETH, ele precisa ter permissão para fazê-lo.
const approve = async (address: string, amount: number) => {
if (!WETHContract)
throw new Error('WETH contract has not been initialized');
const parsedAmount = ethers.utils.parseUnits(
amount.toString(),
WETH_DECIMALS
);
const txn = WETHContract.approve(address, parsedAmount);
return txn;
};
return { deposit, approve };
Dentro da nossa função approve
chamamos a função de aprovação do contrato WETH e definimos quantos tokens o roteador pode acessar.
Agora estamos prontos para escrever a lógica da troca.
Execute o swap
Vá até o arquivo useSwap.tsx
e adicione esta importação:
import ISwapRouterArtifact from '@uniswap/v3-periphery/artifacts/contracts/interfaces/ISwapRouter.sol/ISwapRouter.json';
Agora, adicione essa constante à parte superior do arquivo:
const ROUTER_ADDRESS = '0xE592427A0AEce92De3Edee1F18E0157C05861564';
E o seguinte, sob o já existente hook useProvider
:
const { address } = useAccount();
const { data: signer } = useSigner();
const routerContract = useContract({
address: ROUTER_ADDRESS,
abi: ISwapRouterArtifact.abi,
signerOrProvider: signer,
});
const { approve, deposit } = useWETH();
Como mudaremos o estado da blockchain, precisamos de um assinante em vez de um provedor para o contrato do roteador. Além disso, importamos as funções que escrevemos no hook useWETH
.
Agora, finalmente, crie a função swap
que executa o swap.
const swap = async (amount: number) => {
if (!routerContract)
throw new Error('Router contract has not been initialized');
await deposit(amount);
await approve(ROUTER_ADDRESS, amount);
const immutables = await getPoolImmutables();
const parsedAmount = ethers.utils.parseUnits(
amount.toString(),
UNI_DECIMALS
);
const params = {
tokenIn: immutables.token1,
tokenOut: immutables.token0,
fee: immutables.fee,
recipient: address,
deadline: Math.floor(Date.now() / 1000) + 60 * 10,
amountIn: parsedAmount,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0,
};
const txn = await routerContract.exactInputSingle(params, {
gasLimit: ethers.utils.hexlify(700000),
});
return txn;
};
Esta função aceita a quantidade em ETH que precisa ser trocada como um argumento. Em seguida, converta ETH em WETH com a função deposit
que definimos anteriormente e aprova o roteador Uniswap v3 para acessar exatamente esse valor no WETH com a ajuda da função approve
.
Em seguida, obtenha os dados de pool necessários ( endereços de token, taxa ) com função getPoolImmutables
e construa um objeto de params que será passado como argumento para a transação. Finalmente, chamamos a função exactInputSingle
no contrato do roteador Uniswap que executa o swap. Um limite de gás personalizado deve ser definido porque a MetaMask não pode estimar a taxa de gás nesta transação.
Agora você pode adicionar a função swap à instrução de retorno e seu arquivo deve ter a seguinte aparência:
import {
chainId,
useAccount,
useContract,
useProvider,
useSigner,
} from 'wagmi';
import IUniswapV3PoolArtifact from '@uniswap/v3-core/artifacts/contracts/interfaces/IUniswapV3Pool.sol/IUniswapV3Pool.json';
import { ethers } from 'ethers';
import { Token } from '@uniswap/sdk-core';
import { Pool } from '@uniswap/v3-sdk';
import ISwapRouterArtifact from '@uniswap/v3-periphery/artifacts/contracts/interfaces/ISwapRouter.sol/ISwapRouter.json';
import useWETH from './useWETH';
interface Immutables {
token0: string;
token1: string;
fee: number;
}
interface State {
liquidity: ethers.BigNumber;
sqrtPriceX96: ethers.BigNumber;
tick: number;
}
const WETH_DECIMALS = 18;
const UNI_DECIMALS = 18;
// WETH - UNI pool with 0.3% fee
const POOL_ADDRESS = '0x07A4f63f643fE39261140DF5E613b9469eccEC86';
const ROUTER_ADDRESS = '0xE592427A0AEce92De3Edee1F18E0157C05861564';
const useSwap = () => {
const provider = useProvider();
const { address } = useAccount();
const { data: signer } = useSigner();
const routerContract = useContract({
address: ROUTER_ADDRESS,
abi: ISwapRouterArtifact.abi,
signerOrProvider: signer,
});
const { approve, deposit } = useWETH();
const poolContract = useContract({
address: POOL_ADDRESS,
abi: IUniswapV3PoolArtifact.abi,
signerOrProvider: provider,
});
const swap = async (amount: number) => {
if (!routerContract)
throw new Error('Router contract has not been initialized');
await deposit(amount);
await approve(ROUTER_ADDRESS, amount);
const immutables = await getPoolImmutables();
const parsedAmount = ethers.utils.parseUnits(
amount.toString(),
UNI_DECIMALS
);
const params = {
tokenIn: immutables.token1,
tokenOut: immutables.token0,
fee: immutables.fee,
recipient: address,
deadline: Math.floor(Date.now() / 1000) + 60 * 10,
amountIn: parsedAmount,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0,
};
const txn = await routerContract.exactInputSingle(params, {
gasLimit: ethers.utils.hexlify(700000),
});
return txn;
};
const getQuote = async (amount: number) => {
const [immutables, state] = await Promise.all([
getPoolImmutables(),
getPoolState(),
]);
const tokenA = new Token(chainId.goerli, immutables.token0, UNI_DECIMALS);
const tokenB = new Token(chainId.goerli, immutables.token1, WETH_DECIMALS);
const pool = new Pool(
tokenA,
tokenB,
immutables.fee,
state.sqrtPriceX96.toString(),
state.liquidity.toString(),
state.tick
);
const outputAmount = amount * parseFloat(pool.token1Price.toFixed(2));
return outputAmount;
};
const getPoolImmutables = async () => {
if (!poolContract)
throw new Error('Pool contract has not been initialized');
const [token0, token1, fee] = await Promise.all([
poolContract.token0(),
poolContract.token1(),
poolContract.fee(),
]);
const immutables: Immutables = {
token0,
token1,
fee,
};
return immutables;
};
const getPoolState = async () => {
if (!poolContract)
throw new Error('Pool contract has not been initialized');
const [liquidity, slot] = await Promise.all([
poolContract.liquidity(),
poolContract.slot0(),
]);
const PoolState: State = {
liquidity,
sqrtPriceX96: slot[0],
tick: slot[1],
};
return PoolState;
};
return { getQuote, swap };
};
export default useSwap;
Agora vá para o componente SwapCard.tsx
, adicione a lógica para salvar o valor da entrada no estado e adicione um botão que inicie o swap com o referido valor. Seu arquivo agora deve ficar assim:
import React from 'react';
import { useAccount } from 'wagmi';
import useSwap from '../hooks/useSwap';
const SwapCard = () => {
const [quote, setQuote] = React.useState(0);
const [amount, setAmount] = React.useState(0);
const { address } = useAccount();
const { getQuote, swap } = useSwap();
const onChangeAmountInput = async (
event: React.ChangeEvent<HTMLInputElement>
) => {
setAmount(parseFloat(event.target.value));
const quote = await getQuote(parseFloat(event.target.value));
setQuote(quote);
};
const onClickSwapButton = async () => {
const txn = await swap(amount);
await txn.wait();
};
return (
<>
<input
type="text"
placeholder="Amount in"
onChange={onChangeAmountInput}
/>
<p>ETH</p>
<input
type="text"
placeholder="Amount out"
disabled
value={quote === 0 ? '' : quote}
/>
<p>UNI</p>
<button disabled={address ? false : true} onClick={onClickSwapButton}>
Swap
</button>
</>
);
};
export default SwapCard;
Além disso, adicionei o comportamento de que o botão de troca só ficará ativado se uma carteira estiver conectada.
Agora vá até o localhost: 3000 para testá-lo, porque tudo deve estar funcionando agora. Apenas certifique-se de ter ETH suficiente na rede Goerli.
Se você precisar de mais ETH de teste, vá até uma torneira Goerli como a da Alchemy. Além disso, lembre-se de que pode demorar um pouco para que as três transações sejam mineradas, e que não temos nenhum feedback do usuário sobre o status atual da transação no momento.
Exibir o saldo atual de ETH e UNI
Para ver diretamente quanto ETH você pode trocar e obter algum tipo de feedback sobre o resultado da troca, seria bom exibir o saldo atual. Felizmente, com o wagmi.sh, é incrivelmente fácil fazer isso.
Adicione esta constante ao topo do arquivo SwapCard.tsx
:
const UNI_ADDRESS = '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984';
Em seguida, adicione isso abaixo do hook useAccount
:
const { data: ETHBalance } = useBalance({
address,
watch: true,
});
const { data: UNIBalance } = useBalance({
address,
token: UNI_ADDRESS,
watch: true,
});
A propriedade watch
garante que nosso saldo esteja sempre atualizado, verificando se o saldo foi alterado após cada novo bloco. Agora, por último, mas não menos importante, adicionamos uma pequena nota no modelo para ambos os tokens como este para ETH:
<p>Balance: {ETHBalance?.formatted}</p>
Agora, reunindo todas as coisas, seu arquivo deve se parecer um pouco com isso:
import React from 'react';
import { useAccount, useBalance } from 'wagmi';
import useSwap from '../hooks/useSwap';
const UNI_ADDRESS = '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984';
const SwapCard = () => {
const [quote, setQuote] = React.useState(0);
const [amount, setAmount] = React.useState(0);
const { address } = useAccount();
const { data: ETHBalance } = useBalance({
address,
watch: true,
});
const { data: UNIBalance } = useBalance({
address,
token: UNI_ADDRESS,
watch: true,
});
const { getQuote, swap } = useSwap();
const onChangeAmountInput = async (
event: React.ChangeEvent<HTMLInputElement>
) => {
setAmount(parseFloat(event.target.value));
const quote = await getQuote(parseFloat(event.target.value));
setQuote(quote);
};
const onClickSwapButton = async () => {
const txn = await swap(amount);
await txn.wait();
};
return (
<>
<input
type="text"
placeholder="Amount in"
onChange={onChangeAmountInput}
/>
<p>ETH</p>
<p>Balance: {ETHBalance?.formatted}</p>
<input
type="text"
placeholder="Amount out"
disabled
value={quote === 0 ? '' : quote}
/>
<p>UNI</p>
<p>Balance: {UNIBalance?.formatted}</p>
<button disabled={address ? false : true} onClick={onClickSwapButton}>
Swap
</button>
</>
);
};
export default SwapCard;
Nosso DApp sem estilo agora deve ficar assim:
Se você tentar trocar tokens agora, verá que, após um swap bem-sucedido, o saldo de ambos os tokens muda adequadamente.
. . .
Parabéns!!!
Você construiu com sucesso um dApp que permite trocar seu ETH por UNI. Com base nisso, você pode torná-lo mais bonito, adicionar validação, feedback do usuário, mais tokens etc. As possibilidades são infinitas.
Este tutorial focou apenas na lógica por trás da troca de tokens. Sem o CSS, parece sem graça, e foi por isso que adicionei estilos para o dApp que construímos neste tutorial.
Lógica tutorial com estilos adicionais ( verifique os links abaixo )
Links
Se você quiser conferir o código inteiro, siga em frente aqui.
Se você quiser conferir o código inteiro, incluindo estilos e mais algumas adições e ajustes, siga em frente aqui.
Se você deseja obter uma demonstração do dApp, incluindo estilos e mais algumas adições e ajustes: implantei-o no Vercel aqui.
Obrigado pela leitura!
O feedback é sempre apreciado!
Este artigo foi escrito por Jonas Wolfram e traduzido por Adriano P. de Araujo. O original em inglês pode ser encontrado aqui.
Oldest comments (0)