wagmi-extended
is a powerful library that builds on Wagmi, Viem, and TanStack Query to deliver extended hooks and helper functions for Ethereum and blockchain development with React.
It simplifies tasks like fetching token metadata, handling ERC20 approvals, sending transactions, writing contracts and getting user friendly error message from ABIs.
Our library adheres to one core principle: always wait for a transaction receipt. This guarantees that your dApps capture transaction outcomes reliably—no manual state management or race conditions required.
Whether you're building a DeFi platform, a governance system, or any blockchain solution, wagmi-extended
offers a consistent, reliable, and developer-friendly interface, trusted in production with over $500M in volume.
- Installation
- Requirements
- Playground
- API
- Usage
- Non-hook functions setup
- Contributing
- Donations
- License
Install via npm:
npm install wagmi-extended
Or with Yarn:
yarn add wagmi-extended
Your project must include the following peer dependencies:
- React: ^17.0.0 || ^18.0.0 || ^19.0.0
- Wagmi: ^2.0.0
- Viem: ^2.0.0
- @tanstack/react-query: ^5.0.0
Note: : You must wrap your application with necessary providers (e.g. QueryClientProvider from TanStack Query and WagmiProvider).
Compare wagmi-extended with standard wagmi implementations in our interactive CodeSandbox project. View the CodeSandbox Project to explore its features and see how it works in practice!
You can import all the functions and hooks directly from wagmi-extended
. For example:
import {
useTokenX,
useERC20ApproveX,
useSendTransactionX,
useWriteTransactionX,
setDefaults,
getDefaults,
} from "wagmi-extended";
Each hook is documented with detailed JSDoc comments (including usage examples) in the source code. Refer to that documentation for additional details.
The useContractWriteX
hook wraps Wagmi’s useWriteContract
with extra features:
- Receipt waiting: always waits for the transaction receipt before resolving.
- Logging control: disable console logging if you wish.
- Query invalidation: automatically invalidates queries after receipt, either by explicit keys or by predicate.
const { writeContractAsync, isPending, errorMessage } = useContractWriteX({
queriesToInvalidate: [["userBalance"], ["userActivity"]],
onSuccess: (txHash) => console.log("✅", txHash),
onError: (err) => console.error("❌", err),
});
// will wait for the receipt, then invalidate `userBalance` & `userActivity`
await writeContractAsync({
address: "0xContractAddress",
abi: MyContractAbi,
functionName: "executeFunction",
args: [
/* ... */
],
});
Use writeContractX
if you need control over the simulation step:
const { writeContractX, isPending, errorMessage } = useContractWriteX({
onSuccess: (tx) => console.log("✔ Receipt confirmed:", tx),
onError: (e) => console.error("✖ Failed:", e),
});
// simulate + send:
await writeContractX(
{
address: "0xContractAddress",
abi: MyContractAbi,
functionName: "executeFunction",
args: [
/* ... */
],
account: myAddress,
chain: myChain,
value: 0n,
},
/* disableSimulation? */ false
);
// send immediately without simulation:
await writeContractX(params, /* disableSimulation= */ true);
writeContractAsync
= always runs the built-in dry-run, then write.writeContractX
= you can pass a boolean to skip the simulation step.
The useSendTransactionX
hook wraps Wagmi’s useSendTransaction
with the same lifecycle features:
sendTransaction
= Wagmi’s raw sendsendTransactionX
= optionally simulate (contract call) or justeth_call
, thensendTransaction
+ receipt waiting + invalidations.
const {
sendTransaction, // raw
sendTransactionX, // wrapped
isPending,
errorMessage,
} = useSendTransactionX({
queriesToInvalidate: [["ethBalance"]],
onSuccess: (tx) => console.log("🎉 Tx sent & confirmed:", tx),
onError: (e) => console.error("🚫 Simulation or send failed:", e),
});
// simulate & send an ETH transfer:
await sendTransactionX(
{ to: recipient, value: 1n * 10n ** 18n, account: myAddress, chain: myChain },
// for contract calls, pass simulation params:
{ abi: MyAbi, functionName: "deposit", args: [1000n], chain: myChain }
);
// or just raw send (no simulationParams):
await sendTransactionX({ to, value, account });
In all “X” hooks you pass a WriteExtendedAsyncParams
object:
export type WriteExtendedAsyncParams = {
onSuccess?: (txHash: Address) => void;
onError?: (e: any) => void;
onSettled?: () => void;
onSuccessAsync?: (txHash: Address) => Promise<void>;
onErrorAsync?: (e: any) => Promise<void>;
onSettledAsync?: () => Promise<void>;
/** simple list of query keys to invalidate after receipt */
queriesToInvalidate?: (QueryKey | undefined)[];
/** predicate-based invalidation:
any active query where `predicate(query)` returns true
will be invalidated after the tx settles. */
invalidatePredicate?: (query: Query<unknown, unknown>) => boolean;
disableLogging?: boolean;
disableWaitingForReceipt?: boolean;
};
Fetch summary data from an ERC-4626 vault (total assets, shares, allowances, balances, etc.):
import { useFetchERC4626DataX } from "wagmi-extended";
function VaultInfo({ vaultAddress, user, spender }) {
const { data, isLoading, error } = useFetchERC4626DataX({
vault,
user,
spender,
});
if (isLoading) return <p>Loading vault data…</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<p>Total assets: {data.totalAssets}</p>
<p>Current shares: {data.shares}</p>
<p>Your balance: {data.userBalance}</p>
<p>Your allowance: {data.allowance}</p>
</div>
);
}
Fetch summary data for a generic ERC-20 token (decimals, name, symbol, balances, allowances):
import { useFetchERC20DataX } from "wagmi-extended";
function TokenInfo({ token, user, spender }) {
const { data, isLoading, error } = useFetchERC20DataX({
address: token,
user,
spender,
});
if (isLoading) return <p>Loading token info…</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<p>Name: {data.name}</p>
<p>Symbol: {data.symbol}</p>
<p>Decimals: {data.decimals}</p>
<p>Your balance: {data.balance}</p>
<p>Your allowance: {data.allowance}</p>
</div>
);
}
The useTokenX hook fetches token metadata (symbol, decimals, name and symbol) for a given token address using React Query and your Wagmi configuration.
import { useTokenX } from "wagmi-extended";
import { Address } from "viem";
function TokenDisplay({ tokenAddress }: { tokenAddress: Address }) {
const { data, isLoading, error } = useTokenX(tokenAddress);
if (isLoading) return <div>Loading token data...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<p>Symbol: {data.symbol}</p>
<p>Decimals: {data.decimals}</p>
<p>Name: {data.name}</p>
</div>
);
}
Feel free to skip Setup-section if using only hooks - no fetch methods directly
For easier use of fetch (non hook) functions setup the default configuration.
This is done by calling setDefaults
in your application initialization (e.g., in index.tsx or App.tsx).
Example:
// index.tsx (or App.tsx)
import React from "react";
import ReactDOM from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useConfig } from "wagmi";
import { setDefaults } from "wagmi-extended";
import { wagmiConfig } from "/your/path/to/wagmi-config";
const queryClient = new QueryClient();
// Obtain your Wagmi configuration from your initialization or provider
// Set defaults for the extended library functions.
setDefaults(queryClient, wagmiConfig);
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</React.StrictMode>
);
The library provides a built-in error mapping mechanism to transform raw errors into user-friendly messages. This is especially useful when working with smart contracts that may revert with different kinds of errors.
Note: : mapper is just a
fallback
if error message can't be understood from ABIs.
Example: Override the default allowance error message and add a new custom error. Provide error type (name), signature or reason and string as a message.
setErrorMapping({
ErrorNotEnoughAllowance: "Custom message: Please approve tokens first!",
NewCustomError: "A custom error occurred.",
"0xea8d7f02":
"Action exceeded safe slippage parameters, please try again later",
});
Note: : this
EXPANDS
the current object, it doesn't override.
This is default error mapper, feel free to create PR to extend it as well:
const defaultErrorMapping: Record<string, string> = {
EnforcedPause: "Temporary pause in effect, please check Discord for updates.",
ErrorNotEnoughAllowance:
"Not enough allowance, did you approve your tokens first?",
"0xc2139725": "Not enough allowance, did you approve your tokens first?",
SharesReceivedBelowMinimum:
"Action exceeded safe slippage parameters, please try again later",
"0xea8d7f02":
"Action exceeded safe slippage parameters, please try again later",
MaxSlippageExceeded:
"Action exceeded safe slippage parameters, please try again later",
"51": "Supply cap exceeded",
};
Note: : if you don't like initial error mapping and messages, you can call
resetErrorMapping()
which will reset the mapping to{}
.
Example:
const tokensData = await Promise.all(tokenAddresses.map((token) =>
fetchTokenX(token);
));
Note: : if you did not setup configs (queryClient and wagmi config) you can call by passing client and config as params:
fetchTokenX(token, queryClient, wagmiConfig)
.
This project is open source and we welcome contributions from the community! If you have ideas, improvements, or bug fixes, please feel free to open pull requests or file issues. Your help makes this project better for everyone.
-
Open Issues & PRs:
You can report bugs or request features by opening an issue on GitHub Issues. Similarly, feel free to open a pull request (PR) with your changes. -
Star the Project:
If you likewagmi-extended
or find it useful, please consider starring the repository on GitHub. It helps the project gain visibility and motivates further development.
Thank you for your support and contributions!
If you enjoy this project and would like to support its ongoing development, please consider donating!
-
Buy Me a Coffee:
buymeacoffee.com/srdjanr160N -
Ethereum Donation Address:
0x410A11ed53a9a59094F24D2ae4ACbeF7f84955a1
Any donation, no matter how small, is greatly appreciated!
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to http://unlicense.org/