Quick Overview
@trezo/evm gives you a type-safe layer for reading and writing to EVM smart contracts, connecting wallets, and reacting to on-chain events
Installation
Run the below command with your desired package manager
pnpm add @trezo/evm
Define your config
Set this up once at the module level.
import { create, Chains } from "@trezo/evm";
export const contractAbi = [...] as const; // must be as const
export const evmConfig = create({
address: "0x...",
abi: contractAbi,
chains: [Chains.optimismSepolia],
rpcUrls: {
"sepolia": "https://optimism-sepolia-public.nodies.app",
},
wallet: {
from: "family", // "family" | "reown"
options: {
projectId: "your_wallet_connect_project_id",
appInfo: {
name: "My App",
},
},
},
});
export const { useConfig } = evmConfig;Key fields:
address— deployed contract addressabi— contract ABI typedas const. Withoutas constyou lose all type safety.chains— supported chains.Chainsre-exports all chains fromwagmi/chains.rpcUrls— custom RPC endpoints keyed by chain ID. Avoids rate-limited public nodes.wallet— wallet modal provider. TypeScript autocompletes all supported options.
Wrap your app
Pass the config as an argument in the EvmProvider
import { EvmProvider } from "@trezo/evm/react";
import { evmConfig } from "@/config/evm.config";
export default function Root({ children }) {
return <EvmProvider config={evmConfig}>{children}</EvmProvider>;
}Basic Usage
"use client"; // required for Next.js
import { ConnectButton } from "@trezo/evm/react";
import { useConfig } from "@/config/evm.config";
export default function Home() {
const { wallet, call, web3Provider } = useConfig();
async function fetchTask() {
const { data } = await call.queryFn("getTask", [BigInt(1)]);
}
async function addTask() {
const { data } = await call.mutateFn("addTask", ["Buy milk"]);
console.log(data?.hash);
}
function listenForChanges() {
return call.listenFn("TaskAdded", (_, __, content) => {
console.log(content);
});
}
return (
<div>
<ConnectButton>
{({ isConnected, truncatedAddress, open, isConnecting }) => (
<button onClick={() => open()} disabled={isConnecting}>
{isConnected ? truncatedAddress : "Connect Wallet"}
</button>
)}
</ConnectButton>
{wallet.account.isConnected ? (
<p>Connected: {wallet.account.address}</p>
) : (
<p>Not connected</p>
)}
</div>
);
}wallet— connection statecall— contract helpers (queryFn,mutateFn,listenFn)web3Provider— injected provider info
Call methods
Read from Contract
For view and pure functions — reads that don't change state and cost no gas.
Example Usage
const { data, error } = await call.queryFn("fetchAllTasks", []);
if (error) {
console.error(error.message); // human-readable, safe to show users
console.error(error.raw); // full Error object for debugging
} else {
console.log(data); // typed from your ABI
}Options:
- from: override sender (defaults to connected address)
- blockTag:
latest,earliest,pending, ornumber - gasLimit
- value
Write to Contract
For nonpayable and payable functions — writes that change on-chain state and require a wallet signature.
Example Usage
const { data, error } = await call.mutateFn("addTask", ["Buy groceries"]);
if (error) {
toast.error(error.message); // e.g. "CONTRACT_ERROR_MESSAGE"
} else {
console.log(data?.hash); // transaction hash
console.log(data?.receipt); // full TransactionReceipt
}Options:
- estimateGas:
trueorfalse - gasLimit
- value
- maxFeePerGas
- maxPriorityFeePerGas
- nonce
Listen to Events
Subscribes to contract events and fires your listener each time. Automatically falls back to block-polling on RPCs that don't support eth_newFilter (most public/free-tier nodes).
Single event
const cleanup = call.listenFn("TaskAdded", (user, taskId, content) => {
console.log("Task added:", { user, taskId, content });
});
cleanup(); // stop listeningListener args are fully typed from your ABI.
Multiple events
const cleanup = call.listenFn({
add: { eventName: "TaskAdded", listener: fetchAllTasks },
update: { eventName: "TaskUpdated", listener: fetchAllTasks },
remove: { eventName: "TaskRemoved", listener: fetchAllTasks },
toggle: { eventName: "TaskToggled", listener: fetchAllTasks },
});
cleanup(); // stops all listeners at onceMore efficient than multiple listenFn calls — all listeners share the same underlying connection.
ConnectButton
Supports a render prop for full UI control, or a default button when no children are passed.
Import from @trezo/evm/react.
Default button
import { ConnectButton } from "@trezo/evm/react";
<ConnectButton label="Connect Wallet" />;Custom render
import { ConnectButton } from "@trezo/evm/react";
<ConnectButton>
{({ isConnected, truncatedAddress, ensName, open, isConnecting }) => (
<button onClick={() => open()}>
{isConnected ? (ensName ?? truncatedAddress) : "Connect"}
</button>
)}
</ConnectButton>;Wallet State
const { wallet } = useConfig();
wallet.account.isConnected; // true when wallet is connected
wallet.account.isConnecting; // true during connection or page-reload reconnect
wallet.account.address; // `0x${string}` | undefined
wallet.account.chainId; // number | undefinedUse isConnecting to differentiate between "not connected" and "reconnecting on reload":
{
wallet.account.isConnecting ? (
<span>Reconnecting...</span>
) : wallet.account.isConnected ? (
<span>{wallet.account.address}</span>
) : (
<span>Not connected</span>
);
}Provider State
const { web3Provider } = useConfig();
web3Provider.isAvailable; // true if window.ethereum exists (MetaMask, etc.)Use it to disable UI that requires an injected wallet:
<button disabled={!web3Provider.isAvailable}>Add Task</button>Outside React (Vanilla JS)
evmConfig exposes static methods for use outside React components — useful for utility functions, server actions, or non-component files:
import { evmConfig } from "@/config/evm.config";
// Read current state snapshot
const state = evmConfig._store.getState();
console.log(state.wallet.address);
// Call contract functions
const result = await evmConfig._store.call.queryFn("fetchAllTasks", []);
// Subscribe to state changes
const unsubscribe = evmConfig._store.subscribe(() => {
console.log("State changed:", evmConfig._store.getState());
});
unsubscribe();Wallet Providers
| Key | Package required | Notes |
|---|---|---|
"family" | connectkit | ConnectKit styled modal |
"reown" | @reown/appkit, @reown/appkit-adapter-wagmi | AppKit (WalletConnect v2) |
Family (ConnectKit)
wallet: {
from: "family",
options: {
projectId: "your_walletconnect_project_id",
appInfo: {
name: "My App",
description: "My dApp description",
url: "https://myapp.xyz",
icon: "https://myapp.xyz/icon.png",
},
},
},Reown (AppKit)
wallet: {
from: "reown",
options: {
projectId: "your_walletconnect_project_id",
metadata: {
name: "My App",
description: "My dApp description",
url: "https://myapp.xyz",
icons: ["https://myapp.xyz/icon.png"],
},
ssr: false, // set true for Next.js SSR
features: {
analytics: true, // optional, defaults to Cloud config
},
},
},Plugins Setup
Avoid duplicate dependencies across packages.
Vite Plugin
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { trezoVitePlugin } from "@trezo/evm/plugins/vite";
export default defineConfig({
plugins: [react(), trezoVitePlugin()],
});Package Exports
| Import path | Contents |
|---|---|
@trezo/evm | create, Chains, all types |
@trezo/evm/react | EvmProvider, ConnectButton, useConfig |
@trezo/evm/plugins/vite | trezoVitePlugin |
Summary
| API | Purpose |
|---|---|
create(config) | Initialize the entire Web3 setup |
EvmProvider | App wrapper (React) — required |
ConnectButton | Wallet connection UI with render props (React) |
useConfig() | React hook — access wallet, call, provider state |
call.queryFn | Read contract data (view/pure functions) |
call.mutateFn | Write to contract (nonpayable/payable functions) |
call.listenFn | Single or multiple event listeners |
wallet | Connection state (address, chainId, isConnected) |
web3Provider | Injected provider availability |
evmConfig._store.getState() | Static state access outside React |
evmConfig._store.subscribe() | Subscribe to state changes outside React |
trezoVitePlugin() | Vite plugin — prevents duplicate React/wagmi/viem |
On This Page
Quick OverviewInstallationDefine your configWrap your appBasic UsageCall methodsRead from ContractExample UsageWrite to ContractExample UsageListen to EventsSingle eventMultiple eventsConnectButtonDefault buttonCustom renderWallet StateProvider StateOutside React (Vanilla JS)Wallet ProvidersFamily (ConnectKit)Reown (AppKit)Plugins SetupVite PluginPackage ExportsSummary