Get ETH Balance with Ethersjs - reactjs

I'm trying to get the balance of my wallet address to render on my frontend. Here's what I have so far.
const [balance, setBalance] = useState("");
const handleWalletBalance = async () => {
const { ethereum } = window;
if(ethereum) {
const balance = await ethereum.request({method: 'eth_getBalance'})
const provider = new ethers.providers.Web3Provider(ethereum)
await provider.getBalance(balance)
setBalance(balance)
console.log(balance)
}
}
The error I'm getting is MetaMask - RPC Error: missing value for required argument 0 .
I'm using a method for querying accounts. What am I missing?

You can use:
const getBalance = async (address) => {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const balance = await provider.getBalance(address);
const balanceInEth = ethers.utils.formatEther(balance);
console.log(balanceInEth);
}
You set the provider, then you ask for balance through the provider (account must be logged in), and finally you format the result of BigNumber to Eth.

I think that you need to specify the account you want to use from your MetaMask. You can do it with the following code.
const provider = new ethers.providers.Web3Provider(window.ethereum, "any");
const accounts = await provider.send("eth_requestAccounts", []);
Then, to check the balance, you don't need MetaMask, you can do it with the following code.
const balance = await provider.getBalance(accounts[0])
ethers.utils.formatEther(balance)

Related

Runtime error after page refresh when doing an api call

I have a web3 application that I am trying to view NFTs on a certain page and everything works when I route to it via links, or go back. But on page refresh I receive a runtime error that seems to indicate that there is improper data being passed but all objects being passed are defined properly
const { account } = useAccount();
const [NFTList, setNFTList] = useState([])
const [imageList, setImageList] = useState([])
const [tokenIDList, setTokenIDList] = useState([])
ConnectContract()
const getOwnedNFTs = useCallback(async () => {
const web3 = createAlchemyWeb3(
`https://polygon-mumbai.g.alchemy.com/v2/${process.env.NEXT_PUBLIC_ALCH_KEY}`,
);
const nfts = await web3.alchemy.getNfts({owner: account.data, contractAddresses: [ContractAddress]})
for (let i = 0; i < nfts.totalCount; i++) {
const response = await web3.alchemy.getNftMetadata({
contractAddress: ContractAddress,
tokenId: nfts.ownedNfts[i].id.tokenId
})
const image = await GetTokenImage(nfts.ownedNfts[i].id.tokenId)
setImageList(imageList => [...imageList, image])
setNFTList(NFTList => [...NFTList, response.metadata])
setTokenIDList(tokenIDList => [...tokenIDList, nfts.ownedNfts[i].id.tokenId])
}
},[])
useEffect(() => {
getOwnedNFTs()
},[])
Error:
I narrowed down the problem to the nfts line. If I hard code an address for owner then refresh works fine
const nfts = await web3.alchemy.getNfts({owner: "", contractAddresses: [ContractAddress]})
I don't really understand why this is happening as account.data is properly defined after the page is refreshed
UPDATE: I didn't see it before but the GET request is returning 400 error The request for some reason is leaving out the owner key/value from the api request
https://polygon-mumbai.g.alchemy.com/<API_KEY>/v1/getNFTs/?contractAddresses%5B%5D=<CONTRACT_ADDRESS>

How to display custom Nextjs error page when api call fails?

I created a custom Nextjs error page that I would like to display when the api call fails. What is currently happening is even if the api call fails, it still displays the same page as a successful route. For example, I have a route that is companies/neimans that pulls data from an api to display certain text on the page. If I type, companies/neiman I want my custom error page to show, but it is displaying the same page as if going to companies/neimans just without the data coming from the api. I do get a 404 in the console when visiting a url that is invalid but it doesn't display the custom error page or the default next js 404 page.
In my file system I have a pages directory and inside that a directory called companies with a file [companydata].tsx and one called 404.tsx. [companydata].tsx is the page that dynamically displays information about the company from the api.
This is what my api call currently looks like:
export const getCompanies = async (routeData: string): Promise<Company> => {
const client = getApiClient();
const response = await client.get<Company>(`api/companies/${routeData}`);
if (response) {
return response.data;
}
return {} as Company;
In the [companydata].tsx, I tried to do a check if the object was empty to then redirect to companies/404 but doing so makes it always display the 404 page.
if (Object.keys(company).length === 0) {
return <Redirect to="/company/404"/>;
}
If I console.log the company, it is rendering multiple times. The first 6 times, it is an empty array so that would explain why the 404 page is always showing. The data doesn't come through until after the 6th render. I am not sure why that is.
I am calling getCompanies inside another function,
export const getData = async (companyName: string): Promise<[Company, Sales]> => {
if (companyName) {
return (await Promise.all([getCompanies(companyName), getSales()])) as [
Company,
Sales
];
}
return [{} as Company, {} as Sales];
};
I am calling getData inside a useEffect within [companydata].tsx.
const Company: NextPage = (): JSX.Element => {
const [selectedCompany, setSelectedCompany] = useState<Company>({} as Company);
const [salesAvailable, setSalesAvailable] = useState<boolean>(false);
const [sales, setSales] = useState<Sales>({} as Sales);
const router = useRouter();
const {companydata} = router.query;
useEffect(() => {
const init = async (companyName: string) => {
const [companyData, salesData] = await getData(companyName);
if (companyData) {
setSelectedCompany(companyData);
}
if (salesData) {
setSalesAvailable(true);
setSales(salesData);
} else {
setSalesAvailable(false);
}
}
};
init(companydata as string);
};
}, [companydata]);
// returning company page here
You currently do not have a method to check the status of the API call. There are four possible outcomes of most API calls - data, no data, error, and loading. You should add the status checks in your API calls
Below are two examples of how this can be achieved.
get companies hook
export const useGetCompanies = async (path: string) => {
const [data, setData] = useState<Company>();
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
try {
setError(false);
setLoading(true);
const client = getApiClient();
const response = await client.get(`api/companies/${path}`);
setData(response.data);
} catch (error) {
setError(true);
} finally {
setLoading(false);
}
return {data, error, loading};
};
Since your data isn't related you also do a generic API fetch call something like
export async function useFetchData<T>(path:string){
const [data, setData] = useState<T>();
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
try {
setError(false);
setLoading(true);
const client = getAPIClient();
const response = await client.get<{ data: T }>(path);
if(response) setData(response.data);
} catch (error) {
setError(true);
} finally {
setLoading(false);
}
return { data, error, loading };
};
Example use.
const Company = async () =>{
const { query } = useRouter();
const company = await useFetchData<Company>(`api/companies/${query.companydata}`);
const sales = await useFetchData<Sales>(`api/companies/${query.companydata}/sales`);
if (company.loading || sales.loading) return <p>Loading...</p>;
if (company.error || sales.error) return <p>Error or could show a not found</p>;
if (!company.data || !sales.data) return <Redirect to="/company/404"/>;
return "your page";
}
It would be best to render the data independently of each other on the page and do the if checks there. This is beneficial because you don't have to wait for both calls to complete before showing the page.
I'd create two separate components (company and sales) and place the corresponding API call in each.
Typically assigning empty objects ({} as Company or {} as Sales) to defined types is bad practice because it makes TS think the object's values are defined when they are not - defeating the purpose of using TS.
They should be left undefined, and there should be a check to see if they are defined.
Lastly, I can't test the code because I don't have access to the original code base so there might be bugs, but you should get a pretty good idea of what's happening.

hardhat tasks don't persist data on local network

I create a little NFT marketplace using solidity and hardhat. I have a JSON file with the NFT details and I wrote a hardhat task for automating the process.
task("populate-market", "populate market with nfts").setAction(async function (
taskArguments,
hre
) {
const [owner] = await hre.ethers.getSigners();
const Market = await hre.ethers.getContractFactory("NFTMarket");
const market = await Market.deploy(owner.address);
await market.deployed();
const marketAddress = market.address;
/* deploy the NFT contract */
const Item = await hre.ethers.getContractFactory("Item");
const nft = await Item.deploy(marketAddress);
await nft.deployed();
for (const item of nfts) {
const transaction = await nft.createToken(item.url);
const tx = await transaction.wait();
const event = tx.events[0];
const tokenId = event.args[2].toNumber();
const price = hre.ethers.utils.parseUnits(item.price.toString(), "ether");
await market.createMarketItem(nft.address, tokenId, price, item.supply);
}
console.log(await market.fetchMarketItems());
console.log("done!");
});
the problem it's when I load the data in my react app; I created an function getNfts() like this:
useEffect(() => getNfts(), []);
const getNfts = async () => {
const provider = new ethers.providers.JsonRpcProvider();
const nftContract = new ethers.Contract(NFT_ADDRESS, NFT.abi, provider);
const marketContract = new ethers.Contract(
MARKET_ADDRESS,
Market.abi,
provider
);
const data = await marketContract.fetchMarketItems();
console.log(data);
};
in this function data it returns empty array but, in task the console.log(await market.fetchMarketItems()); it returns all nfts. I don't understand why in the task it returns data but, in react it shows me an empty array. How to fix this?
Try to do this:
async function getNfts() {
// The rest of the code goes here
}
Basically use a function instead a variable asigned to a function. Sometimes there are errors with the this keyword that are not being passed correctly and it breaks the functionality of the library.

How to Use BUSD coin when we buy the NFT In React.js with Binance Block chain

Hi we want to create a NFT market place.my project almost done but currently we are using BNB for the purchase NFT and also Gas fee. but I want to change Currency Type BUSD
const web3Modal = new Web3Modal();
const connection = await web3Modal.connect();
const provider = new ethers.providers.Web3Provider(connection);
const signer = provider.getSigner();
/* next, create the item */
const contract = new ethers.Contract(nftaddress, NFT.abi, signer);
const price = ethers.utils.parseUnits("0.1", "ether");
// console.log(provider);
const transaction = await contract.mint(url);
const tx = await transaction.wait();
// console.log(tx);
const _event = tx.events[0];
const value = _event?.args[2];
const tokenId = value.toNumber();
// console.log(_event);
const tokenURI = await contract.tokenURI(tokenId)

web3 react contract interaction

I am having some issues when calling a contract via form with React and Web3 component.
In my contract, I have a public function called GetDomainInfo which takes the domain as a parameter. You can view the contract here: https://ropsten.etherscan.io/address/0x756ad7f7c22e7c04a845bd8a138e455a1bc95f6f
The problem is that my components gets the form value, but when use it in a contract, gives the error:
Uncaught TypeError: Cannot read property 'GetDomainInfo' of undefined
Code:
GetInfo = (event) => {
event.preventDefault();
console.log(this.state.value);
const response = this.state.rouDomains.method.GetDomainInfo(this.state.value).send({from: this.state.account})
.once('receipt', (receipt) => {
console.log("receipt: ", receipt);
console.log(response);
})
}
The data arrives in console.log(this.state.value), I can see it.
EDIT: rouDomains is the async load data from web3.
async loadData() {
const web3 = new Web3(Web3.givenProvider || "http://localhost:8545");
const network = await web3.eth.net.getNetworkType();
this.setState({network});
//Fetch account
const accounts = await web3.eth.getAccounts();
this.setState({ account: accounts[0]});
//Load the contract
const rouDomains = new web3.eth.Contract(ROU_TLD, ROU_TLD_ADDRESS);
this.setState({ rouDomains });
console.log("ROU: ", rouDomains);
}
The answer was that I forgot a 's' for method in the below:
const response = this.state.rouDomains.method.GetDomainInfo(this.state.value).send({from: this.state.account})
const response = this.state.rouDomains.methods.GetDomainInfo(this.state.value).send({from: this.state.account})
Stupid rookie mistake :)

Resources