Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Wallet connection and transaction management for dApps using wagmi and viem. Supports multiple connectors, chain switching, EIP-712 signing, and hardware wallet integration.
.claude/skills/a5c-ai-wallet-integration/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 71% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 94% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 112% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 81% | 0% |
| case-02 | ✗→✗ | = Same ✗ | 98% | 0% |
Expert wallet connection and transaction management for Web3 dApps using wagmi and viem.
bash# Install wagmi and viem npm install wagmi viem @tanstack/react-query # Optional UI kits npm install @rainbow-me/rainbowkit # or npm install @web3modal/wagmi
typescript// 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), }, });
typescript// 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, });
tsx// 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> ); }
tsx// 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> ); }
tsx// 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> ); }
tsx// 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> ); }
tsx// 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> ); }
tsx// 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> ); }
tsx// 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> ); }
tsx// 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 expert| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 17,542 | 14,682 | -16% | 1 | 1 | 0% | 3,606 | 6,165 | +71% | 0 | 0 | — |
case-02 | fail→fail | 11,263 | 8,289 | -26% | 1 | 1 | 0% | 2,271 | 4,489 | +98% | 0 | 0 | — |
case-03 | fail→pass | 12,116 | 7,975 | -34% | 1 | 1 | 0% | 2,333 | 4,523 | +94% | 0 | 0 | — |
case-04 | fail→fail | 14,855 | 10,834 | -27% | 1 | 1 | 0% | 3,471 | 5,027 | +45% | 0 | 0 | — |
case-05 | fail→fail | 9,592 | 6,054 | -37% | 1 | 1 | 0% | 2,106 | 4,031 | +91% | 0 | 0 | — |
case-06 | pass→pass | 12,547 | 9,792 | -22% | 1 | 1 | 0% | 2,779 | 5,033 | +81% | 0 | 0 | — |
case-07 | fail→fail | 11,319 | 10,456 | -8% | 1 | 1 | 0% | 2,508 | 5,283 | +111% | 0 | 0 | — |
case-08 | fail→fail | 14,354 | 14,096 | -2% | 1 | 1 | 0% | 3,289 | 6,116 | +86% | 0 | 0 | — |
case-09 | fail→fail | 10,570 | 8,112 | -23% | 1 | 1 | 0% | 2,218 | 4,475 | +102% | 0 | 0 | — |
case-10 | fail→fail | 5,779 | 5,944 | +3% | 1 | 1 | 0% | 1,375 | 4,139 | +201% | 0 | 0 | — |
case-11 | fail→fail | 9,525 | 5,781 | -39% | 1 | 1 | 0% | 2,056 | 4,076 | +98% | 0 | 0 | — |
case-12 | fail→fail | 11,361 | 8,423 | -26% | 1 | 1 | 0% | 2,410 | 4,462 | +85% | 0 | 0 | — |
case-13 | fail→fail | 7,581 | 2,479 | -67% | 1 | 1 | 0% | 1,448 | 3,216 | +122% | 0 | 0 | — |
case-14 | fail→pass | 9,799 | 6,617 | -32% | 1 | 1 | 0% | 1,981 | 4,202 | +112% | 0 | 0 | — |
case-15 | fail→fail | 11,972 | 11,647 | -3% | 1 | 1 | 0% | 2,426 | 5,561 | +129% | 0 | 0 | — |
case-16 | fail→fail | 14,240 | 6,436 | -55% | 1 | 1 | 0% | 2,165 | 4,093 | +89% | 0 | 0 | — |
case-17 | fail→fail | 10,508 | 6,332 | -40% | 1 | 1 | 0% | 1,975 | 3,812 | +93% | 0 | 0 | — |
case-18 | fail→fail | 3,598 | 3,513 | -2% | 1 | 1 | 0% | 713 | 3,469 | +387% | 0 | 0 | — |
case-19 | fail→fail | 9,922 | 9,237 | -7% | 1 | 1 | 0% | 2,371 | 4,966 | +109% | 0 | 0 | — |
case-20 | fail→fail | 9,057 | 9,453 | +4% | 1 | 1 | 0% | 1,608 | 4,679 | +191% | 0 | 0 | — |
case-21 | fail→fail | 13,063 | 11,023 | -16% | 1 | 1 | 0% | 2,818 | 5,301 | +88% | 0 | 0 | — |
case-22 | fail→fail | 9,321 | 4,307 | -54% | 1 | 1 | 0% | 2,071 | 3,775 | +82% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +14 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.