From ethskills
Provides UX rules for Ethereum dApp frontends preventing common UI bugs: onchain buttons with pending states, approval flows, address handling, USD context, RPC reliability, theming, metadata. Use for Ethereum dApp UIs.
npx claudepluginhub austintgriffith/ethskills --plugin ethskillsThis skill uses the workspace's default tool permissions.
**"The button works."** A clickable button is not enough. It must disable immediately, show a clear pending state, and stay locked until onchain confirmation.
Provides Web3 wallet integration for DApps including provider detection, connection, transaction signing, account management, gas estimation, and UX patterns.
Builds production-ready Web3 apps, smart contracts in Solidity/Rust/Vyper, and decentralized systems for DeFi, NFTs, DAOs across Ethereum L2s, Solana, Cosmos.
Share bugs, ideas, or general feedback.
"The button works." A clickable button is not enough. It must disable immediately, show a clear pending state, and stay locked until onchain confirmation.
"Addresses are just strings." Address UX needs validation, safe formatting, copy support, explorer linking, and ENS/name handling where available.
"Token amounts are clear." Raw token values without USD context force users to guess risk and value. Show dollar context anywhere amounts matter.
Any button that triggers an onchain transaction must:
Approving..., Staking...)// Separate loading state per action
const [isApproving, setIsApproving] = useState(false);
const [isStaking, setIsStaking] = useState(false);
<button
disabled={isApproving}
onClick={async () => {
setIsApproving(true);
try {
await sendApproveTx();
} catch (e) {
notifyError("Approval failed");
} finally {
setIsApproving(false); // always release — even on rejection
}
}}
>
{isApproving ? "Approving..." : "Approve"}
</button>
Never use one shared isLoading state for multiple buttons. It causes wrong labels, wrong disabled states, and duplicate submissions.
For approval flows: isPending alone is not enough.
isPending drops to false when the wallet returns the tx hash — before on-chain confirmation. There is a window where isPending = false AND the allowance hasn't updated → button re-enables mid-flight and a user can double-submit.
Approval handlers need two states: approvalSubmitting (set on click, cleared in finally {}) to cover the wallet→confirmation gap, and approveCooldown (set after confirm, cleared after 4s + refetch) to cover the confirmation→cache gap. Both go on disabled. finally {} is required — without it a rejected tx locks the button permanently.
Show one primary action at a time:
1. Not connected -> Connect Wallet
2. Wrong network -> Switch Network
3. Needs approval -> Approve
4. Ready -> Execute action (Stake/Deposit/Swap/etc.)
Critical details:
Every displayed address should support:
Every address input should support:
If your UI kit includes dedicated address components, use them. Do not use a raw free-text field for critical address entry.
Every token/ETH amount shown to users should include USD context:
<span>0.5 ETH (~$1,250.00)</span>
<span>1,000 TOKEN (~$4.20)</span>
Do not show only token units without value context.
Healthy baseline: low, steady request volume. Spiky or sustained high QPS usually indicates frontend hook/config bugs.
Do not hardcode full-page dark backgrounds that ignore theme/system preference.
Use semantic theme tokens/classes so light/dark mode stays coherent across:
If you intentionally ship dark-only, remove or disable theme controls that no longer apply.
Users should never see raw revert selectors or silent failures.
Implement:
try {
await sendTx();
} catch (e) {
setTxError(parseContractError(e));
}
Before production release:
https://...)Always convert between contract units and display units:
import { formatEther, formatUnits, parseEther, parseUnits } from "viem";
formatEther(weiAmount);
formatUnits(tokenAmount, tokenDecimals);
parseEther("1.5");
parseUnits("100", 6); // USDC-style 6 decimals
Never show raw base units like 1500000000000000000.
Found something wrong, confusing, or genuinely helpful in this skill? Send a note via feedback/SKILL.md.