I am having the same issue.
Hey @PatrickAlphaC @DappThatApp, you can use onSuccess
callback to listen for events from the tx
object (same value populated in data
), which will trigger once the transaction is completed
useWeb3Contract({
// other options
onSuccess: (tx) => {},
});
Another way to listen for events with Moralis is to use Event Syncing which will capture all emitted events real-time and populate them to the Database table, which then can be listened with useMoralisSubscription
whenever new rows is added
To clarify, isFetching
is to indicate whether the transaction is still running or not, and isLoading
is basically checking isFetching && data === null
(thereās more details).
Cheers~
This looks promising. I want to try this in my code. However, in my solidity code, i am trying to create an erc721 token, which requires metamask signing for the transaction. I am having problems getting the metamask wallet to sign the transaction as part of the call to useWeb3Contract.
More specifically, how do I get metamask to open when I call runContractFunction?
I think that you have to use enableWeb3 before that, and metamsk should pop up automatically
Currently, Im using the function below to mint nftās and get the confirmed transaction response. It works, and I get the token Id that I need, but I was wanting to use the Moralis apiās so i can get used to them.
This code launches metamask and waits for the confirmed response. How would i do this using useWeb3Contract?
const mintNft = async () => {
const ethers = Moralis.web3Library
const web3 = await Moralis.enableWeb3({ provider: "web3Auth", chainId: '0x3', clientId:
"redacted" });
const signer = web3.getSigner()
const contract = new ethers.Contract(TOKEN_CONTRACT_ADDRESS, abi, signer)
const transaction = await contract.createItem(uri)
const tx = await transaction.wait()
const event = tx.events[0]
const value = event.args[2]
const tokenId = value.toNumber()
return tokenId
}
this may be a case that is not handled now by Moralis.executeFunction, you could try with Moralis.executeFunction to see if it works
I see. So, useWeb3Contract doesnt support function calls that require a signed wallet transaction?
I just want to make sure I understand correctly. Thanks for your help
Amazing. Iāll try this out this week. Thanks!
@DappThatApp @PatrickAlphaC on a related note, are you using event listeners to check if user is on the wrong chain etc?
I successfully handled most things like change in account and a case of manual disconnecting metamask from the dapp by using {account} = useMoralis();
which causes re-render anytime account
is changed.
But I canāt seem to figure out the onChainChanged
event listener. If you can copy paste that bit of your code it will help.
hey @zubin. the way to handle chain changes you can request metamask to handle chain changes whenever the user clicks on some button to change which uses a metamask request option to change chain by triggering a popup forcing the user to change. on the other hand if a user just decides to change chain then you can use an onChainChange event handler to trigger the popup automatically with useEffect also. the code below shows an example of how to do this for the example of ethereum, ploygon and binance smart chain. this is even more powerful implemnted with web3react. or mroalis if you desire
import { useState, useEffect } from "react";
const networks = {
polygon: {
chainId: `0x${Number(137).toString(16)}`,
chainName: "Polygon Mainnet",
nativeCurrency: {
name: "MATIC",
symbol: "MATIC",
decimals: 18
},
rpcUrls: ["https://polygon-rpc.com/"],
blockExplorerUrls: ["https://polygonscan.com/"]
},
bsc: {
chainId: `0x${Number(56).toString(16)}`,
chainName: "Binance Smart Chain Mainnet",
nativeCurrency: {
name: "Binance Chain Native Token",
symbol: "BNB",
decimals: 18
},
rpcUrls: ["https://bsc-dataseed1.binance.org"],
blockExplorerUrls: ["https://bscscan.com"]
}
};
const changeNetwork = async ({ networkName, setError }) => {
try {
if (!window.ethereum) throw new Error("No crypto wallet found");
await window.ethereum.request({
method: "wallet_addEthereumChain",
params: [
{
...networks[networkName]
}
]
});
} catch (err) {
setError(err.message);
}
};
export default function App() {
const [error, setError] = useState();
const handleNetworkSwitch = async (networkName) => {
setError();
await changeNetwork({ networkName, setError });
};
const networkChanged = (chainId) => {
console.log({ chainId });
};
useEffect(() => {
window.ethereum.on("chainChanged", networkChanged);
return () => {
window.ethereum.removeListener("chainChanged", networkChanged);
};
}, []);
return (
<div className="credit-card w-full lg:w-1/2 sm:w-auto shadow-lg mx-auto rounded-xl bg-white">
<main className="mt-4 p-4">
<h1 className="text-xl font-semibold text-gray-700 text-center">
Force MetaMask network
</h1>
<div className="mt-4">
<button
onClick={() => handleNetworkSwitch("polygon")}
className="mt-2 mb-2 btn btn-primary submit-button focus:ring focus:outline-none w-full"
>
Switch to Polygon
</button>
<button
onClick={() => handleNetworkSwitch("bsc")}
className="mt-2 mb-2 bg-warning border-warning btn submit-button focus:ring focus:outline-none w-full"
>
Switch to BSC
</button>
</div>
</main>
</div>
);
}
the above example is basic and only supports thos two chains. to make it more robust you should integrate this web3react or moralis (which you are jsing) and use this hook to get the current chain. something like
const { chainId } = useWebReact()
or for your case
const { cahinId } = useMoralis
if you then, in the useffect with the chainChain event listener add the dependancy of the chainId then evrry time you switch networks the popup will automatically show, that is if you call the handleNetworkChange function inside the useEffect.
@mcgrane5 Hereās what I did:
- In my App.js I added an
if statement
to an existing useEffect in order to change the network immediately on Metamask login.
useEffect(() => {
const connectorId = window.localStorage.getItem("connectorId");
if (isAuthenticated && !isWeb3Enabled && !isWeb3EnableLoading){
enableWeb3({ provider: connectorId });}
if (chainId != '0xa86a') {
switchNetwork("0xa86a")
}
So far so good.
2. In order to catch the edge case of user absent mindedly switching network again after login (perhaps while working on another tab with Ethereum), I added a new useEffect with an event listener as you suggested.
useEffect(()=>{
if(chainId != '0xa86a'){
const unsubscribed = Moralis.onChainChanged((chain)=>{
if (window.confirm(`You're on ${chain}! Click OK to switch to Avalanche!`)) {
switchNetwork("0xa86a")
} else {
alert("You're on the wrong network & will result in loss of funds due to failed transaction! Switch to Avalance C-chain manually in your Metamask Wallet!");
}
unsubscribed();
}); }
}, [chain])
Trouble is, this extra useEffect keeps getting triggered even if I am already on Avax. Iāve tried all kinds of permutations with the If statement. Any idea what Iām doing wrong?
Edit:
I solved it by removing the above useEffect
in #2 and including chain
in the array of the original useEffect
.
useEffect(() => {
const connectorId = window.localStorage.getItem("connectorId");
if (isAuthenticated && !isWeb3Enabled && !isWeb3EnableLoading){
enableWeb3({ provider: connectorId });}
if (chainId != '0xa86a') {
if (window.confirm(`You're on the wrong network! Click OK to switch to Avalanche C-chain!`)) {
switchNetwork("0xa86a")
} else {
alert("You're on the wrong network & will result in loss of funds due to failed transaction! Switch to Avalance C-chain manually in your Metamask Wallet!");
}
}
}, [isAuthenticated, isWeb3Enabled, chain]);
This way if user is not on Avax it prompts and then changes the chain to Avax.
useWeb3Contract({
// other options
onSuccess: (tx) => {},
});
This fires the moment you hit the confirm button on Metamask. Itās pretty much useless if you are trying to use it as a callback for when a tx has been confirmed successfully.
Hi, Patrick please tell me you tried this or found another way?
I cannot for the life of me figure out how to listen or track a successful tx confirmation using react-moralis.
Iām using the useWeb3ExecuteFunction hook to mint my nfts but no way to figure out whether it got successfully minted.
Yep! I found out you have to add the onSuccess
when you call the function. For example, if this is where you define your function:
const {
runContractFunction: enterRaffle,
data: enterTxResponse,
isLoading,
isFetching,
} = useWeb3Contract({
abi: abi,
contractAddress: raffleAddress,
functionName: "enterRaffle",
msgValue: entranceFee,
params: {},
})
Then youād add your success function like so:
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded ml-auto"
onClick={async () =>
await enterRaffle({
onSuccess: handleSuccess,
})
}
disabled={isLoading || isFetching}
>
And here is my func:
const handleSuccess = async (tx) => {
await tx.wait(1)
updateUIValues()
handleNewNotification(tx)
}
Wow dude, it works perfectly. Thank you very much!
Thanks to the Moralis team for helping me find the answer!!!
@orangemat enterRaffle
is the contract function name right? Shouldnāt it be async()=> await fetch and not enterRaffle? Unless youāve declared fetch: enterRaffle in the useWebExecuteFunction?
Fetch is equivalent to ārunContractFunctionā here. Also, you need to use the useWeb3Contract hook instead of the useWeb3ExecuteFunction one.
Both are basically the same. UseWeb3ExecuteFunction is working well for me. Itās just changing/deactivating the button after confirmation of txns that is the problem. And keeping it deactivated on a page refresh. Difference between `useWeb3ExecuteFunction`, `useApiContract`, `executeFunction`, `useWeb3Contract`, and `Moralis.executeFunction`