I am currently trying to write a dapp from scratch, using Dapp University's EthSwap (https://github.com/dappuniversity/eth_swap) as a model (Basically trying to recreate this dapp without looking at his code). Basically, the dapp just allows you to buy erc20 tokens. It does seem to work, but it is really slow. I assume this might be a problem with react, as I am currently new to the framework and might have fallen into some performance issues. I tried the following to make sure to eliminate other causes:
- Change browsers (Chrome --> Brave) and installed and used metamask on both
- Switched over to ganache-cli from ganache gui
- I treated the connection to web3 as a separate component and imported it, but in the end just put this code in App.js
Here is my code:
// App.js
import './App.css';
import PageNavbar from './components/PageNavbar';
import TokenExchangeForm from './components/TokenExchangeForm';
import Web3 from 'web3';
import { useContext, useState, useEffect } from 'react';
import GenericToken from './abis/GenericToken.json';
import TokenExchange from './abis/TokenExchange.json';
function App() {
const [account, setAccount] = useState('');
const [ethBalance, setEthBalance] = useState('');
const [tokenBalance, setTokenBalance] = useState('');
const [genericToken, setGenericToken] = useState(null);
const [tokenExchange, setTokenExchange] = useState(null);
useEffect(() => {
const init = async () => {
await getWeb3();
await getBlockchainData();
}
init();
});
const getBlockchainData = async () => {
const web3 = window.web3
let retrievedAccounts = await web3.eth.getAccounts();
setAccount(retrievedAccounts[0]);
let ethBalance = await web3.eth.getBalance(retrievedAccounts[0]);
setEthBalance(web3.utils.fromWei(ethBalance.toString(), 'Ether'));
let networkId = await web3.eth.net.getId()
let genericTokenData = GenericToken.networks[networkId];
let tokenExchangeData = TokenExchange.networks[networkId];
let genericToken = new web3.eth.Contract(GenericToken.abi, genericTokenData.address);
setGenericToken(genericToken);
let tokenExchange = new web3.eth.Contract(TokenExchange.abi, tokenExchangeData.address);
setTokenExchange(tokenExchange);
let tokenBalance = await genericToken.methods.balanceOf(retrievedAccounts[0]).call();
setTokenBalance(web3.utils.fromWei(tokenBalance.toString(), 'Ether'));
}
const getWeb3 = async () => {
if (window.ethereum) {
window.web3 = new Web3(window.ethereum);
try {
await window.ethereum.request({ method: 'eth_requestAccounts' });
} catch (err) {
console.log('Transaction rejected by user:', err)
}
}
else if (window.web3) {
window.web3 = new Web3(window.web3.currentProvider);
}
else {
console.log('Non-Ethereum browser detected. You should consider trying MetaMask!');
}
}
let buy = (etherAmount) => {
//const gasEstimate = await tokenExchange.methods.buy().estimateGas({ from: account, value: window.web3.utils.toWei(etherAmount, 'ether') });
//const gasPriceEstimate = await window.web3.eth.getGasPrice();
tokenExchange.methods.buy().send({ from: account, value: window.web3.utils.toWei(etherAmount, 'Ether')});
}
return (
<div className="App">
<PageNavbar title='Token Exchange' account={account}/>
<TokenExchangeForm ethBalance={ethBalance} tokenBalance={tokenBalance} buy={buy}></TokenExchangeForm>
</div>
);
}
export default App;
// TokenExchangeForm.js
import Card from 'react-bootstrap/Card';
import Container from 'react-bootstrap/esm/Container';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import { useState } from 'react';
import ExchangeField from './ExchangeField';
const TokenExchangeForm = (props) => {
const [etherValue, setEtherValue] = useState('');
const [tokenValue, setTokenValue] = useState('');
const changeHandlerEth = (event) => {
setEtherValue(event.target.value);
}
const changeHandlerToken = (event) => {
setTokenValue(window.web3.utils.fromWei(etherValue, 'Ether'));
}
const submitHandlerBuy = (event) => {
event.preventDefault();
props.buy(etherValue);
}
return(
<Container>
<Card id="centered-form">
<Form onSubmit={submitHandlerBuy}>
<Container>
<br />
<ExchangeField label ='Input' balance={props.ethBalance} value={props.etherValue} onChange={changeHandlerEth} placeholder='0' appendtext ="ETH"/>
<br/>
<ExchangeField label='Output' balance={props.tokenBalance} value={props.tokenValue} onChange={changeHandlerToken} placeholder='0' appendtext="GT"/>
<br />
<Form.Row>
<Form.Label>Exchange Rate</Form.Label>
<Form.Label className='add-space'>1 ETH = 100 GT</Form.Label>
</Form.Row>
<br />
<Button type='submit' variant="primary" size="lg" className ="w-100" block>
SWAP
</Button>
</Container>
</Form>
</Card>
</Container>
);
}
export default TokenExchangeForm;
I fixed my problem. It turns out that useEffect keeps running after every render, and so it kept reloading web3 constantly, slowing the application. You can prevent this by adding an empty array in useEffect:
This allows useEffect to run only when the component was mounted. Check this post to learn more: https://stackoverflow.com/a/53464623/4421062