w3-kitdocs

stake-tokens

Stake tokens to earn yield by locking assets in a staking contract (EVM) or delegating SOL to a validator (Solana).

evmsolana

Dependencies

viem@solana/web3.js
stake-tokens/evm.tsx
/**
 * stake-tokens/evm.tsx
 * Generic EVM staking pattern: approve token → call stake(amount) on staking contract.
 * Replace STAKING_ADDRESS and STAKING_ABI with your target protocol.
 */

import { useState } from "react";
import {
  useAccount,
  useWriteContract,
  useReadContract,
  useWaitForTransactionReceipt,
} from "wagmi";
import { parseUnits, formatUnits } from "viem";

const TOKEN_ADDRESS = "0xTOKEN" as `0x${string}`;
const STAKING_ADDRESS = "0xSTAKING" as `0x${string}`;
const DECIMALS = 18;

const ERC20_ABI = [
  { name: "approve", type: "function", stateMutability: "nonpayable",
    inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }],
    outputs: [{ type: "bool" }] },
] as const;

const STAKING_ABI = [
  { name: "stake", type: "function", stateMutability: "nonpayable",
    inputs: [{ name: "amount", type: "uint256" }], outputs: [] },
  { name: "stakedBalance", type: "function", stateMutability: "view",
    inputs: [{ name: "account", type: "address" }],
    outputs: [{ type: "uint256" }] },
] as const;

export function useStakeTokens() {
  const { address } = useAccount();
  const [amount, setAmount] = useState("");

  const { data: stakedBalance } = useReadContract({
    address: STAKING_ADDRESS,
    abi: STAKING_ABI,
    functionName: "stakedBalance",
    args: address ? [address] : undefined,
    query: { enabled: !!address },
  });

  const { writeContract: approve, data: approveTxHash } = useWriteContract();
  const { isSuccess: approved } = useWaitForTransactionReceipt({ hash: approveTxHash });

  const { writeContract: stake, data: stakeTxHash } = useWriteContract();
  const { isSuccess: staked, isLoading: staking } =
    useWaitForTransactionReceipt({ hash: stakeTxHash });

  function handleApprove() {
    approve({ address: TOKEN_ADDRESS, abi: ERC20_ABI, functionName: "approve",
      args: [STAKING_ADDRESS, parseUnits(amount, DECIMALS)] });
  }

  function handleStake() {
    stake({ address: STAKING_ADDRESS, abi: STAKING_ABI, functionName: "stake",
      args: [parseUnits(amount, DECIMALS)] });
  }

  return {
    amount, setAmount,
    stakedBalance: stakedBalance ? formatUnits(stakedBalance, DECIMALS) : "0",
    handleApprove, approved,
    handleStake, staking, staked,
  };
}

Learn: Staking

Proof-of-Stake basics

PoS networks (Ethereum, Solana) select validators proportional to their stake. Validators earn block rewards and fees; delegators share in those rewards.

Native vs liquid vs DeFi staking

TypeExampleTradeoff
NativeSOL delegation, ETH solo validatorFull rewards, illiquid
LiquidstSOL (Marinade), stETH (Lido)Tradeable LST, small fee
DeFiSynthetix SNX, Curve CRVToken emissions, smart contract risk

APY vs APR

  • APR (Annual Percentage Rate): simple interest, no compounding.
  • APY (Annual Percentage Yield): includes compounding effect.
  • Most DeFi UIs show APY. Native staking usually shows APR.
  • Formula: APY = (1 + APR/n)^n − 1 where n = compounding periods/year.

Epoch-based vs continuous rewards

  • Solana: rewards distributed once per epoch (~2 days). Stake must be active (not in warmup) to earn for that epoch.
  • EVM protocols: vary — some accrue per block, others require manual claim.

Slashing risks

  • EVM: protocol-specific; some have no slashing, others slash for inactivity or double-signing.
  • Solana native: validators can be slashed for double-voting; rare in practice.
  • Liquid staking: slashing risk is socialised across all LST holders.

Unbonding / cooldown

Always read the protocol docs for cooldown periods before staking. Some EVM protocols have multi-day withdrawal queues. Solana native stake takes ~1 epoch to deactivate before funds can be withdrawn.

Key takeaway

Staking is not risk-free. Understand lock-up, slashing exposure, and whether you're earning in a volatile token before committing capital.