Interact with smart contracts
Interact with smart contracts in your Wagmi or Vanilla JavaScript dapp. With the SDK, you can:
- Read data from smart contracts.
- Write data to smart contracts.
- Handle contract events.
- Manage transaction states.
- Handle contract errors.
Use Wagmi
Wagmi provides dedicated hooks for smart contract interactions. The following are examples of using these hooks.
Read contract data:
import { useReadContract } from "wagmi"
function TokenBalance() {
const {
data: balance,
isError,
isLoading
} = useReadContract({
address: "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
abi: [
{
name: "balanceOf",
type: "function",
stateMutability: "view",
inputs: [{ name: "owner", type: "address" }],
outputs: [{ name: "balance", type: "uint256" }],
},
],
functionName: "balanceOf",
args: ["0x03A71968491d55603FFe1b11A9e23eF013f75bCF"],
})
if (isLoading) return <div>Loading balance...</div>
if (isError) return <div>Error fetching balance</div>
return <div>Balance: {balance?.toString()}</div>
}
Write to contracts:
import { useWriteContract, useWaitForTransactionReceipt } from "wagmi"
function MintNFT() {
const {
writeContract,
data: hash,
error,
isPending
} = useWriteContract()
const {
isLoading: isConfirming,
isSuccess: isConfirmed
} = useWaitForTransactionReceipt({
hash
})
function mint() {
writeContract({
address: "0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2",
abi: [
{
name: "mint",
type: "function",
stateMutability: "nonpayable",
inputs: [{ name: "tokenId", type: "uint256" }],
outputs: [],
},
],
functionName: "mint",
args: [123n], // Token ID
})
}
return (
<div>
<button
onClick={mint}
disabled={isPending || isConfirming}
>
{isPending ? "Confirming..." : "Mint NFT"}
</button>
{hash && (
<div>
Transaction Hash: {hash}
{isConfirming && <div>Waiting for confirmation...</div>}
{isConfirmed && <div>NFT Minted Successfully!</div>}
</div>
)}
{error && <div>Error: {error.message}</div>}
</div>
)
}
Use Vanilla JavaScript
You can implement smart contract interactions directly in Vanilla JavaScript.
For example, read contract data:
async function getBalance(contractAddress, userAddress) {
try {
// Create function signature for balanceOf(address)
const functionSignature = "0x70a08231";
// Pad address to 32 bytes
const encodedAddress = userAddress.slice(2).padStart(64, "0");
const result = await ethereum.request({
method: "eth_call",
params: [{
to: contractAddress,
data: functionSignature + encodedAddress,
}],
});
return BigInt(result);
} catch (error) {
console.error("Error reading balance:", error);
throw error;
}
}
// Example usage
async function displayBalance() {
const status = document.getElementById("status");
try {
const balance = await getBalance(
"0xContractAddress",
"0xUserAddress"
);
status.textContent = `Balance: ${balance.toString()}`;
} catch (error) {
status.textContent = `Error: ${error.message}`;
}
}
Write to contracts:
async function mintNFT(contractAddress, tokenId) {
try {
// Get user"s account
const accounts = await ethereum.request({
method: "eth_requestAccounts"
});
// Create function signature for mint(uint256)
const functionSignature = "0x6a627842";
// Pad tokenId to 32 bytes
const encodedTokenId = tokenId.toString(16).padStart(64, "0");
// Send transaction
const txHash = await ethereum.request({
method: "eth_sendTransaction",
params: [{
from: accounts[0],
to: contractAddress,
data: functionSignature + encodedTokenId,
}],
});
return txHash;
} catch (error) {
if (error.code === 4001) {
throw new Error("Transaction rejected by user");
}
throw error;
}
}
// Track transaction status
async function watchTransaction(txHash) {
return new Promise((resolve, reject) => {
const checkTransaction = async () => {
try {
const tx = await ethereum.request({
method: "eth_getTransactionReceipt",
params: [txHash],
});
if (tx) {
if (tx.status === "0x1") {
resolve(tx);
} else {
reject(new Error("Transaction failed"));
}
} else {
setTimeout(checkTransaction, 2000);
}
} catch (error) {
reject(error);
}
};
checkTransaction();
});
}
The following is an example implementation of contract interaction:
<div class="contract-interaction">
<button onclick="handleMint()">Mint NFT</button>
<div id="status"></div>
</div>
<script>
async function handleMint() {
const status = document.getElementById("status");
try {
status.textContent = "Sending transaction...";
const txHash = await mintNFT("0xContractAddress", 123);
status.textContent = `Transaction sent: ${txHash}`;
status.textContent = "Waiting for confirmation...";
await watchTransaction(txHash);
status.textContent = "NFT Minted Successfully!";
} catch (error) {
status.textContent = `Error: ${error.message}`;
}
}
</script>
info
See the Provider API reference and JSON-RPC API reference for more information.
Best practices
Follow these best practices when interacting with smart contracts.
Contract validation
- Always verify contract addresses.
- Double check ABI correctness.
- Validate input data before sending.
- Use typed data when possible (for example, using Viem).
Error handling
- Handle common errors like user rejection and contract reverts.
- Provide clear error messages to users.
- Implement proper error recovery flows.
- Consider gas estimation failures.
User experience
- Show clear loading states.
- Display transaction progress.
- Provide confirmation feedback.
- Enable proper error recovery.
Common errors
Error code | Description | Solution |
---|---|---|
4001 | User rejected transaction | Show a retry option and a clear error message. |
-32000 | Invalid input | Validate the input data before sending. |
-32603 | Contract execution reverted | Check the contract conditions and handle the error gracefully. |
-32002 | Request already pending | Prevent multiple concurrent transactions. |
Next steps
See the following guides to add more functionality to your dapp: