library/specializations/cryptography-blockchain/skills/wallet-integration/SKILL.md
Wallet connection and transaction management for dApps using wagmi and viem. Supports multiple connectors, chain switching, EIP-712 signing, and hardware wallet integration.
npx skillsauth add a5c-ai/babysitter wallet-integrationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expert wallet connection and transaction management for Web3 dApps using wagmi and viem.
# Install wagmi and viem
npm install wagmi viem @tanstack/react-query
# Optional UI kits
npm install @rainbow-me/rainbowkit # or
npm install @web3modal/wagmi
// config/wagmi.ts
import { createConfig, http } from "wagmi";
import { mainnet, sepolia, polygon, arbitrum } from "wagmi/chains";
import { injected, walletConnect, coinbaseWallet } from "wagmi/connectors";
export const config = createConfig({
chains: [mainnet, sepolia, polygon, arbitrum],
connectors: [
injected(),
walletConnect({
projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID!,
}),
coinbaseWallet({
appName: "My dApp",
}),
],
transports: {
[mainnet.id]: http(process.env.NEXT_PUBLIC_MAINNET_RPC),
[sepolia.id]: http(process.env.NEXT_PUBLIC_SEPOLIA_RPC),
[polygon.id]: http(process.env.NEXT_PUBLIC_POLYGON_RPC),
[arbitrum.id]: http(process.env.NEXT_PUBLIC_ARBITRUM_RPC),
},
});
// config/rainbowkit.ts
import "@rainbow-me/rainbowkit/styles.css";
import { getDefaultConfig } from "@rainbow-me/rainbowkit";
import { mainnet, sepolia, polygon } from "wagmi/chains";
export const config = getDefaultConfig({
appName: "My dApp",
projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID!,
chains: [mainnet, sepolia, polygon],
ssr: true,
});
// app/providers.tsx
"use client";
import { WagmiProvider } from "wagmi";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { RainbowKitProvider } from "@rainbow-me/rainbowkit";
import { config } from "./config/wagmi";
const queryClient = new QueryClient();
export function Providers({ children }: { children: React.ReactNode }) {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<RainbowKitProvider>{children}</RainbowKitProvider>
</QueryClientProvider>
</WagmiProvider>
);
}
// components/ConnectButton.tsx
import { useAccount, useConnect, useDisconnect } from "wagmi";
export function ConnectButton() {
const { address, isConnected } = useAccount();
const { connect, connectors, isPending, error } = useConnect();
const { disconnect } = useDisconnect();
if (isConnected) {
return (
<div>
<p>
{address?.slice(0, 6)}...{address?.slice(-4)}
</p>
<button onClick={() => disconnect()}>Disconnect</button>
</div>
);
}
return (
<div>
{connectors.map((connector) => (
<button
key={connector.id}
onClick={() => connect({ connector })}
disabled={isPending}
>
{isPending ? "Connecting..." : `Connect ${connector.name}`}
</button>
))}
{error && <p>Error: {error.message}</p>}
</div>
);
}
// components/Account.tsx
import { useAccount, useBalance, useEnsName, useEnsAvatar } from "wagmi";
export function Account() {
const { address, chain } = useAccount();
const { data: balance } = useBalance({ address });
const { data: ensName } = useEnsName({ address });
const { data: ensAvatar } = useEnsAvatar({ name: ensName ?? undefined });
return (
<div>
{ensAvatar && <img src={ensAvatar} alt="ENS Avatar" />}
<p>{ensName ?? `${address?.slice(0, 6)}...${address?.slice(-4)}`}</p>
<p>
{balance?.formatted} {balance?.symbol}
</p>
<p>Network: {chain?.name}</p>
</div>
);
}
// components/NetworkSwitcher.tsx
import { useAccount, useSwitchChain } from "wagmi";
export function NetworkSwitcher() {
const { chain } = useAccount();
const { chains, switchChain, isPending, error } = useSwitchChain();
return (
<div>
<p>Current: {chain?.name ?? "Not connected"}</p>
<div>
{chains.map((c) => (
<button
key={c.id}
onClick={() => switchChain({ chainId: c.id })}
disabled={isPending || c.id === chain?.id}
>
{c.name}
</button>
))}
</div>
{error && <p>Error: {error.message}</p>}
</div>
);
}
// components/SendTransaction.tsx
import { useSendTransaction, useWaitForTransactionReceipt } from "wagmi";
import { parseEther } from "viem";
export function SendTransaction() {
const { data: hash, isPending, error, sendTransaction } = useSendTransaction();
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({
hash,
});
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const to = formData.get("to") as `0x${string}`;
const value = formData.get("value") as string;
sendTransaction({
to,
value: parseEther(value),
});
}
return (
<form onSubmit={handleSubmit}>
<input name="to" placeholder="0x..." required />
<input name="value" placeholder="0.01" required />
<button type="submit" disabled={isPending}>
{isPending ? "Sending..." : "Send"}
</button>
{hash && <p>Tx: {hash}</p>}
{isConfirming && <p>Confirming...</p>}
{isSuccess && <p>Confirmed!</p>}
{error && <p>Error: {error.message}</p>}
</form>
);
}
// components/ContractInteraction.tsx
import {
useReadContract,
useWriteContract,
useWaitForTransactionReceipt,
} from "wagmi";
import { parseUnits, formatUnits } from "viem";
import { erc20Abi } from "viem";
const TOKEN_ADDRESS = "0x...";
export function TokenBalance({ address }: { address: `0x${string}` }) {
const { data: balance, refetch } = useReadContract({
address: TOKEN_ADDRESS,
abi: erc20Abi,
functionName: "balanceOf",
args: [address],
});
return <p>Balance: {balance ? formatUnits(balance, 18) : "0"}</p>;
}
export function TokenTransfer() {
const { data: hash, writeContract, isPending } = useWriteContract();
const { isSuccess } = useWaitForTransactionReceipt({ hash });
function handleTransfer(to: string, amount: string) {
writeContract({
address: TOKEN_ADDRESS,
abi: erc20Abi,
functionName: "transfer",
args: [to as `0x${string}`, parseUnits(amount, 18)],
});
}
return (
<button
onClick={() => handleTransfer("0x...", "100")}
disabled={isPending}
>
{isPending ? "Transferring..." : "Transfer 100 Tokens"}
</button>
);
}
// components/SignTypedData.tsx
import { useSignTypedData, useAccount } from "wagmi";
const domain = {
name: "My dApp",
version: "1",
chainId: 1,
verifyingContract: "0x..." as const,
};
const types = {
Permit: [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" },
{ name: "value", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
],
};
export function SignPermit() {
const { address } = useAccount();
const { signTypedData, data: signature, isPending } = useSignTypedData();
function handleSign() {
signTypedData({
domain,
types,
primaryType: "Permit",
message: {
owner: address!,
spender: "0x..." as const,
value: BigInt("1000000000000000000"),
nonce: BigInt(0),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
},
});
}
return (
<div>
<button onClick={handleSign} disabled={isPending}>
Sign Permit
</button>
{signature && <p>Signature: {signature}</p>}
</div>
);
}
// utils/errors.ts
import { BaseError, ContractFunctionRevertedError } from "viem";
export function parseContractError(error: unknown): string {
if (error instanceof BaseError) {
const revertError = error.walk(
(err) => err instanceof ContractFunctionRevertedError
);
if (revertError instanceof ContractFunctionRevertedError) {
const errorName = revertError.data?.errorName ?? "Unknown error";
return `Contract reverted: ${errorName}`;
}
return error.shortMessage;
}
return "Unknown error occurred";
}
| Process | Purpose |
|---------|---------|
| dapp-frontend-development.js | dApp building |
| hd-wallet-implementation.js | Wallet integration |
| multi-signature-wallet.js | Multi-sig dApps |
skills/subgraph-indexing/SKILL.md - Data indexingagents/web3-frontend/AGENT.md - Frontend expertdevelopment
Model documentation skill for generating model cards following Google's model card framework.
development
MLflow integration skill for experiment tracking, model registry, and artifact management. Enables LLMs to log experiments, compare runs, manage model lifecycle, and retrieve artifacts through the MLflow API.
data-ai
LIME-based local explanation skill for individual predictions across tabular, text, and image data.
devops
Kubeflow Pipelines skill for ML workflow orchestration, component management, and Kubernetes-native ML.