> ## Documentation Index
> Fetch the complete documentation index at: https://luminouslabs-cc5545c6-docs-main-reorder.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Mint Account with Token Metadata

> Program and client guides to create a mint with token metadata. For existing SPL or Token 2022 mints add an interface PDA for interoperability. Includes step-by-step implementation and full code examples.

export const lightCreateMintMetadataCpiCode = ["use light_token::instruction::CreateMintCpi;", "", "let extensions = Some(vec![", "    ExtensionInstructionData::TokenMetadata(", "        TokenMetadataInstructionData {", "            update_authority: Some(authority.key.to_bytes().into()),", '            name: b"Example Token".to_vec(),', '            symbol: b"EXT".to_vec(),', '            uri: b"https://example.com/metadata.json".to_vec(),', "            additional_metadata: None,", "        },", "    ),", "]);", "", "CreateMintCpi {", "    mint_seed: mint_seed.clone(),", "    authority: authority.clone(),", "    payer: payer.clone(),", "    address_tree: address_tree.clone(),", "    output_queue: output_queue.clone(),", "    compressible_config: compressible_config.clone(),", "    mint: mint.clone(),", "    rent_sponsor: rent_sponsor.clone(),", "    system_accounts,", "    cpi_context: None,", "    cpi_context_account: None,", "    params, // includes extensions", "}", ".invoke()?"].join("\n");

export const splCreateMintMetadataCpiCode = ["use spl_token_2022::instruction::initialize_mint;", "use spl_token_metadata_interface::instruction::initialize as init_metadata;", "", "let ix_mint = initialize_mint(", "    &spl_token_2022::id(),", "    &mint.pubkey(),", "    &mint_authority,", "    Some(&freeze_authority),", "    decimals,", ")?;", "invoke(&ix_mint, &[mint.clone(), rent_sysvar.clone()])?;", "", "let ix_meta = init_metadata(", "    &spl_token_2022::id(),", "    &mint.pubkey(),", "    &update_authority,", "    &mint.pubkey(),", "    &mint_authority,", "    name, symbol, uri,", ")?;", "invoke(&ix_meta, &[mint, update_auth])?;"].join("\n");

export const lightCreateMintCpiCode = ["use light_token::instruction::CreateMintCpi;", "", "CreateMintCpi {", "    mint_seed: mint_seed.clone(),", "    authority: authority.clone(),", "    payer: payer.clone(),", "    address_tree: address_tree.clone(),", "    output_queue: output_queue.clone(),", "    compressible_config: compressible_config.clone(),", "    mint: mint.clone(),", "    rent_sponsor: rent_sponsor.clone(),", "    system_accounts,", "    cpi_context: None,", "    cpi_context_account: None,", "    params,", "}", ".invoke()?"].join("\n");

export const splCreateMintCpiCode = ["use spl_token::instruction::initialize_mint;", "", "let ix = initialize_mint(", "    &spl_token::id(),", "    &mint.pubkey(),", "    &mint_authority,", "    Some(&freeze_authority),", "    decimals,", ")?;", "", "invoke(&ix, &[mint, rent_sysvar])?;"].join("\n");

export const lightCreateMintMetadataMacroCode = ["#[light_account(init,", "    mint::signer = mint_signer,", "    mint::authority = fee_payer,", "    mint::decimals = 9,", "    mint::seeds = &[MINT_SIGNER_SEED, self.authority.to_account_info().key.as_ref()],", "    mint::bump = params.mint_signer_bump,", "    mint::name = params.name.clone(),", "    mint::symbol = params.symbol.clone(),", "    mint::uri = params.uri.clone(),", "    mint::update_authority = authority", ")]", "pub mint: UncheckedAccount<'info>,"].join("\n");

export const splCreateMintMetadataMacroCode = ["#[account(", "    init,", "    payer = fee_payer,", "    mint::decimals = 9,", "    mint::authority = fee_payer,", "    extensions::metadata_pointer::authority = fee_payer,", "    extensions::metadata_pointer::metadata_address = mint_account,", ")]", "pub mint_account: InterfaceAccount<'info, Mint>,", "", "// Metadata requires a separate CPI:", "token_metadata_initialize(", "    cpi_ctx,", "    params.name,", "    params.symbol,", "    params.uri,", ")?;"].join("\n");

export const lightCreateMintMacroCode = ["#[light_account(init,", "    mint::signer = mint_signer,", "    mint::authority = fee_payer,", "    mint::decimals = 9,", "    mint::seeds = &[MINT_SIGNER_SEED, self.authority.to_account_info().key.as_ref()],", "    mint::bump = params.mint_signer_bump", ")]", "pub mint: UncheckedAccount<'info>,"].join("\n");

export const splCreateMintMacroCode = ["#[account(", "    init,", "    payer = fee_payer,", "    mint::decimals = 9,", "    mint::authority = fee_payer,", ")]", "pub mint: InterfaceAccount<'info, Mint>,"].join("\n");

export const lightCreateMintRustCode = ["use light_token::instruction::CreateMint;", "", "let ix = CreateMint::new(", "    params,", "    mint_seed.pubkey(),", "    payer.pubkey(),", "    address_tree.tree,", "    output_queue,", ")", ".instruction()?;"].join("\n");

export const splCreateMintRustCode = ["use spl_token::instruction::initialize_mint;", "", "let ix = initialize_mint(", "    &spl_token::id(),", "    &mint.pubkey(),", "    &mint_authority,", "    Some(&freeze_authority),", "    decimals,", ")?;"].join("\n");

export const lightCreateMintCode = ['import { createMintInterface } from "@lightprotocol/compressed-token";', "", "const { mint } = await createMintInterface(", "  rpc,", "  payer,", "  mintAuthority,", "  freezeAuthority,", "  decimals,", "  mintKeypair", ");"].join("\n");

export const splCreateMintCode = ['import { createMint } from "@solana/spl-token";', "", "const mint = await createMint(", "  connection,", "  payer,", "  mintAuthority,", "  freezeAuthority,", "  decimals", ");"].join("\n");

export const CodeCompare = ({firstCode = "", secondCode = "", firstLabel = "Light Token", secondLabel = "SPL", language = "javascript"}) => {
  const [sliderPercent, setSliderPercent] = useState(100);
  const [isDragging, setIsDragging] = useState(false);
  const [isAnimating, setIsAnimating] = useState(false);
  const [copied, setCopied] = useState(false);
  const containerRef = useRef(null);
  const animationRef = useRef(null);
  const firstPreRef = useRef(null);
  const secondPreRef = useRef(null);
  const [containerHeight, setContainerHeight] = useState(null);
  const showingFirst = sliderPercent > 50;
  const handleCopy = async () => {
    const codeToCopy = showingFirst ? firstCode : secondCode;
    await navigator.clipboard.writeText(codeToCopy);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };
  const highlightCode = code => {
    let escaped = code.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
    if (language === "rust") {
      const rustPattern = /(\/\/.*$)|(["'])(?:(?!\2)[^\\]|\\.)*?\2|\b(use|let|mut|pub|fn|struct|impl|enum|mod|const|static|trait|type|where|for|in|if|else|match|loop|while|return|self|Self|true|false|Some|None|Ok|Err|Result|Option|vec!)\b|::([a-zA-Z_][a-zA-Z0-9_]*)|&amp;([a-zA-Z_][a-zA-Z0-9_]*)|\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()|(\?)/gm;
      return escaped.replace(rustPattern, (match, comment, stringQuote, keyword, pathSegment, reference, func, questionMark) => {
        if (comment) return `<span style="color:#6b7280;font-style:italic">${match}</span>`;
        if (stringQuote) return `<span style="color:#059669">${match}</span>`;
        if (keyword) return `<span style="color:#db2777">${match}</span>`;
        if (pathSegment) return `::<span style="color:#0891b2">${pathSegment}</span>`;
        if (reference) return `&amp;<span style="color:#6366f1">${reference}</span>`;
        if (func) return `<span style="color:#2563eb">${match}</span>`;
        if (questionMark) return `<span style="color:#db2777">?</span>`;
        return match;
      });
    }
    const pattern = /(\/\/.*$)|(["'`])(?:(?!\2)[^\\]|\\.)*?\2|\b(const|let|var|await|async|import|from|export|return|if|else|function|class|new|throw|try|catch)\b|\.([a-zA-Z_][a-zA-Z0-9_]*)\b|\b([a-zA-Z_][a-zA-Z0-9_]*)\s*(?=\()/gm;
    return escaped.replace(pattern, (match, comment, stringQuote, keyword, property, func) => {
      if (comment) return `<span style="color:#6b7280;font-style:italic">${match}</span>`;
      if (stringQuote) return `<span style="color:#059669">${match}</span>`;
      if (keyword) return `<span style="color:#db2777">${match}</span>`;
      if (property) return `.<span style="color:#0891b2">${property}</span>`;
      if (func) return `<span style="color:#2563eb">${match}</span>`;
      return match;
    });
  };
  const animateTo = target => {
    if (animationRef.current) cancelAnimationFrame(animationRef.current);
    setIsAnimating(true);
    const start = sliderPercent;
    const startTime = performance.now();
    const duration = 400;
    const animate = currentTime => {
      const elapsed = currentTime - startTime;
      const progress = Math.min(elapsed / duration, 1);
      const eased = 1 - Math.pow(1 - progress, 3);
      const current = start + (target - start) * eased;
      setSliderPercent(current);
      if (progress < 1) {
        animationRef.current = requestAnimationFrame(animate);
      } else {
        setSliderPercent(target);
        setIsAnimating(false);
        animationRef.current = null;
      }
    };
    animationRef.current = requestAnimationFrame(animate);
  };
  const handleToggle = () => {
    animateTo(showingFirst ? 0 : 100);
  };
  const handleMouseDown = e => {
    if (isAnimating) {
      cancelAnimationFrame(animationRef.current);
      setIsAnimating(false);
    }
    e.preventDefault();
    setIsDragging(true);
  };
  const handleMouseUp = () => {
    setIsDragging(false);
  };
  const handleMouseMove = e => {
    if (!isDragging || !containerRef.current) return;
    const rect = containerRef.current.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const percent = Math.max(0, Math.min(100, x / rect.width * 100));
    setSliderPercent(percent);
  };
  const handleTouchMove = e => {
    if (!containerRef.current) return;
    if (isAnimating) {
      cancelAnimationFrame(animationRef.current);
      setIsAnimating(false);
    }
    const rect = containerRef.current.getBoundingClientRect();
    const x = e.touches[0].clientX - rect.left;
    const percent = Math.max(0, Math.min(100, x / rect.width * 100));
    setSliderPercent(percent);
  };
  const handleKeyDown = e => {
    if (e.key === "ArrowLeft") {
      setSliderPercent(p => Math.max(0, p - 5));
    } else if (e.key === "ArrowRight") {
      setSliderPercent(p => Math.min(100, p + 5));
    }
  };
  useEffect(() => {
    if (isDragging) {
      document.addEventListener("mousemove", handleMouseMove);
      document.addEventListener("mouseup", handleMouseUp);
      return () => {
        document.removeEventListener("mousemove", handleMouseMove);
        document.removeEventListener("mouseup", handleMouseUp);
      };
    }
  }, [isDragging]);
  useEffect(() => {
    return () => {
      if (animationRef.current) cancelAnimationFrame(animationRef.current);
    };
  }, []);
  useEffect(() => {
    const activeRef = showingFirst ? firstPreRef : secondPreRef;
    if (activeRef.current) {
      setContainerHeight(activeRef.current.scrollHeight);
    }
  }, [showingFirst]);
  return <>
      <div className="rounded-3xl not-prose mt-4 backdrop-blur-xl border overflow-hidden border-zinc-300 dark:border-zinc-700" style={{
    fontFamily: "Inter, sans-serif"
  }}>
        {}
        <div className="flex items-center justify-between px-4 py-3 border-b border-zinc-200 dark:border-zinc-700 bg-gray-50 dark:bg-zinc-900">
          <span className="text-sm font-medium text-zinc-600 dark:text-zinc-300">
            {showingFirst ? firstLabel : secondLabel}
          </span>

          <div className="flex items-center gap-3">
            {}
            <button onClick={handleCopy} className="p-1.5 rounded hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors text-zinc-500 dark:text-zinc-400" title="Copy code" style={{
    background: "transparent",
    border: "none",
    cursor: "pointer"
  }}>
              {copied ? <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2">
                  <path d="M20 6L9 17l-5-5" />
                </svg> : <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
                  <path d="M14.25 5.25H7.25C6.14543 5.25 5.25 6.14543 5.25 7.25V14.25C5.25 15.3546 6.14543 16.25 7.25 16.25H14.25C15.3546 16.25 16.25 15.3546 16.25 14.25V7.25C16.25 6.14543 15.3546 5.25 14.25 5.25Z" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
                  <path d="M2.80103 11.998L1.77203 5.07397C1.61003 3.98097 2.36403 2.96397 3.45603 2.80197L10.38 1.77297C11.313 1.63397 12.19 2.16297 12.528 3.00097" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
                </svg>}
            </button>

            {}
            <div onClick={handleToggle} className="bg-zinc-200 dark:bg-zinc-600" style={{
    position: "relative",
    width: "56px",
    height: "28px",
    borderRadius: "14px",
    boxShadow: "inset -2px -2px 4px rgba(255,255,255,0.3), inset 2px 2px 4px rgba(0,0,0,0.1)",
    cursor: "pointer",
    transition: "all 0.3s ease"
  }}>
              {}
              <div className="bg-white dark:bg-zinc-300" style={{
    position: "absolute",
    width: "24px",
    height: "24px",
    borderRadius: "12px",
    top: "2px",
    left: showingFirst ? "30px" : "2px",
    boxShadow: "0 2px 4px rgba(0,0,0,0.2)",
    transition: "all 0.3s ease-in-out",
    display: "flex",
    alignItems: "center",
    justifyContent: "center"
  }}>
                {}
                <div style={{
    width: "6px",
    height: "6px",
    background: showingFirst ? "#0066ff" : "#999",
    borderRadius: "50%",
    boxShadow: showingFirst ? "0 0 5px 1px rgba(0, 102, 255, 0.6)" : "0 0 4px 1px rgba(0, 0, 0, 0.1)",
    transition: "all 0.3s ease-in-out"
  }} />
              </div>
            </div>
          </div>
        </div>

        {}
        <div ref={containerRef} className="p-0" style={{
    cursor: isDragging ? "grabbing" : "default"
  }} onTouchMove={handleTouchMove} tabIndex={0} onKeyDown={handleKeyDown} role="slider" aria-valuenow={sliderPercent} aria-valuemin={0} aria-valuemax={100} aria-label="Code comparison slider">
          <div className="relative" style={{
    minHeight: "140px",
    overflow: "hidden",
    height: containerHeight ? `${containerHeight}px` : "auto",
    transition: "height 0.3s ease"
  }}>
            <div style={{
    position: "relative"
  }}>
              {}
              <pre ref={secondPreRef} className="m-0 p-4 text-zinc-700 dark:text-white/80 bg-transparent" style={{
    position: showingFirst ? "absolute" : "relative",
    top: 0,
    left: 0,
    right: 0,
    fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    fontSize: "13px",
    lineHeight: "1.6",
    whiteSpace: "pre",
    zIndex: 1
  }} dangerouslySetInnerHTML={{
    __html: highlightCode(secondCode)
  }} />

              {}
              <pre ref={firstPreRef} className="m-0 p-4 text-zinc-700 dark:text-white/80 bg-white dark:bg-zinc-900" style={{
    position: showingFirst ? "relative" : "absolute",
    top: 0,
    left: 0,
    right: 0,
    fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
    fontSize: "13px",
    lineHeight: "1.6",
    whiteSpace: "pre",
    zIndex: 2,
    clipPath: `inset(0 ${100 - sliderPercent}% 0 0)`
  }} dangerouslySetInnerHTML={{
    __html: highlightCode(firstCode)
  }} />
            </div>

            {}
            <div className="absolute top-0 bottom-0 flex items-center justify-center pointer-events-none" style={{
    left: `${sliderPercent}%`,
    transform: "translateX(-50%)",
    zIndex: 30
  }}>
              <div className="absolute top-0 bottom-0 w-px bg-zinc-400 dark:bg-white/30" />

              <div className="absolute top-0 bottom-0" style={{
    right: "50%",
    width: "60px",
    background: "linear-gradient(to left, rgba(0, 102, 255, 0.15) 0%, transparent 100%)"
  }} />

              {}
              <div onMouseDown={handleMouseDown} className="pointer-events-auto cursor-grab flex items-center justify-center gap-px transition-transform" style={{
    width: "20px",
    height: "32px",
    borderRadius: "4px",
    background: "#f8fafc",
    border: "1px solid #d1d5db",
    boxShadow: "0 1px 2px rgba(0,0,0,0.05)",
    transform: isDragging ? "scale(1.08)" : "scale(1)"
  }}>
                <div className="flex flex-col gap-0.5">
                  {[0, 1, 2].map(i => <div key={i} style={{
    width: "3px",
    height: "3px",
    borderRadius: "50%",
    background: "#0066ff"
  }} />)}
                </div>
                <div className="flex flex-col gap-0.5">
                  {[0, 1, 2].map(i => <div key={i} style={{
    width: "3px",
    height: "3px",
    borderRadius: "50%",
    background: "#0066ff"
  }} />)}
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </>;
};

export const CompressibleRentCalculator = () => {
  const [hours, setHours] = useState(24);
  const [lamportsPerWrite, setLamportsPerWrite] = useState(766);
  const [showCustomHours, setShowCustomHours] = useState(false);
  const [showCustomLamports, setShowCustomLamports] = useState(false);
  const [showFormula, setShowFormula] = useState(false);
  const DATA_LEN = 272;
  const BASE_RENT = 128;
  const LAMPORTS_PER_BYTE_PER_EPOCH = 1;
  const MINUTES_PER_EPOCH = 90;
  const COMPRESSION_COST = 11000;
  const LAMPORTS_PER_SOL = 1_000_000_000;
  const HOURS_MAX = 36;
  const LAMPORTS_MAX = 6400;
  const numEpochs = Math.ceil(hours * 60 / MINUTES_PER_EPOCH);
  const rentPerEpoch = BASE_RENT + DATA_LEN * LAMPORTS_PER_BYTE_PER_EPOCH;
  const totalPrepaidRent = rentPerEpoch * numEpochs;
  const totalCreationCost = totalPrepaidRent + COMPRESSION_COST;
  const handleHoursChange = value => {
    const num = Math.max(3, Math.min(168, Number.parseInt(value) || 3));
    setHours(num);
  };
  const handleLamportsChange = value => {
    const num = Math.max(0, Math.min(100000, Number.parseInt(value) || 0));
    setLamportsPerWrite(num);
  };
  const hoursPresets = [24];
  const lamportsPresets = [766];
  const SliderMarkers = ({max, step}) => {
    const marks = [];
    for (let i = step; i < max; i += step) {
      const percent = i / max * 100;
      marks.push(<div key={i} className="absolute top-1/2 -translate-y-1/2 w-px h-2 bg-zinc-300 dark:bg-white/30" style={{
        left: `${percent}%`
      }} />);
    }
    return <>{marks}</>;
  };
  return <div className="p-5 rounded-3xl not-prose mt-4 dark:bg-white/5 backdrop-blur-xl border border-black/[0.04] dark:border-white/10 shadow-lg" style={{
    fontFamily: "Inter, sans-serif"
  }}>
      <div className="space-y-5">
        {}
        <div className="space-y-2 px-3">
          <div className="flex justify-between items-center">
            <span className="text-sm text-zinc-700 dark:text-white/80">
              Prepaid Epochs in Hours
            </span>
            <div className="flex items-center gap-1.5">
              {hoursPresets.map(h => <button key={h} onClick={() => {
    setHours(h);
    setShowCustomHours(false);
  }} className={`px-2.5 py-1 text-xs font-medium rounded-lg border backdrop-blur-sm transition-all ${hours === h && !showCustomHours ? "bg-blue-500/20 border-blue-500/50 text-blue-600 dark:text-blue-400" : "bg-black/[0.015] dark:bg-white/5 border-black/[0.04] dark:border-white/20 text-zinc-600 dark:text-white/70 hover:bg-black/[0.03]"}`}>
                  {h === 24 ? "Default" : `${h}h`}
                </button>)}
              {showCustomHours ? <input type="number" min="3" max="168" value={hours} onChange={e => handleHoursChange(e.target.value)} className="w-16 px-2 py-1 text-right text-xs font-mono font-medium bg-blue-500/10 dark:bg-blue-500/20 border border-blue-500/50 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" autoFocus /> : <button onClick={() => setShowCustomHours(true)} className="px-2.5 py-1 text-xs font-medium rounded-lg border backdrop-blur-sm transition-all bg-black/[0.015] dark:bg-white/5 border-black/[0.04] dark:border-white/20 text-zinc-600 dark:text-white/70 hover:bg-black/[0.03]">
                  Custom
                </button>}
            </div>
          </div>
          <div className="flex items-center">
            <span className="w-1/3 text-xs text-zinc-500 dark:text-white/50 whitespace-nowrap">
              ≈ {(hours * 60 / MINUTES_PER_EPOCH).toFixed(1)} epochs / {hours.toFixed(1)}h
            </span>
            <div className="w-2/3 relative">
              <SliderMarkers max={HOURS_MAX} step={2} />
              <input type="range" min="3" max={HOURS_MAX} value={Math.min(hours, HOURS_MAX)} onChange={e => {
    setHours(Number.parseInt(e.target.value));
    setShowCustomHours(false);
  }} className="relative w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm z-10" />
            </div>
          </div>
        </div>

        {}
        <div className="space-y-2 px-3">
          <div className="flex justify-between items-center">
            <span className="text-sm text-zinc-700 dark:text-white/80">Lamports per Write</span>
            <div className="flex items-center gap-1.5">
              {lamportsPresets.map(l => <button key={l} onClick={() => {
    setLamportsPerWrite(l);
    setShowCustomLamports(false);
  }} className={`px-2.5 py-1 text-xs font-medium rounded-lg border backdrop-blur-sm transition-all ${lamportsPerWrite === l && !showCustomLamports ? "bg-blue-500/20 border-blue-500/50 text-blue-600 dark:text-blue-400" : "bg-black/[0.015] dark:bg-white/5 border-black/[0.04] dark:border-white/20 text-zinc-600 dark:text-white/70 hover:bg-black/[0.03]"}`}>
                  {l === 766 ? "Default" : l.toLocaleString()}
                </button>)}
              {showCustomLamports ? <input type="number" min="0" max="100000" value={lamportsPerWrite} onChange={e => handleLamportsChange(e.target.value)} className="w-20 px-2 py-1 text-right text-xs font-mono font-medium bg-blue-500/10 dark:bg-blue-500/20 border border-blue-500/50 rounded-lg backdrop-blur-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50" autoFocus /> : <button onClick={() => setShowCustomLamports(true)} className="px-2.5 py-1 text-xs font-medium rounded-lg border backdrop-blur-sm transition-all bg-black/[0.015] dark:bg-white/5 border-black/[0.04] dark:border-white/20 text-zinc-600 dark:text-white/70 hover:bg-black/[0.03]">
                  Custom
                </button>}
            </div>
          </div>
          <div className="flex items-center">
            <span className="w-1/3 text-xs text-zinc-500 dark:text-white/50 whitespace-nowrap">
              ≈ {(lamportsPerWrite / rentPerEpoch).toFixed(1)} epochs /{" "}
              {(lamportsPerWrite / rentPerEpoch * MINUTES_PER_EPOCH / 60).toFixed(1)}h
            </span>
            <div className="w-2/3 relative">
              <SliderMarkers max={LAMPORTS_MAX} step={800} />
              <input type="range" min="0" max={LAMPORTS_MAX} step="100" value={Math.min(lamportsPerWrite, LAMPORTS_MAX)} onChange={e => {
    setLamportsPerWrite(Number.parseInt(e.target.value));
    setShowCustomLamports(false);
  }} className="relative w-full h-1.5 bg-black/[0.03] dark:bg-white/20 rounded-full appearance-none cursor-pointer backdrop-blur-sm z-10" />
            </div>
          </div>
        </div>

        {}
        <div className="grid grid-cols-2 gap-3">
          <div className="p-4 bg-black/[0.015] dark:bg-white/5 backdrop-blur-md rounded-2xl text-center border border-black/[0.04] dark:border-white/10 shadow-sm">
            <div className="text-xs text-zinc-500 dark:text-white/50 mb-1 uppercase tracking-wide">
              Total Creation Cost
            </div>
            <div className="text-xl font-mono font-semibold text-zinc-900 dark:text-white">
              {totalCreationCost.toLocaleString()}
            </div>
            <div className="text-xs text-zinc-400 dark:text-white/40">lamports</div>
            <div className="text-xs text-zinc-500 dark:text-white/50 mt-1">
              ≈ {(totalCreationCost / LAMPORTS_PER_SOL).toFixed(6)} SOL
            </div>
          </div>

          <div className="p-4 bg-black/[0.015] dark:bg-white/5 backdrop-blur-md rounded-2xl text-center border border-black/[0.04] dark:border-white/10 shadow-sm">
            <div className="text-xs text-zinc-500 dark:text-white/50 mb-1 uppercase tracking-wide">
              Top-up Amount
            </div>
            <div className="text-xl font-mono font-semibold text-zinc-900 dark:text-white">
              {lamportsPerWrite.toLocaleString()}
            </div>
            <div className="text-xs text-zinc-400 dark:text-white/40">lamports</div>
            <div className="text-xs text-zinc-500 dark:text-white/50 mt-1">
              ≈ {(lamportsPerWrite / LAMPORTS_PER_SOL).toFixed(6)} SOL
            </div>
          </div>
        </div>

        {}
        <div className="pt-3 border-t border-black/[0.04] dark:border-white/10">
          <button onClick={() => setShowFormula(!showFormula)} className="flex items-center gap-2 text-xs text-zinc-500 dark:text-white/50 hover:text-zinc-700 dark:hover:text-white/70 transition-colors">
            <span className={`transition-transform ${showFormula ? "rotate-90" : ""}`}>▶</span>
            Show formula
          </button>
          {showFormula && <div className="text-xs font-mono text-zinc-500 dark:text-white/40 mt-3">
              <div className="text-zinc-600 dark:text-white/60 mb-2">
                Total cost for {DATA_LEN}-byte light-token account:
              </div>
              total_creation_cost = prepaid_rent + compression_cost
              <br />
              <br />
              rent_per_epoch = base_rent + (data_len × lamports_per_byte_per_epoch)
              <br />
              rent_per_epoch = {BASE_RENT} + ({DATA_LEN} × {LAMPORTS_PER_BYTE_PER_EPOCH}) ={" "}
              {rentPerEpoch} lamports
              <br />
              compression_cost = {COMPRESSION_COST.toLocaleString()} lamports
            </div>}
        </div>
      </div>
    </div>;
};

***

1. Mint accounts uniquely represent a token on Solana and store its global metadata.
2. Light mints are on-chain accounts like SPL mints, but the light token program sponsors the rent-exemption cost for you.
3. Add an interface PDA to existing SPL or Token 2022 mints for interoperability with Light Token. The interface PDA holds SPL or Token 2022 tokens when wrapped to Light Token.

<Accordion title="Light Rent Config Explained">
  1) A rent sponsor PDA by Light Protocol pays the rent-exemption cost for the account.
  2) Transaction fee payers bump a virtual rent balance when writing to the account, which keeps the account "hot".
  3) "Cold" accounts virtual rent balance below threshold (eg 24h without write bump) get auto-compressed.
  4) The cold account's state is cryptographically preserved on the Solana ledger.
     Users can load a cold account into hot state in-flight when using the account
     again.
</Accordion>

<Accordion title="Agent skill">
  Install the [agent skill](https://zkcompression.com/skill.md):

  ```bash theme={null}
  npx skills add https://zkcompression.com
  ```

  See the [AI tools guide](/ai-tools/guide) for dedicated skills.
</Accordion>

<Tabs>
  <Tab title="TypeScript Client">
    `createMintInterface` is a unified interface that dispatches to different mint creation paths based on programId:

    * `TOKEN_PROGRAM_ID` or `TOKEN_2022_PROGRAM_ID` → delegates to SPL or T22 `createMint`
    * Otherwise it defaults to `LIGHT_TOKEN_PROGRAM_ID` → creates a Light Token mint

    You can use the same interface regardless of mint type.

    Compare to SPL:

    <CodeCompare firstCode={lightCreateMintCode} secondCode={splCreateMintCode} firstLabel="light-token" secondLabel="SPL" />

    <Info>
      Find the source code
      [here](https://github.com/Lightprotocol/light-protocol/blob/main/js/compressed-token/src/v3/actions/create-mint-interface.ts).
    </Info>

    <Tabs>
      <Tab title="Guide">
        <Steps>
          <Step>
            ### Create Mint with Token Metadata

            <Accordion title="Installation">
              <Tabs>
                <Tab title="npm">
                  Install packages in your working directory:

                  ```bash theme={null}
                  npm install @lightprotocol/stateless.js@beta \
                              @lightprotocol/compressed-token@beta
                  ```

                  Install the CLI globally:

                  ```bash theme={null}
                  npm install -g @lightprotocol/zk-compression-cli@beta
                  ```
                </Tab>

                <Tab title="yarn">
                  Install packages in your working directory:

                  ```bash theme={null}
                  yarn add @lightprotocol/stateless.js@beta \
                           @lightprotocol/compressed-token@beta
                  ```

                  Install the CLI globally:

                  ```bash theme={null}
                  yarn global add @lightprotocol/zk-compression-cli@beta
                  ```
                </Tab>

                <Tab title="pnpm">
                  Install packages in your working directory:

                  ```bash theme={null}
                  pnpm add @lightprotocol/stateless.js@beta \
                           @lightprotocol/compressed-token@beta
                  ```

                  Install the CLI globally:

                  ```bash theme={null}
                  pnpm add -g @lightprotocol/zk-compression-cli@beta
                  ```
                </Tab>
              </Tabs>
            </Accordion>

            <Tabs>
              <Tab title="Localnet">
                ```bash theme={null}
                # start local test-validator in a separate terminal
                light test-validator
                ```

                <Note>
                  In the code examples, use `createRpc()` without arguments for localnet.
                </Note>
              </Tab>

              <Tab title="Devnet">
                Get an API key from [Helius](https://helius.dev) and add to `.env`:

                ```bash title=".env" theme={null}
                API_KEY=<your-helius-api-key>
                ```

                <Note>
                  In the code examples, use `createRpc(RPC_URL)` with the devnet URL.
                </Note>
              </Tab>
            </Tabs>

            <Note>
              The `mintAuthority` must be a `Signer` for light-mints but can be just a
              `PublicKey` for SPL/T22.
            </Note>

            <Tabs>
              <Tab title="Action">
                ```typescript theme={null}
                import "dotenv/config";
                import { Keypair } from "@solana/web3.js";
                import { createRpc } from "@lightprotocol/stateless.js";
                import { createMintInterface, createTokenMetadata } from "@lightprotocol/compressed-token";
                import { homedir } from "os";
                import { readFileSync } from "fs";

                // devnet:
                const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
                const rpc = createRpc(RPC_URL);
                // localnet:
                // const rpc = createRpc();

                const payer = Keypair.fromSecretKey(
                    new Uint8Array(
                        JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8"))
                    )
                );

                (async function () {
                    const { mint, transactionSignature } = await createMintInterface(
                        rpc,
                        payer,
                        payer,
                        null,
                        9,
                        undefined,
                        undefined,
                        undefined,
                        createTokenMetadata("Example Token", "EXT", "https://example.com/metadata.json")
                    );

                    console.log("Mint:", mint.toBase58());
                    console.log("Tx:", transactionSignature);
                })();
                ```
              </Tab>

              <Tab title="Instruction">
                ```typescript theme={null}
                import "dotenv/config";
                import {
                    Keypair,
                    ComputeBudgetProgram,
                    PublicKey,
                    Transaction,
                    sendAndConfirmTransaction,
                } from "@solana/web3.js";
                import {
                    createRpc,
                    getBatchAddressTreeInfo,
                    selectStateTreeInfo,
                    CTOKEN_PROGRAM_ID,
                } from "@lightprotocol/stateless.js";
                import {
                    createMintInstruction,
                    createTokenMetadata,
                } from "@lightprotocol/compressed-token";
                import { homedir } from "os";
                import { readFileSync } from "fs";

                const COMPRESSED_MINT_SEED = Buffer.from("compressed_mint");

                function findMintAddress(mintSigner: PublicKey): [PublicKey, number] {
                    return PublicKey.findProgramAddressSync(
                        [COMPRESSED_MINT_SEED, mintSigner.toBuffer()],
                        CTOKEN_PROGRAM_ID,
                    );
                }

                // devnet:
                // const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
                // const rpc = createRpc(RPC_URL);
                // localnet:
                const rpc = createRpc();

                const payer = Keypair.fromSecretKey(
                    new Uint8Array(
                        JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")),
                    ),
                );

                (async function () {
                    const mintSigner = Keypair.generate();
                    const addressTreeInfo = getBatchAddressTreeInfo();
                    const stateTreeInfo = selectStateTreeInfo(await rpc.getStateTreeInfos());
                    const [mintPda] = findMintAddress(mintSigner.publicKey);

                    const validityProof = await rpc.getValidityProofV2(
                        [],
                        [{ address: mintPda.toBytes(), treeInfo: addressTreeInfo }],
                    );

                    const ix = createMintInstruction(
                        mintSigner.publicKey,
                        9,
                        payer.publicKey,
                        null,
                        payer.publicKey,
                        validityProof,
                        addressTreeInfo,
                        stateTreeInfo,
                        createTokenMetadata(
                            "Example Token",
                            "EXT",
                            "https://example.com/metadata.json",
                        ),
                    );

                    const tx = new Transaction().add(
                        ComputeBudgetProgram.setComputeUnitLimit({ units: 1_000_000 }),
                        ix,
                    );
                    const signature = await sendAndConfirmTransaction(rpc, tx, [
                        payer,
                        mintSigner,
                    ]);

                    console.log("Mint:", mintPda.toBase58());
                    console.log("Tx:", signature);
                })();
                ```
              </Tab>
            </Tabs>
          </Step>
        </Steps>
      </Tab>

      <Tab title="AI Prompt">
        <Prompt description="Add rent-free mint with token metadata" actions={["copy", "cursor"]}>
          {`---
                    description: Add rent-free mint with token metadata
                    allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
                    ---

                    ## Add rent-free mint with token metadata

                    Context:
                    - Guide: https://zkcompression.com/light-token/cookbook/create-mint
                    - Skills and resources index: https://zkcompression.com/skill.md
                    - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js

                    SPL equivalent: createMint() → Light Token: createMintInterface()

                    ### 1. Index project
                    - Grep \`@solana/spl-token|Connection|Keypair|createMint\` across src/
                    - Glob \`**/*.ts\` for project structure
                    - Identify: RPC setup, existing mint logic, entry point for mint creation
                    - Task subagent (Grep/Read/WebFetch) if project has multiple packages to scan in parallel

                    ### 2. Read references
                    - WebFetch the guide above — follow the TypeScript Client tab
                    - WebFetch skill.md — check for a dedicated skill and resources matching this task
                    - TaskCreate one todo per phase below to track progress

                    ### 3. Clarify intention
                    - AskUserQuestion: what is the goal? (new feature, migrate existing SPL code, add alongside existing)
                    - AskUserQuestion: does the project already have mint operations to extend, or is this greenfield?
                    - Summarize findings and wait for user confirmation before implementing

                    ### 4. Create plan
                    - Based on steps 1–3, draft an implementation plan: which files to modify, what code to add, dependency changes
                    - Verify existing connection/signer setup is compatible with the cookbook prerequisites
                    - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
                    - Present the plan to the user for approval before proceeding

                    ### 5. Implement
                    - Add deps if missing: Bash \`npm install @lightprotocol/compressed-token @lightprotocol/stateless.js\`
                    - Follow the cookbook guide and the approved plan
                    - Write/Edit to create or modify files
                    - TaskUpdate to mark each step done

                    ### 6. Verify
                    - Bash \`tsc --noEmit\`
                    - Bash run existing test suite if present
                    - TaskUpdate to mark complete

                    ### Tools
                    - mcp__zkcompression__SearchLightProtocol("<query>") for API details
                    - mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
                    - Task subagent with Grep/Read/WebFetch for parallel lookups
                    - TaskList to check remaining work`}
        </Prompt>

        ```text theme={null}
        ---
        description: Add rent-free mint with token metadata
        allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
        ---

        ## Add rent-free mint with token metadata

        Context:
        - Guide: https://zkcompression.com/light-token/cookbook/create-mint
        - Skills and resources index: https://zkcompression.com/skill.md
        - Packages: @lightprotocol/compressed-token, @lightprotocol/stateless.js

        SPL equivalent: createMint() → Light Token: createMintInterface()

        ### 1. Index project
        - Grep `@solana/spl-token|Connection|Keypair|createMint` across src/
        - Glob `**/*.ts` for project structure
        - Identify: RPC setup, existing mint logic, entry point for mint creation
        - Task subagent (Grep/Read/WebFetch) if project has multiple packages to scan in parallel

        ### 2. Read references
        - WebFetch the guide above — follow the TypeScript Client tab
        - WebFetch skill.md — check for a dedicated skill and resources matching this task
        - TaskCreate one todo per phase below to track progress

        ### 3. Clarify intention
        - AskUserQuestion: what is the goal? (new feature, migrate existing SPL code, add alongside existing)
        - AskUserQuestion: does the project already have mint operations to extend, or is this greenfield?
        - Summarize findings and wait for user confirmation before implementing

        ### 4. Create plan
        - Based on steps 1–3, draft an implementation plan: which files to modify, what code to add, dependency changes
        - Verify existing connection/signer setup is compatible with the cookbook prerequisites
        - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
        - Present the plan to the user for approval before proceeding

        ### 5. Implement
        - Add deps if missing: Bash `npm install @lightprotocol/compressed-token @lightprotocol/stateless.js`
        - Follow the cookbook guide and the approved plan
        - Write/Edit to create or modify files
        - TaskUpdate to mark each step done

        ### 6. Verify
        - Bash `tsc --noEmit`
        - Bash run existing test suite if present
        - TaskUpdate to mark complete

        ### Tools
        - mcp__zkcompression__SearchLightProtocol("<query>") for API details
        - mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
        - Task subagent with Grep/Read/WebFetch for parallel lookups
        - TaskList to check remaining work
        ```
      </Tab>
    </Tabs>

    ### Add Interface PDA to SPL / Token 2022 mints

    <Tabs>
      <Tab title="Add to Existing Mints">
        Register an interface PDA for an existing SPL mint for interoperability with Light Token. No mint authority required.

        <Tabs>
          <Tab title="Action">
            ```typescript theme={null}
            import "dotenv/config";
            import { Keypair, PublicKey } from "@solana/web3.js";
            import { createRpc } from "@lightprotocol/stateless.js";
            import { createSplInterface } from "@lightprotocol/compressed-token";
            import { homedir } from "os";
            import { readFileSync } from "fs";

            // devnet:
            const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
            const rpc = createRpc(RPC_URL);
            // localnet:
            // const rpc = createRpc();

            const payer = Keypair.fromSecretKey(
                new Uint8Array(
                    JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8"))
                )
            );

            (async function () {
                const existingMint = new PublicKey("YOUR_EXISTING_MINT_ADDRESS");

                const tx = await createSplInterface(rpc, payer, existingMint);

                console.log("Mint:", existingMint.toBase58());
                console.log("Tx:", tx);
            })();
            ```
          </Tab>

          <Tab title="Instruction">
            ```typescript theme={null}
            import "dotenv/config";
            import {
                Keypair,
                PublicKey,
                Transaction,
                sendAndConfirmTransaction,
            } from "@solana/web3.js";
            import { createRpc } from "@lightprotocol/stateless.js";
            import { LightTokenProgram } from "@lightprotocol/compressed-token";
            import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
            import { homedir } from "os";
            import { readFileSync } from "fs";

            // devnet:
            const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
            const rpc = createRpc(RPC_URL);
            // localnet:
            // const rpc = createRpc();

            const payer = Keypair.fromSecretKey(
                new Uint8Array(
                    JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8"))
                )
            );

            (async function () {
                const existingMint = new PublicKey("YOUR_EXISTING_MINT_ADDRESS");

                const ix = await LightTokenProgram.createSplInterface({
                    feePayer: payer.publicKey,
                    mint: existingMint,
                    tokenProgramId: TOKEN_PROGRAM_ID,
                });

                const tx = new Transaction().add(ix);
                const signature = await sendAndConfirmTransaction(rpc, tx, [payer]);

                console.log("Mint:", existingMint.toBase58());
                console.log("Tx:", signature);
            })();
            ```
          </Tab>
        </Tabs>
      </Tab>

      <Tab title="Create SPL Mint with Interface PDA">
        Pass `TOKEN_PROGRAM_ID` to create a standard SPL mint with an interface PDA in one transaction for interoperability with Light Token.

        <Tabs>
          <Tab title="Action">
            ```typescript theme={null}
            import "dotenv/config";
            import { Keypair } from "@solana/web3.js";
            import { createRpc } from "@lightprotocol/stateless.js";
            import { createMintInterface } from "@lightprotocol/compressed-token";
            import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
            import { homedir } from "os";
            import { readFileSync } from "fs";

            // devnet:
            const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
            const rpc = createRpc(RPC_URL);
            // localnet:
            // const rpc = createRpc();

            const payer = Keypair.fromSecretKey(
                new Uint8Array(
                    JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8"))
                )
            );

            (async function () {
                // Creates SPL mint + SPL Interface PDA in one transaction
                const mintKeypair = Keypair.generate();
                const { mint, transactionSignature } = await createMintInterface(
                    rpc,
                    payer,
                    payer,
                    null,
                    9,
                    mintKeypair,
                    undefined,
                    TOKEN_PROGRAM_ID
                );

                console.log("Mint:", mint.toBase58());
                console.log("Tx:", transactionSignature);
            })();
            ```
          </Tab>

          <Tab title="Instruction">
            ```typescript theme={null}
            import "dotenv/config";
            import {
                Keypair,
                SystemProgram,
                Transaction,
                sendAndConfirmTransaction,
            } from "@solana/web3.js";
            import { createRpc } from "@lightprotocol/stateless.js";
            import { LightTokenProgram } from "@lightprotocol/compressed-token";
            import {
                MINT_SIZE,
                TOKEN_PROGRAM_ID,
                createInitializeMint2Instruction,
            } from "@solana/spl-token";
            import { homedir } from "os";
            import { readFileSync } from "fs";

            // devnet:
            const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
            const rpc = createRpc(RPC_URL);
            // localnet:
            // const rpc = createRpc();

            const payer = Keypair.fromSecretKey(
                new Uint8Array(
                    JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8"))
                )
            );

            (async function () {
                const mintKeypair = Keypair.generate();
                const decimals = 9;

                // Get rent for mint account
                const rentExemptBalance = await rpc.getMinimumBalanceForRentExemption(MINT_SIZE);

                // Instruction 1: Create mint account
                const createMintAccountIx = SystemProgram.createAccount({
                    fromPubkey: payer.publicKey,
                    lamports: rentExemptBalance,
                    newAccountPubkey: mintKeypair.publicKey,
                    programId: TOKEN_PROGRAM_ID,
                    space: MINT_SIZE,
                });

                // Instruction 2: Initialize mint
                const initializeMintIx = createInitializeMint2Instruction(
                    mintKeypair.publicKey,
                    decimals,
                    payer.publicKey, // mint authority
                    null, // freeze authority
                    TOKEN_PROGRAM_ID
                );

                // Instruction 3: Create SPL Interface PDA (enables compression for this mint)
                const createSplInterfaceIx = await LightTokenProgram.createSplInterface({
                    feePayer: payer.publicKey,
                    mint: mintKeypair.publicKey,
                    tokenProgramId: TOKEN_PROGRAM_ID,
                });

                const tx = new Transaction().add(
                    createMintAccountIx,
                    initializeMintIx,
                    createSplInterfaceIx
                );

                const signature = await sendAndConfirmTransaction(rpc, tx, [
                    payer,
                    mintKeypair,
                ]);

                console.log("Mint:", mintKeypair.publicKey.toBase58());
                console.log("Tx:", signature);
            })();
            ```
          </Tab>
        </Tabs>
      </Tab>

      <Tab title="Create Token 2022 with Interface PDA">
        Pass `TOKEN_2022_PROGRAM_ID` to create a Token-2022 mint with an interface PDA in one transaction for interoperability with Light Token.

        <Tabs>
          <Tab title="Action">
            ```typescript theme={null}
            import "dotenv/config";
            import { Keypair } from "@solana/web3.js";
            import { createRpc } from "@lightprotocol/stateless.js";
            import { createMintInterface } from "@lightprotocol/compressed-token";
            import { TOKEN_2022_PROGRAM_ID } from "@solana/spl-token";
            import { homedir } from "os";
            import { readFileSync } from "fs";

            // devnet:
            const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
            const rpc = createRpc(RPC_URL);
            // localnet:
            // const rpc = createRpc();

            const payer = Keypair.fromSecretKey(
                new Uint8Array(
                    JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8"))
                )
            );

            (async function () {
                // Creates Token-2022 mint and SPL interface PDA in one transaction
                // SPL interface PDA holds Token-2022 tokens when wrapped to light-token
                const mintKeypair = Keypair.generate();
                const { mint, transactionSignature } = await createMintInterface(
                    rpc,
                    payer,
                    payer,
                    null,
                    9,
                    mintKeypair,
                    undefined,
                    TOKEN_2022_PROGRAM_ID
                );

                console.log("Mint:", mint.toBase58());
                console.log("Tx:", transactionSignature);
            })();
            ```
          </Tab>

          <Tab title="Instruction">
            ```typescript theme={null}
            import "dotenv/config";
            import {
                Keypair,
                SystemProgram,
                Transaction,
                sendAndConfirmTransaction,
            } from "@solana/web3.js";
            import { createRpc } from "@lightprotocol/stateless.js";
            import { LightTokenProgram } from "@lightprotocol/compressed-token";
            import {
                MINT_SIZE,
                TOKEN_2022_PROGRAM_ID,
                createInitializeMint2Instruction,
            } from "@solana/spl-token";
            import { homedir } from "os";
            import { readFileSync } from "fs";

            // devnet:
            const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`;
            const rpc = createRpc(RPC_URL);
            // localnet:
            // const rpc = createRpc();

            const payer = Keypair.fromSecretKey(
                new Uint8Array(
                    JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8"))
                )
            );

            (async function () {
                const mintKeypair = Keypair.generate();
                const decimals = 9;

                // Get rent for mint account
                const rentExemptBalance =
                    await rpc.getMinimumBalanceForRentExemption(MINT_SIZE);

                // Instruction 1: Create mint account
                const createMintAccountIx = SystemProgram.createAccount({
                    fromPubkey: payer.publicKey,
                    lamports: rentExemptBalance,
                    newAccountPubkey: mintKeypair.publicKey,
                    programId: TOKEN_2022_PROGRAM_ID,
                    space: MINT_SIZE,
                });

                // Instruction 2: Initialize mint
                const initializeMintIx = createInitializeMint2Instruction(
                    mintKeypair.publicKey,
                    decimals,
                    payer.publicKey, // mint authority
                    null, // freeze authority
                    TOKEN_2022_PROGRAM_ID
                );

                // Instruction 3: Create SPL interface PDA
                // Holds Token-2022 tokens when wrapped to light-token
                const createSplInterfaceIx = await LightTokenProgram.createSplInterface({
                    feePayer: payer.publicKey,
                    mint: mintKeypair.publicKey,
                    tokenProgramId: TOKEN_2022_PROGRAM_ID,
                });

                const tx = new Transaction().add(
                    createMintAccountIx,
                    initializeMintIx,
                    createSplInterfaceIx
                );

                const signature = await sendAndConfirmTransaction(rpc, tx, [
                    payer,
                    mintKeypair,
                ]);

                console.log("Mint:", mintKeypair.publicKey.toBase58());
                console.log("Tx:", signature);
            })();
            ```
          </Tab>
        </Tabs>
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Rust Client">
    `CreateMint` creates an on-chain mint account that can optionally include token metadata.
    The instruction also writes a compressed mint address to the address Merkle tree, which preserves the mint state when the on-chain account is compressed.

    Compare to SPL:

    <CodeCompare firstCode={lightCreateMintRustCode} secondCode={splCreateMintRustCode} firstLabel="light-token" secondLabel="SPL" language="rust" />

    <Tabs>
      <Tab title="Guide">
        <Steps>
          <Step>
            ### Prerequisites

            <Accordion title="Dependencies">
              ```toml Cargo.toml theme={null}
              [dependencies]
              light-token = "0.4.0"
              light-client = { version = "0.19.0", features = ["v2"] }
              solana-sdk = "2"
              borsh = "0.10.4"
              tokio = { version = "1", features = ["full"] }
              ```
            </Accordion>

            <Accordion title="Developer Environment">
              <Tabs>
                <Tab title="In-Memory (LightProgramTest)">
                  Test with Lite-SVM (...)

                  ```bash theme={null}
                  # Initialize project
                  cargo init my-light-project
                  cd my-light-project

                  # Run tests
                  cargo test
                  ```

                  ```rust theme={null}
                  use light_program_test::{LightProgramTest, ProgramTestConfig};
                  use solana_sdk::signer::Signer;

                  #[tokio::test]
                  async fn test_example() {
                      // In-memory test environment 
                      let mut rpc = LightProgramTest::new(ProgramTestConfig::default())
                          .await
                          .unwrap();

                      let payer = rpc.get_payer().insecure_clone();
                      println!("Payer: {}", payer.pubkey());
                  }
                  ```
                </Tab>

                <Tab title="Localnet (LightClient)">
                  Connects to a local test validator.

                  <Tabs>
                    <Tab title="npm">
                      ```bash theme={null}
                      npm install -g @lightprotocol/zk-compression-cli@beta
                      ```
                    </Tab>

                    <Tab title="yarn">
                      ```bash theme={null}
                      yarn global add @lightprotocol/zk-compression-cli@beta
                      ```
                    </Tab>

                    <Tab title="pnpm">
                      ```bash theme={null}
                      pnpm add -g @lightprotocol/zk-compression-cli@beta
                      ```
                    </Tab>
                  </Tabs>

                  ```bash theme={null}
                  # Initialize project
                  cargo init my-light-project
                  cd my-light-project

                  # Start local test validator (in separate terminal)
                  light test-validator
                  ```

                  ```rust theme={null}
                  use light_client::rpc::{LightClient, LightClientConfig, Rpc};

                  #[tokio::main]
                  async fn main() -> Result<(), Box<dyn std::error::Error>> {
                      // Connects to http://localhost:8899
                      let rpc = LightClient::new(LightClientConfig::local()).await?;

                      let slot = rpc.get_slot().await?;
                      println!("Current slot: {}", slot);

                      Ok(())
                  }
                  ```
                </Tab>

                <Tab title="Devnet (LightClient)">
                  Replace `<your-api-key>` with your actual API key. [Get your API key here](https://www.helius.dev/zk-compression).

                  ```rust theme={null}
                  use light_client::rpc::{LightClient, LightClientConfig, Rpc};

                  #[tokio::main]
                  async fn main() -> Result<(), Box<dyn std::error::Error>> {
                      let rpc_url = "https://devnet.helius-rpc.com?api-key=<your_api_key>";
                      let rpc = LightClient::new(
                          LightClientConfig::new(rpc_url.to_string(), None, None)
                      ).await?;

                      println!("Connected to Devnet");
                      Ok(())
                  }
                  ```
                </Tab>
              </Tabs>
            </Accordion>
          </Step>

          <Step>
            ### Create Light Token Mint with Token Metadata

            <Info>
              Find the source code [here](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-libs/token-sdk/src/instruction/create_mint.rs).
            </Info>

            <Tabs>
              <Tab title="Action">
                ```rust theme={null}
                use light_client::rpc::Rpc;
                use light_token_client::actions::{CreateMint, TokenMetadata};
                use rust_client::setup_rpc_and_payer;
                use solana_sdk::signer::Signer;

                #[tokio::main]
                async fn main() -> Result<(), Box<dyn std::error::Error>> {
                    let (mut rpc, payer) = setup_rpc_and_payer().await;

                    let (signature, mint) = CreateMint {
                        decimals: 9,
                        freeze_authority: None,
                        token_metadata: Some(TokenMetadata {
                            name: "Example Token".to_string(),
                            symbol: "EXT".to_string(),
                            uri: "https://example.com/metadata.json".to_string(),
                            update_authority: Some(payer.pubkey()),
                            additional_metadata: Some(vec![("type".to_string(), "example".to_string())]),
                        }),
                        seed: None,
                    }
                    .execute(&mut rpc, &payer, &payer)
                    .await?;

                    let data = rpc.get_account(mint).await?;
                    println!("Mint: {mint} exists: {} Tx: {signature}", data.is_some());

                    Ok(())
                }
                ```
              </Tab>

              <Tab title="Instruction">
                ```rust theme={null}
                use light_client::{
                    indexer::{AddressWithTree, Indexer},
                    rpc::Rpc,
                };
                use light_program_test::{LightProgramTest, ProgramTestConfig};
                use light_token::instruction::{
                    derive_mint_compressed_address, find_mint_address, CreateMint, CreateMintParams,
                    DEFAULT_RENT_PAYMENT, DEFAULT_WRITE_TOP_UP,
                };
                use light_token_interface::{
                    instructions::extensions::{
                        token_metadata::TokenMetadataInstructionData, ExtensionInstructionData,
                    },
                    state::AdditionalMetadata,
                };
                use solana_sdk::{signature::Keypair, signer::Signer};

                #[tokio::main]
                async fn main() -> Result<(), Box<dyn std::error::Error>> {
                    let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None)).await?;

                    let payer = rpc.get_payer().insecure_clone();
                    let mint_seed = Keypair::new();
                    let decimals = 9u8;

                    // Get address tree to store compressed address for when mint turns inactive
                    // We must create a compressed address at creation to ensure the mint does not exist yet
                    let address_tree = rpc.get_address_tree_v2();
                    // Get state tree to store mint when inactive
                    let output_queue = rpc.get_random_state_tree_info().unwrap().queue;

                    // Derive mint addresses
                    let compression_address =
                        derive_mint_compressed_address(&mint_seed.pubkey(), &address_tree.tree);
                    let mint = find_mint_address(&mint_seed.pubkey()).0; // on-chain Mint PDA

                    // Fetch validity proof to proof address does not exist yet
                    let rpc_result = rpc
                        .get_validity_proof(
                            vec![],
                            vec![AddressWithTree {
                                address: compression_address,
                                tree: address_tree.tree,
                            }],
                            None,
                        )
                        .await
                        .unwrap()
                        .value;

                    // Build CreateMintParams with token metadata extension
                    let params = CreateMintParams {
                        decimals,
                        address_merkle_tree_root_index: rpc_result.addresses[0].root_index, // stores mint compressed address
                        mint_authority: payer.pubkey(),
                        proof: rpc_result.proof.0.unwrap(),
                        compression_address, // address for compression when mint turns inactive
                        mint,
                        bump: find_mint_address(&mint_seed.pubkey()).1,
                        freeze_authority: None,
                        extensions: Some(vec![ExtensionInstructionData::TokenMetadata(
                            TokenMetadataInstructionData {
                                update_authority: Some(payer.pubkey().to_bytes().into()),
                                name: b"Example Token".to_vec(),
                                symbol: b"EXT".to_vec(),
                                uri: b"https://example.com/metadata.json".to_vec(),
                                additional_metadata: Some(vec![AdditionalMetadata {
                                    key: b"type".to_vec(),
                                    value: b"example".to_vec(),
                                }]),
                            },
                        )]),
                        rent_payment: DEFAULT_RENT_PAYMENT, // 24h of rent
                        write_top_up: DEFAULT_WRITE_TOP_UP, // 3h of rent
                    };

                    // Build and send instruction (mint_seed must sign)
                    let create_mint_instruction = CreateMint::new(
                        params,
                        mint_seed.pubkey(),
                        payer.pubkey(),
                        address_tree.tree,
                        output_queue,
                    )
                    .instruction()?;

                    let sig = rpc
                        .create_and_send_transaction(&[create_mint_instruction], &payer.pubkey(), &[&payer, &mint_seed])
                        .await?;

                    let data = rpc.get_account(mint).await?;
                    println!("Mint: {} exists: {} Tx: {sig}", mint, data.is_some());

                    Ok(())
                }
                ```
              </Tab>
            </Tabs>
          </Step>
        </Steps>
      </Tab>

      <Tab title="AI Prompt">
        <Prompt description="Create rent-free mint with token metadata" actions={["copy", "cursor"]}>
          {`---
                    description: Create rent-free mint with token metadata
                    allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
                    ---

                    ## Create rent-free mint with token metadata

                    Context:
                    - Guide: https://zkcompression.com/light-token/cookbook/create-mint
                    - Skills and resources index: https://zkcompression.com/skill.md
                    - Crates: light-token-client (actions), light-token (instructions), light-client (RPC)

                    SPL equivalent: spl_token::instruction::initialize_mint → Light Token: CreateMint

                    ### 1. Index project
                    - Grep \`light_token::|light_token_client::|solana_sdk|Keypair|async|CreateMint|initialize_mint\` across src/
                    - Glob \`**/*.rs\` for project structure
                    - Identify: RPC setup, existing token ops, entry point for mint creation
                    - Check Cargo.toml for existing light-* dependencies and solana-sdk version
                    - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

                    ### 2. Read references
                    - WebFetch the guide above — follow the Rust Client tab
                    - WebFetch skill.md — check for a dedicated skill and resources matching this task
                    - TaskCreate one todo per phase below to track progress

                    ### 3. Clarify intention
                    - AskUserQuestion: what is the goal? (new feature, migrate existing SPL code, add alongside existing)
                    - AskUserQuestion: does the project already have mint operations to extend, or is this greenfield?
                    - AskUserQuestion: action-level API (high-level, fewer lines) or instruction-level API (low-level, full control)?
                    - Summarize findings and wait for user confirmation before implementing

                    ### 4. Create plan
                    - Based on steps 1–3, draft an implementation plan: which files to modify, what code to add, dependency changes
                    - Verify existing Rpc/signer setup is compatible with the cookbook prerequisites (light_client::rpc::Rpc, solana_sdk::signature::Keypair)
                    - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
                    - Present the plan to the user for approval before proceeding

                    ### 5. Implement
                    - Add deps if missing: Bash \`cargo add light-token-client light-token light-client --features light-client/v2\`
                    - Follow the cookbook guide and the approved plan
                    - Write/Edit to create or modify files
                    - TaskUpdate to mark each step done

                    ### 6. Verify
                    - Bash \`cargo check\`
                    - Bash \`cargo test\` if tests exist
                    - TaskUpdate to mark complete

                    ### Tools
                    - mcp__zkcompression__SearchLightProtocol("<query>") for API details
                    - mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
                    - Task subagent with Grep/Read/WebFetch for parallel lookups
                    - TaskList to check remaining work`}
        </Prompt>

        ```text theme={null}
        ---
        description: Create rent-free mint with token metadata
        allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
        ---

        ## Create rent-free mint with token metadata

        Context:
        - Guide: https://zkcompression.com/light-token/cookbook/create-mint
        - Skills and resources index: https://zkcompression.com/skill.md
        - Crates: light-token-client (actions), light-token (instructions), light-client (RPC)

        SPL equivalent: spl_token::instruction::initialize_mint → Light Token: CreateMint

        ### 1. Index project
        - Grep `light_token::|light_token_client::|solana_sdk|Keypair|async|CreateMint|initialize_mint` across src/
        - Glob `**/*.rs` for project structure
        - Identify: RPC setup, existing token ops, entry point for mint creation
        - Check Cargo.toml for existing light-* dependencies and solana-sdk version
        - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

        ### 2. Read references
        - WebFetch the guide above — follow the Rust Client tab
        - WebFetch skill.md — check for a dedicated skill and resources matching this task
        - TaskCreate one todo per phase below to track progress

        ### 3. Clarify intention
        - AskUserQuestion: what is the goal? (new feature, migrate existing SPL code, add alongside existing)
        - AskUserQuestion: does the project already have mint operations to extend, or is this greenfield?
        - AskUserQuestion: action-level API (high-level, fewer lines) or instruction-level API (low-level, full control)?
        - Summarize findings and wait for user confirmation before implementing

        ### 4. Create plan
        - Based on steps 1–3, draft an implementation plan: which files to modify, what code to add, dependency changes
        - Verify existing Rpc/signer setup is compatible with the cookbook prerequisites (light_client::rpc::Rpc, solana_sdk::signature::Keypair)
        - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
        - Present the plan to the user for approval before proceeding

        ### 5. Implement
        - Add deps if missing: Bash `cargo add light-token-client light-token light-client --features light-client/v2`
        - Follow the cookbook guide and the approved plan
        - Write/Edit to create or modify files
        - TaskUpdate to mark each step done

        ### 6. Verify
        - Bash `cargo check`
        - Bash `cargo test` if tests exist
        - TaskUpdate to mark complete

        ### Tools
        - mcp__zkcompression__SearchLightProtocol("<query>") for API details
        - mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
        - Task subagent with Grep/Read/WebFetch for parallel lookups
        - TaskList to check remaining work
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Program">
    <Tabs>
      <Tab title="CPI">
        <Tabs>
          <Tab title="Guide">
            Compare to SPL:

            <Tabs>
              <Tab title="Mint">
                <CodeCompare firstCode={lightCreateMintCpiCode} secondCode={splCreateMintCpiCode} firstLabel="light-token" secondLabel="SPL" language="rust" />
              </Tab>

              <Tab title="With Token Metadata">
                <CodeCompare firstCode={lightCreateMintMetadataCpiCode} secondCode={splCreateMintMetadataCpiCode} firstLabel="light-token" secondLabel="SPL Token 2022" language="rust" />
              </Tab>
            </Tabs>

            <Note>
              Find [a full code example at the end](#full-code-example).
            </Note>

            <Steps>
              <Step>
                ### Configure Token Metadata

                ```rust theme={null}
                use light_token_interface::instructions::extensions::{
                    token_metadata::TokenMetadataInstructionData, ExtensionInstructionData,
                };

                let extensions = Some(vec![ExtensionInstructionData::TokenMetadata(
                    TokenMetadataInstructionData {
                        update_authority: Some(authority.key.to_bytes().into()),
                        name: b"Example Token".to_vec(),
                        symbol: b"EXT".to_vec(),
                        uri: b"https://example.com/metadata.json".to_vec(),
                        additional_metadata: None,
                    },
                )]);
                ```

                <Note>
                  **Fields must be set at light-mint creation.** Standard fields (`name`,
                  `symbol`, `uri`) can be updated by `update_authority`. For
                  `additional_metadata`, only existing keys can be modified or removed. New keys
                  cannot be added after creation.
                </Note>
              </Step>

              <Step>
                ### Configure Mint

                Configure mint parameters including `decimals`, authorities, rent settings, and pass `extensions` from the previous step.

                ```rust theme={null}
                use light_token::instruction::CreateMintParams;

                let params = CreateMintParams {
                    decimals,
                    address_merkle_tree_root_index,
                    mint_authority: *ctx.accounts.authority.key,
                    proof,
                    compression_address,
                    mint,
                    bump,
                    freeze_authority,
                    extensions,
                    rent_payment: rent_payment.unwrap_or(16),    // ~24 hours rent
                    write_top_up: write_top_up.unwrap_or(766),   // ~3 hours rent
                };
                ```

                <Info>
                  The address of the mint account is stored in an address Merkle tree
                  , which is maintained by the protocol.
                  The client passes a validity proof that proves the mint address does not
                  exist yet.
                </Info>
              </Step>

              <Step>
                ### System Accounts

                Include system accounts such as the Light System Program
                to verify the proof and write the mint address to the address tree.

                <Accordion title="System Accounts List">
                  <table>
                    <colgroup>
                      <col style={{width: '5%'}} />

                      <col style={{width: '30%', textAlign: 'left'}} />

                      <col style={{width: '65%'}} />
                    </colgroup>

                    <thead>
                      <tr>
                        <th style={{textAlign: 'left'}} />

                        <th style={{textAlign: 'left'}}>Account</th>
                        <th style={{textAlign: 'left'}}>Description</th>
                      </tr>
                    </thead>

                    <tbody>
                      <tr>
                        <td>1</td>
                        <td style={{textAlign: 'left'}}><strong><Tooltip tip="SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7" cta="Program ID" href="https://solscan.io/account/SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7">Light System Program</Tooltip></strong></td>
                        <td>Verifies validity proofs and executes compressed account state transitions.</td>
                      </tr>

                      <tr>
                        <td>2</td>
                        <td style={{textAlign: 'left'}}><strong>CPI Authority PDA</strong></td>
                        <td>PDA that authorizes CPIs from the Light Token Program to the Light System Program.</td>
                      </tr>

                      <tr>
                        <td>3</td>
                        <td style={{textAlign: 'left'}}><strong>Registered Program PDA</strong></td>
                        <td>Proves the Light Token Program is registered to use compression.</td>
                      </tr>

                      <tr>
                        <td>4</td>
                        <td style={{textAlign: 'left'}}><strong><Tooltip tip="HZH7qSLcpAeDqCopVU4e5XkhT9j3JFsQiq8CmruY3aru" cta="PDA" href="https://solscan.io/account/HZH7qSLcpAeDqCopVU4e5XkhT9j3JFsQiq8CmruY3aru">Account Compression Authority</Tooltip></strong></td>
                        <td>Signs CPI calls from the Light System Program to the Account Compression Program.</td>
                      </tr>

                      <tr>
                        <td>5</td>
                        <td style={{textAlign: 'left'}}><strong><Tooltip tip="compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq" cta="Program ID" href="https://solscan.io/account/compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq">Account Compression Program</Tooltip></strong></td>
                        <td>Writes to state and address Merkle tree accounts.</td>
                      </tr>

                      <tr>
                        <td>6</td>
                        <td style={{textAlign: 'left'}}><strong><Tooltip tip="11111111111111111111111111111111" cta="Program ID" href="https://solscan.io/account/11111111111111111111111111111111">System Program</Tooltip></strong></td>
                        <td>Solana System Program.</td>
                      </tr>
                    </tbody>
                  </table>
                </Accordion>

                ```rust theme={null}
                use light_token::instruction::SystemAccountInfos;

                let system_accounts = SystemAccountInfos {
                    light_system_program: ctx.accounts.light_system_program.to_account_info(),
                    cpi_authority_pda: ctx.accounts.cpi_authority_pda.to_account_info(),
                    registered_program_pda: ctx.accounts.registered_program_pda.to_account_info(),
                    account_compression_authority: ctx.accounts.account_compression_authority.to_account_info(),
                    account_compression_program: ctx.accounts.account_compression_program.to_account_info(),
                    system_program: ctx.accounts.system_program.to_account_info(),
                };
                ```
              </Step>

              <Step>
                ### Build Account Infos and CPI the Light Token Program

                Use `invoke` or `invoke_signed`:

                * When `mint_seed` is an external keypair, use `invoke`.
                * When `mint_seed` is a PDA, use `invoke_signed` with its seeds.
                * When both `mint_seed` and `authority` are PDAs, use `invoke_signed` with both seeds.

                <Tabs>
                  <Tab title="invoke (External signer)">
                    ```rust theme={null}
                    use light_token::instruction::CreateMintCpi;

                    CreateMintCpi::new(
                        mint_seed.clone(),
                        authority.clone(),
                        payer.clone(),
                        address_tree.clone(), // stores address
                        output_queue.clone(), // stores account when inactive
                        compressible_config.clone(), // rent settings
                        mint.clone(),
                        rent_sponsor.clone(),
                        system_accounts,
                        params,
                    )
                    .invoke()
                    ```
                  </Tab>

                  <Tab title="invoke_signed (PDA mint seed)">
                    ```rust theme={null}
                    use light_token::instruction::CreateMintCpi;

                    let signer_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[bump]];

                    CreateMintCpi::new(
                        mint_seed.clone(),
                        authority.clone(),
                        payer.clone(),
                        address_tree.clone(),
                        output_queue.clone(),
                        compressible_config.clone(),
                        mint.clone(),
                        rent_sponsor.clone(),
                        system_accounts,
                        params,
                    )
                    .invoke_signed(&[signer_seeds])
                    ```
                  </Tab>

                  <Tab title="invoke_signed (Two PDA signers)">
                    ```rust theme={null}
                    use light_token::instruction::CreateMintCpi;

                    let mint_seed_seeds: &[&[u8]] = &[MINT_SIGNER_SEED, &[mint_seed_bump]];
                    let authority_seeds: &[&[u8]] = &[MINT_AUTHORITY_SEED, &[authority_bump]];

                    CreateMintCpi::new(
                        mint_seed.clone(),
                        authority.clone(),
                        payer.clone(),
                        address_tree.clone(),
                        output_queue.clone(),
                        compressible_config.clone(),
                        mint.clone(),
                        rent_sponsor.clone(),
                        system_accounts,
                        params,
                    )
                    .invoke_signed(&[mint_seed_seeds, authority_seeds])
                    ```
                  </Tab>
                </Tabs>
              </Step>
            </Steps>

            # Full Code Example

            <Tabs>
              <Tab title="Anchor">
                <Info>
                  View the [source code](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-libs/token-sdk/src/instruction/create_mint.rs) and [full example](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-instructions/create-mint) with shared test utilities.
                </Info>

                <CodeGroup>
                  ```rust lib.rs theme={null}
                  #![allow(unexpected_cfgs, deprecated)]

                  use anchor_lang::prelude::*;
                  use light_token::instruction::{
                      CreateMintCpi, CreateMintParams, SystemAccountInfos, DEFAULT_RENT_PAYMENT, DEFAULT_WRITE_TOP_UP,
                  };
                  use light_token::{CompressedProof, ExtensionInstructionData, TokenMetadataInstructionData};

                  declare_id!("A1rJEoepgKYWZYZ8KVFpxgeeRGwBrU7xk8S39srjVkUX");

                  /// Token metadata parameters for creating a mint with metadata.
                  #[derive(AnchorSerialize, AnchorDeserialize, Clone)]
                  pub struct TokenMetadataParams {
                      pub name: Vec<u8>,
                      pub symbol: Vec<u8>,
                      pub uri: Vec<u8>,
                      pub update_authority: Option<Pubkey>,
                  }

                  #[program]
                  pub mod light_token_anchor_create_mint {
                      use super::*;

                      pub fn create_mint(
                          ctx: Context<CreateMintAccounts>,
                          decimals: u8,
                          address_merkle_tree_root_index: u16,
                          compression_address: [u8; 32],
                          proof: CompressedProof,
                          freeze_authority: Option<Pubkey>,
                          bump: u8,
                          rent_payment: Option<u8>,
                          write_top_up: Option<u32>,
                          metadata: Option<TokenMetadataParams>,
                      ) -> Result<()> {
                          let mint = light_token::instruction::find_mint_address(ctx.accounts.mint_seed.key).0;

                          let extensions = metadata.map(|m| {
                              vec![ExtensionInstructionData::TokenMetadata(
                                  TokenMetadataInstructionData {
                                      update_authority: m
                                          .update_authority
                                          .map(|p| p.to_bytes().into()),
                                      name: m.name,
                                      symbol: m.symbol,
                                      uri: m.uri,
                                      additional_metadata: None,
                                  },
                              )]
                          });

                          let params = CreateMintParams {
                              decimals,
                              address_merkle_tree_root_index,
                              mint_authority: *ctx.accounts.authority.key,
                              proof,
                              compression_address,
                              mint,
                              bump,
                              freeze_authority,
                              extensions,
                              rent_payment: rent_payment.unwrap_or(DEFAULT_RENT_PAYMENT),
                              write_top_up: write_top_up.unwrap_or(DEFAULT_WRITE_TOP_UP),
                          };

                          let system_accounts = SystemAccountInfos {
                              light_system_program: ctx.accounts.light_system_program.to_account_info(),
                              cpi_authority_pda: ctx.accounts.cpi_authority_pda.to_account_info(),
                              registered_program_pda: ctx.accounts.registered_program_pda.to_account_info(),
                              account_compression_authority: ctx.accounts.account_compression_authority.to_account_info(),
                              account_compression_program: ctx.accounts.account_compression_program.to_account_info(),
                              system_program: ctx.accounts.system_program.to_account_info(),
                          };

                          CreateMintCpi {
                              mint_seed: ctx.accounts.mint_seed.to_account_info(),
                              authority: ctx.accounts.authority.to_account_info(),
                              payer: ctx.accounts.payer.to_account_info(),
                              address_tree: ctx.accounts.address_tree.to_account_info(),
                              output_queue: ctx.accounts.output_queue.to_account_info(),
                              compressible_config: ctx.accounts.compressible_config.to_account_info(),
                              mint: ctx.accounts.mint.to_account_info(),
                              rent_sponsor: ctx.accounts.rent_sponsor.to_account_info(),
                              system_accounts,
                              cpi_context: None,
                              cpi_context_account: None,
                              params,
                          }
                          .invoke()?;
                          Ok(())
                      }
                  }

                  #[derive(Accounts)]
                  pub struct CreateMintAccounts<'info> {
                      /// CHECK: Light token program for CPI
                      pub light_token_program: AccountInfo<'info>,
                      pub mint_seed: Signer<'info>,
                      /// CHECK: Validated by light-token CPI
                      pub authority: AccountInfo<'info>,
                      #[account(mut)]
                      pub payer: Signer<'info>,
                      /// CHECK: Validated by light-token CPI
                      #[account(mut)]
                      pub address_tree: AccountInfo<'info>,
                      /// CHECK: Validated by light-token CPI
                      #[account(mut)]
                      pub output_queue: AccountInfo<'info>,
                      /// CHECK: Validated by light-token CPI
                      pub light_system_program: AccountInfo<'info>,
                      /// CHECK: Validated by light-token CPI
                      pub cpi_authority_pda: AccountInfo<'info>,
                      /// CHECK: Validated by light-token CPI
                      pub registered_program_pda: AccountInfo<'info>,
                      /// CHECK: Validated by light-token CPI
                      pub account_compression_authority: AccountInfo<'info>,
                      /// CHECK: Validated by light-token CPI
                      pub account_compression_program: AccountInfo<'info>,
                      pub system_program: Program<'info, System>,
                      /// CHECK: Validated by light-token CPI - use light_token::token::config_pda()
                      pub compressible_config: AccountInfo<'info>,
                      /// CHECK: Validated by light-token CPI - derived from find_mint_address(mint_seed)
                      #[account(mut)]
                      pub mint: AccountInfo<'info>,
                      /// CHECK: Validated by light-token CPI - use light_token::token::rent_sponsor_pda()
                      #[account(mut)]
                      pub rent_sponsor: AccountInfo<'info>,
                  }
                  ```

                  ```rust test.rs theme={null}
                  use anchor_lang::{InstructionData, ToAccountMetas};
                  use light_program_test::{Indexer, LightProgramTest, ProgramTestConfig, Rpc};
                  use light_token_anchor_create_mint::{accounts, instruction::CreateMint, ID};
                  use light_token::instruction::{
                      config_pda, derive_mint_compressed_address, find_mint_address, rent_sponsor_pda,
                      SystemAccounts, LIGHT_TOKEN_PROGRAM_ID, DEFAULT_RENT_PAYMENT, DEFAULT_WRITE_TOP_UP,
                  };
                  use anchor_lang::system_program;
                  use solana_sdk::{
                      instruction::Instruction,
                      signature::Keypair,
                      signer::Signer,
                  };

                  #[tokio::test]
                  async fn test_create_mint() {
                      let config =
                          ProgramTestConfig::new_v2(true, Some(vec![("light_token_anchor_create_mint", ID)]));
                      let mut rpc = LightProgramTest::new(config).await.unwrap();
                      let payer = rpc.get_payer().insecure_clone();

                      let mint_seed = Keypair::new();
                      let mint_authority = payer.pubkey();
                      let decimals = 9u8;

                      let address_tree = rpc.get_address_tree_v2();
                      let output_queue = rpc.get_random_state_tree_info().unwrap().queue;

                      let compression_address =
                          derive_mint_compressed_address(&mint_seed.pubkey(), &address_tree.tree);

                      let (mint_pda, bump) = find_mint_address(&mint_seed.pubkey());

                      let rpc_result = rpc
                          .get_validity_proof(
                              vec![],
                              vec![light_client::indexer::AddressWithTree {
                                  address: compression_address,
                                  tree: address_tree.tree,
                              }],
                              None,
                          )
                          .await
                          .unwrap()
                          .value;

                      let system_accounts = SystemAccounts::default();

                      // Call the anchor program to create mint
                      let ix = Instruction {
                          program_id: ID,
                          accounts: accounts::CreateMintAccounts {
                              light_token_program: LIGHT_TOKEN_PROGRAM_ID,
                              mint_seed: mint_seed.pubkey(),
                              authority: mint_authority,
                              payer: payer.pubkey(),
                              address_tree: address_tree.tree,
                              output_queue,
                              light_system_program: system_accounts.light_system_program,
                              cpi_authority_pda: system_accounts.cpi_authority_pda,
                              registered_program_pda: system_accounts.registered_program_pda,
                              account_compression_authority: system_accounts.account_compression_authority,
                              account_compression_program: system_accounts.account_compression_program,
                              system_program: system_program::ID,
                              compressible_config: config_pda(),
                              mint: mint_pda,
                              rent_sponsor: rent_sponsor_pda(),
                          }
                          .to_account_metas(Some(true)),
                          data: CreateMint {
                              decimals,
                              address_merkle_tree_root_index: rpc_result.addresses[0].root_index,
                              compression_address: compression_address.into(),
                              proof: rpc_result.proof.0.unwrap(),
                              freeze_authority: None,
                              bump,
                              rent_payment: Some(DEFAULT_RENT_PAYMENT),
                              write_top_up: Some(DEFAULT_WRITE_TOP_UP),
                              metadata: None,
                          }
                          .data(),
                      };

                      let sig = rpc
                          .create_and_send_transaction(&[ix], &payer.pubkey(), &[&payer, &mint_seed])
                          .await
                          .unwrap();

                      let compressed_account = rpc
                          .get_compressed_account(compression_address, None)
                          .await
                          .unwrap()
                          .value;

                      assert!(compressed_account.is_some(), "Light-mint should exist");
                      println!("Tx: {}", sig);
                  }
                  ```
                </CodeGroup>
              </Tab>
            </Tabs>
          </Tab>

          <Tab title="AI Prompt">
            <Prompt description="Add create-mint CPI to an Anchor program" actions={["copy", "cursor"]}>
              {`---
                            description: Add create-mint CPI to an Anchor program
                            allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
                            ---

                            ## Add create-mint CPI to an Anchor program

                            Context:
                            - Guide: https://zkcompression.com/light-token/cookbook/create-mint
                            - Skills and resources index: https://zkcompression.com/skill.md
                            - Crate: light-token (CreateMintCpi, CreateMintParams, SystemAccountInfos)
                            - Example: https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-instructions/create-mint

                            Key CPI struct: \`light_token::instruction::CreateMintCpi\`

                            ### 1. Index project
                            - Grep \`declare_id|#\[program\]|anchor_lang|Account<|Pubkey|invoke|mint|decimals|authority|metadata\` across src/
                            - Glob \`**/*.rs\` and \`**/Cargo.toml\` for project structure
                            - Identify: program ID, existing instructions, account structs, mint seeds
                            - Read Cargo.toml — note existing dependencies and framework version
                            - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

                            ### 2. Read references
                            - WebFetch the guide above — review the CPI tab under Program: token metadata config, mint params, system accounts, invoke patterns
                            - WebFetch skill.md — check for a dedicated skill and resources matching this task
                            - TaskCreate one todo per phase below to track progress

                            ### 3. Clarify intention
                            - AskUserQuestion: what is the goal? (add create-mint to existing program, new program from scratch, migrate from SPL create_mint)
                            - AskUserQuestion: should the mint seed be an external keypair or a PDA? (determines invoke vs invoke_signed)
                            - AskUserQuestion: do you need token metadata (name, symbol, uri)?
                            - Summarize findings and wait for user confirmation before implementing

                            ### 4. Create plan
                            - Based on steps 1–3, draft an implementation plan: which files to modify, what code to add, dependency changes
                            - Follow the guide's step order: Configure Token Metadata → Configure Mint Params → System Accounts → Build CPI and invoke
                            - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
                            - Present the plan to the user for approval before proceeding

                            ### 5. Implement
                            - Add deps if missing: Bash \`cargo add light-token anchor-lang@0.31\`
                            - Follow the guide and the approved plan
                            - Write/Edit to create or modify files
                            - TaskUpdate to mark each step done

                            ### 6. Verify
                            - Bash \`anchor build\`
                            - Bash \`anchor test\` if tests exist
                            - TaskUpdate to mark complete

                            ### Tools
                            - mcp__zkcompression__SearchLightProtocol("<query>") for API details
                            - mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
                            - Task subagent with Grep/Read/WebFetch for parallel lookups
                            - TaskList to check remaining work`}
            </Prompt>

            ```text theme={null}
            ---
            description: Add create-mint CPI to an Anchor program
            allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
            ---

            ## Add create-mint CPI to an Anchor program

            Context:
            - Guide: https://zkcompression.com/light-token/cookbook/create-mint
            - Skills and resources index: https://zkcompression.com/skill.md
            - Crate: light-token (CreateMintCpi, CreateMintParams, SystemAccountInfos)
            - Example: https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-instructions/create-mint

            Key CPI struct: `light_token::instruction::CreateMintCpi`

            ### 1. Index project
            - Grep `declare_id|#\[program\]|anchor_lang|Account<|Pubkey|invoke|mint|decimals|authority|metadata` across src/
            - Glob `**/*.rs` and `**/Cargo.toml` for project structure
            - Identify: program ID, existing instructions, account structs, mint seeds
            - Read Cargo.toml — note existing dependencies and framework version
            - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

            ### 2. Read references
            - WebFetch the guide above — review the CPI tab under Program: token metadata config, mint params, system accounts, invoke patterns
            - WebFetch skill.md — check for a dedicated skill and resources matching this task
            - TaskCreate one todo per phase below to track progress

            ### 3. Clarify intention
            - AskUserQuestion: what is the goal? (add create-mint to existing program, new program from scratch, migrate from SPL create_mint)
            - AskUserQuestion: should the mint seed be an external keypair or a PDA? (determines invoke vs invoke_signed)
            - AskUserQuestion: do you need token metadata (name, symbol, uri)?
            - Summarize findings and wait for user confirmation before implementing

            ### 4. Create plan
            - Based on steps 1–3, draft an implementation plan: which files to modify, what code to add, dependency changes
            - Follow the guide's step order: Configure Token Metadata → Configure Mint Params → System Accounts → Build CPI and invoke
            - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
            - Present the plan to the user for approval before proceeding

            ### 5. Implement
            - Add deps if missing: Bash `cargo add light-token anchor-lang@0.31`
            - Follow the guide and the approved plan
            - Write/Edit to create or modify files
            - TaskUpdate to mark each step done

            ### 6. Verify
            - Bash `anchor build`
            - Bash `anchor test` if tests exist
            - TaskUpdate to mark complete

            ### Tools
            - mcp__zkcompression__SearchLightProtocol("<query>") for API details
            - mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
            - Task subagent with Grep/Read/WebFetch for parallel lookups
            - TaskList to check remaining work
            ```
          </Tab>
        </Tabs>
      </Tab>

      <Tab title="Anchor Macros">
        <Tabs>
          <Tab title="Guide">
            Compare to SPL:

            <Tabs>
              <Tab title="Mint">
                <CodeCompare firstCode={lightCreateMintMacroCode} secondCode={splCreateMintMacroCode} firstLabel="light-token" secondLabel="SPL" language="rust" />
              </Tab>

              <Tab title="With Token Metadata">
                <CodeCompare firstCode={lightCreateMintMetadataMacroCode} secondCode={splCreateMintMetadataMacroCode} firstLabel="light-token" secondLabel="SPL Token 2022" language="rust" />
              </Tab>
            </Tabs>

            <Note>
              Find [a full code example at the end](#full-code-example).
            </Note>

            <Steps>
              <Step>
                ### Dependencies

                ```toml theme={null}
                [dependencies]
                light-sdk = { version = "0.18.0", features = ["anchor", "v2", "cpi-context"] }
                light-sdk-macros = "0.18.0"
                light-compressible = "0.1.0"
                anchor-lang = "0.31"
                ```
              </Step>

              <Step>
                ### Program

                Add `#[light_program]` above `#[program]`:

                ```rust theme={null}
                use light_sdk_macros::light_program;

                #[light_program]
                #[program]
                pub mod my_program {
                    use super::*;

                    pub fn create_mint<'info>(
                        ctx: Context<'_, '_, '_, 'info, CreateMint<'info>>,
                        params: CreateMintParams,
                    ) -> Result<()> {
                        Ok(())
                    }
                }
                ```
              </Step>

              <Step>
                ### Accounts struct

                Derive `LightAccounts` on your `Accounts` struct and add `#[light_account(...)]` next to `#[account(...)]`.

                <Tabs>
                  <Tab title="Mint">
                    ```rust theme={null}
                    /// CHECK: Validated by light-token CPI
                    #[account(mut)]
                    #[light_account(init,
                        mint::signer = mint_signer,
                        mint::authority = fee_payer,
                        mint::decimals = 9,
                        mint::seeds = &[MINT_SIGNER_SEED, self.authority.to_account_info().key.as_ref()],
                        mint::bump = params.mint_signer_bump
                    )]
                    pub mint: UncheckedAccount<'info>,
                    ```
                  </Tab>

                  <Tab title="Mint with metadata">
                    ```rust theme={null}
                    /// CHECK: Validated by light-token CPI
                    #[account(mut)]
                    #[light_account(init,
                        mint::signer = mint_signer,
                        mint::authority = fee_payer,
                        mint::decimals = 9,
                        mint::seeds = &[MINT_SIGNER_SEED, self.authority.to_account_info().key.as_ref()],
                        mint::bump = params.mint_signer_bump,
                        mint::name = params.name.clone(),
                        mint::symbol = params.symbol.clone(),
                        mint::uri = params.uri.clone(),
                        mint::update_authority = authority,
                        mint::additional_metadata = params.additional_metadata.clone()
                    )]
                    pub mint: UncheckedAccount<'info>,
                    ```
                  </Tab>
                </Tabs>
              </Step>
            </Steps>

            # Full code example

            <Info>
              View the [source code](https://github.com/Lightprotocol/light-protocol/blob/main/sdk-libs/token-sdk/src/instruction/create_mint.rs) and [full example](https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-mint) with shared test utilities.
            </Info>

            <CodeGroup>
              ```rust lib.rs theme={null}
              #![allow(deprecated)]

              use anchor_lang::prelude::*;
              use light_compressible::CreateAccountsProof;
              use light_sdk::derive_light_cpi_signer;
              use light_sdk_macros::{light_program, LightAccounts};
              use light_sdk_types::CpiSigner;

              declare_id!("HVmVqSJyMejBeUigePMSfX4aENJzCGHNxAJuT2PDMPRx");

              pub const LIGHT_CPI_SIGNER: CpiSigner =
                  derive_light_cpi_signer!("HVmVqSJyMejBeUigePMSfX4aENJzCGHNxAJuT2PDMPRx");

              pub const MINT_SIGNER_SEED: &[u8] = b"mint_signer";

              #[derive(AnchorSerialize, AnchorDeserialize, Clone)]
              pub struct CreateMintParams {
                  pub create_accounts_proof: CreateAccountsProof,
                  pub mint_signer_bump: u8,
              }

              #[derive(AnchorSerialize, AnchorDeserialize, Clone)]
              pub struct CreateMintWithMetadataParams {
                  pub create_accounts_proof: CreateAccountsProof,
                  pub mint_signer_bump: u8,
                  pub name: Vec<u8>,
                  pub symbol: Vec<u8>,
                  pub uri: Vec<u8>,
              }

              #[derive(Accounts, LightAccounts)]
              #[instruction(params: CreateMintParams)]
              pub struct CreateMint<'info> {
                  #[account(mut)]
                  pub fee_payer: Signer<'info>,

                  pub authority: Signer<'info>,

                  /// CHECK: PDA derived from authority
                  #[account(
                      seeds = [MINT_SIGNER_SEED, authority.key().as_ref()],
                      bump,
                  )]
                  pub mint_signer: UncheckedAccount<'info>,

                  /// CHECK: Initialized by light_mint CPI
                  #[account(mut)]
                  #[light_account(init,
                      mint::signer = mint_signer,
                      mint::authority = fee_payer,
                      mint::decimals = 9,
                      mint::seeds = &[MINT_SIGNER_SEED, self.authority.to_account_info().key.as_ref()],
                      mint::bump = params.mint_signer_bump
                  )]
                  pub mint: UncheckedAccount<'info>,

                  /// CHECK: Compression config PDA
                  pub compression_config: AccountInfo<'info>,

                  /// CHECK: Light Token compressible config
                  pub light_token_compressible_config: AccountInfo<'info>,

                  /// CHECK: Rent sponsor
                  #[account(mut)]
                  pub rent_sponsor: AccountInfo<'info>,

                  /// CHECK: Light Token program
                  pub light_token_program: AccountInfo<'info>,

                  /// CHECK: Light Token CPI authority
                  pub light_token_cpi_authority: AccountInfo<'info>,

                  pub system_program: Program<'info, System>,
              }

              #[derive(Accounts, LightAccounts)]
              #[instruction(params: CreateMintWithMetadataParams)]
              pub struct CreateMintWithMetadata<'info> {
                  #[account(mut)]
                  pub fee_payer: Signer<'info>,

                  pub authority: Signer<'info>,

                  /// CHECK: PDA derived from authority
                  #[account(
                      seeds = [MINT_SIGNER_SEED, authority.key().as_ref()],
                      bump,
                  )]
                  pub mint_signer: UncheckedAccount<'info>,

                  /// CHECK: Initialized by light_mint CPI
                  #[account(mut)]
                  #[light_account(init,
                      mint::signer = mint_signer,
                      mint::authority = fee_payer,
                      mint::decimals = 9,
                      mint::seeds = &[MINT_SIGNER_SEED, self.authority.to_account_info().key.as_ref()],
                      mint::bump = params.mint_signer_bump,
                      mint::name = params.name.clone(),
                      mint::symbol = params.symbol.clone(),
                      mint::uri = params.uri.clone(),
                      mint::update_authority = authority
                  )]
                  pub mint: UncheckedAccount<'info>,

                  /// CHECK: Compression config PDA
                  pub compression_config: AccountInfo<'info>,

                  /// CHECK: Light Token compressible config
                  pub light_token_compressible_config: AccountInfo<'info>,

                  /// CHECK: Rent sponsor
                  #[account(mut)]
                  pub rent_sponsor: AccountInfo<'info>,

                  /// CHECK: Light Token program
                  pub light_token_program: AccountInfo<'info>,

                  /// CHECK: Light Token CPI authority
                  pub light_token_cpi_authority: AccountInfo<'info>,

                  pub system_program: Program<'info, System>,
              }

              #[light_program]
              #[program]
              pub mod light_token_macro_create_mint {
                  use super::*;

                  #[allow(unused_variables)]
                  pub fn create_mint<'info>(
                      ctx: Context<'_, '_, '_, 'info, CreateMint<'info>>,
                      params: CreateMintParams,
                  ) -> Result<()> {
                      Ok(())
                  }

                  #[allow(unused_variables)]
                  pub fn create_mint_with_metadata<'info>(
                      ctx: Context<'_, '_, '_, 'info, CreateMintWithMetadata<'info>>,
                      params: CreateMintWithMetadataParams,
                  ) -> Result<()> {
                      Ok(())
                  }
              }
              ```

              ```rust test.rs theme={null}
              use anchor_lang::{prelude::borsh, InstructionData, ToAccountMetas};
              use light_client::interface::{
                  get_create_accounts_proof, CreateAccountsProofInput, InitializeRentFreeConfig,
              };
              use light_program_test::{
                  program_test::{setup_mock_program_data, LightProgramTest},
                  ProgramTestConfig, Rpc,
              };
              use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID;
              use light_token::instruction::{find_mint_address, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR};
              use solana_instruction::Instruction;
              use solana_keypair::Keypair;
              use solana_pubkey::Pubkey;
              use solana_signer::Signer;

              use light_token_macro_create_mint::MINT_SIGNER_SEED;

              async fn setup() -> (light_program_test::LightProgramTest, Keypair, Pubkey, Pubkey) {
                  let program_id = light_token_macro_create_mint::ID;
                  let mut config =
                      ProgramTestConfig::new_v2(true, Some(vec![("light_token_macro_create_mint", program_id)]));
                  config = config.with_light_protocol_events();

                  let mut rpc = LightProgramTest::new(config).await.unwrap();
                  let payer = rpc.get_payer().insecure_clone();

                  let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id);

                  let (init_config_ix, config_pda) = InitializeRentFreeConfig::new(
                      &program_id,
                      &payer.pubkey(),
                      &program_data_pda,
                      RENT_SPONSOR,
                      payer.pubkey(),
                  )
                  .build();

                  rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer])
                      .await
                      .expect("Initialize config should succeed");

                  (rpc, payer, config_pda, program_id)
              }

              #[tokio::test]
              async fn test_create_mint() {
                  use light_token_macro_create_mint::CreateMintParams;

                  let (mut rpc, payer, config_pda, program_id) = setup().await;

                  let authority = Keypair::new();
                  let (mint_signer_pda, mint_signer_bump) = Pubkey::find_program_address(
                      &[MINT_SIGNER_SEED, authority.pubkey().as_ref()],
                      &program_id,
                  );
                  let (mint_pda, _) = find_mint_address(&mint_signer_pda);

                  let proof_result = get_create_accounts_proof(
                      &rpc,
                      &program_id,
                      vec![CreateAccountsProofInput::mint(mint_signer_pda)],
                  )
                  .await
                  .unwrap();

                  let accounts = light_token_macro_create_mint::accounts::CreateMint {
                      fee_payer: payer.pubkey(),
                      authority: authority.pubkey(),
                      mint_signer: mint_signer_pda,
                      mint: mint_pda,
                      compression_config: config_pda,
                      light_token_compressible_config: COMPRESSIBLE_CONFIG_V1,
                      rent_sponsor: RENT_SPONSOR,
                      light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(),
                      light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(),
                      system_program: solana_sdk::system_program::ID,
                  };

                  let instruction_data = light_token_macro_create_mint::instruction::CreateMint {
                      params: CreateMintParams {
                          create_accounts_proof: proof_result.create_accounts_proof,
                          mint_signer_bump,
                      },
                  };

                  let instruction = Instruction {
                      program_id,
                      accounts: [
                          accounts.to_account_metas(None),
                          proof_result.remaining_accounts,
                      ]
                      .concat(),
                      data: instruction_data.data(),
                  };

                  rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &authority])
                      .await
                      .expect("CreateMint should succeed");

                  let mint_account = rpc
                      .get_account(mint_pda)
                      .await
                      .unwrap()
                      .expect("Mint should exist on-chain");

                  use light_token_interface::state::Mint;
                  let mint: Mint = borsh::BorshDeserialize::deserialize(&mut &mint_account.data[..])
                      .expect("Failed to deserialize Mint");

                  assert_eq!(mint.base.decimals, 9);
                  assert_eq!(
                      mint.base.mint_authority,
                      Some(payer.pubkey().to_bytes().into()),
                  );
              }

              #[tokio::test]
              async fn test_create_mint_with_metadata() {
                  use light_token_macro_create_mint::CreateMintWithMetadataParams;

                  let (mut rpc, payer, config_pda, program_id) = setup().await;

                  let authority = Keypair::new();
                  let (mint_signer_pda, mint_signer_bump) = Pubkey::find_program_address(
                      &[MINT_SIGNER_SEED, authority.pubkey().as_ref()],
                      &program_id,
                  );
                  let (mint_pda, _) = find_mint_address(&mint_signer_pda);

                  let proof_result = get_create_accounts_proof(
                      &rpc,
                      &program_id,
                      vec![CreateAccountsProofInput::mint(mint_signer_pda)],
                  )
                  .await
                  .unwrap();

                  let accounts = light_token_macro_create_mint::accounts::CreateMintWithMetadata {
                      fee_payer: payer.pubkey(),
                      authority: authority.pubkey(),
                      mint_signer: mint_signer_pda,
                      mint: mint_pda,
                      compression_config: config_pda,
                      light_token_compressible_config: COMPRESSIBLE_CONFIG_V1,
                      rent_sponsor: RENT_SPONSOR,
                      light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(),
                      light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(),
                      system_program: solana_sdk::system_program::ID,
                  };

                  let instruction_data = light_token_macro_create_mint::instruction::CreateMintWithMetadata {
                      params: CreateMintWithMetadataParams {
                          create_accounts_proof: proof_result.create_accounts_proof,
                          mint_signer_bump,
                          name: b"Test Token".to_vec(),
                          symbol: b"TST".to_vec(),
                          uri: b"https://example.com/metadata.json".to_vec(),
                      },
                  };

                  let instruction = Instruction {
                      program_id,
                      accounts: [
                          accounts.to_account_metas(None),
                          proof_result.remaining_accounts,
                      ]
                      .concat(),
                      data: instruction_data.data(),
                  };

                  rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[&payer, &authority])
                      .await
                      .expect("CreateMintWithMetadata should succeed");

                  let mint_account = rpc
                      .get_account(mint_pda)
                      .await
                      .unwrap()
                      .expect("Mint should exist on-chain");

                  use light_token_interface::state::Mint;
                  let mint: Mint = borsh::BorshDeserialize::deserialize(&mut &mint_account.data[..])
                      .expect("Failed to deserialize Mint");

                  assert_eq!(mint.base.decimals, 9);
                  assert_eq!(
                      mint.base.mint_authority,
                      Some(payer.pubkey().to_bytes().into()),
                  );
              }
              ```
            </CodeGroup>
          </Tab>

          <Tab title="AI Prompt">
            <Prompt description="Create a rent-free mint with Anchor macros" actions={["copy", "cursor"]}>
              {`---
                            description: Create a rent-free mint with Anchor macros
                            allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
                            ---

                            ## Create a rent-free mint with Anchor macros

                            Context:
                            - Guide: https://zkcompression.com/light-token/cookbook/create-mint
                            - Skills and resources index: https://zkcompression.com/skill.md
                            - Crates: light-sdk, light-sdk-macros, light-compressible, anchor-lang
                            - Example: https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-mint

                            Key macros: \`#[light_program]\`, \`LightAccounts\`, \`#[light_account(init, mint::...)]\`

                            ### 1. Index project
                            - Grep \`#\[program\]|anchor_lang|Account<|Accounts|seeds|init|payer|mint\` across src/
                            - Glob \`**/*.rs\` and \`**/Cargo.toml\` for project structure
                            - Identify: existing program module, account structs, mint patterns, PDA seeds
                            - Read Cargo.toml — note existing dependencies and framework version
                            - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

                            ### 2. Read references
                            - WebFetch the guide above — review the Anchor Macros tab under Program
                            - WebFetch skill.md — check for a dedicated skill and resources matching this task
                            - TaskCreate one todo per phase below to track progress

                            ### 3. Clarify intention
                            - AskUserQuestion: what is the goal? (new program from scratch, add rent-free mint to existing program, migrate from SPL create_mint)
                            - AskUserQuestion: do you need token metadata (name, symbol, uri)?
                            - Summarize findings and wait for user confirmation before implementing

                            ### 4. Create plan
                            - Based on steps 1–3, draft an implementation plan
                            - Follow the guide's step order: Dependencies → Program Module (#[light_program]) → Accounts Struct (#[light_account(init, mint::...)])
                            - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
                            - Present the plan to the user for approval before proceeding

                            ### 5. Implement
                            - Add deps if missing: Bash \`cargo add light-sdk@0.18 --features anchor,v2,cpi-context\` and \`cargo add light-sdk-macros@0.18 light-compressible@0.1 anchor-lang@0.31\`
                            - Follow the guide and the approved plan
                            - Write/Edit to create or modify files
                            - TaskUpdate to mark each step done

                            ### 6. Verify
                            - Bash \`anchor build\`
                            - Bash \`anchor test\` if tests exist
                            - TaskUpdate to mark complete

                            ### Tools
                            - mcp__zkcompression__SearchLightProtocol("<query>") for API details
                            - mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
                            - Task subagent with Grep/Read/WebFetch for parallel lookups
                            - TaskList to check remaining work`}
            </Prompt>

            ```text theme={null}
            ---
            description: Create a rent-free mint with Anchor macros
            allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, AskUserQuestion, Task, TaskCreate, TaskGet, TaskList, TaskUpdate, TaskOutput, mcp__deepwiki, mcp__zkcompression
            ---

            ## Create a rent-free mint with Anchor macros

            Context:
            - Guide: https://zkcompression.com/light-token/cookbook/create-mint
            - Skills and resources index: https://zkcompression.com/skill.md
            - Crates: light-sdk, light-sdk-macros, light-compressible, anchor-lang
            - Example: https://github.com/Lightprotocol/examples-light-token/tree/main/programs/anchor/basic-macros/create-mint

            Key macros: `#[light_program]`, `LightAccounts`, `#[light_account(init, mint::...)]`

            ### 1. Index project
            - Grep `#\[program\]|anchor_lang|Account<|Accounts|seeds|init|payer|mint` across src/
            - Glob `**/*.rs` and `**/Cargo.toml` for project structure
            - Identify: existing program module, account structs, mint patterns, PDA seeds
            - Read Cargo.toml — note existing dependencies and framework version
            - Task subagent (Grep/Read/WebFetch) if project has multiple crates to scan in parallel

            ### 2. Read references
            - WebFetch the guide above — review the Anchor Macros tab under Program
            - WebFetch skill.md — check for a dedicated skill and resources matching this task
            - TaskCreate one todo per phase below to track progress

            ### 3. Clarify intention
            - AskUserQuestion: what is the goal? (new program from scratch, add rent-free mint to existing program, migrate from SPL create_mint)
            - AskUserQuestion: do you need token metadata (name, symbol, uri)?
            - Summarize findings and wait for user confirmation before implementing

            ### 4. Create plan
            - Based on steps 1–3, draft an implementation plan
            - Follow the guide's step order: Dependencies → Program Module (#[light_program]) → Accounts Struct (#[light_account(init, mint::...)])
            - If anything is unclear or ambiguous, loop back to step 3 (AskUserQuestion)
            - Present the plan to the user for approval before proceeding

            ### 5. Implement
            - Add deps if missing: Bash `cargo add light-sdk@0.18 --features anchor,v2,cpi-context` and `cargo add light-sdk-macros@0.18 light-compressible@0.1 anchor-lang@0.31`
            - Follow the guide and the approved plan
            - Write/Edit to create or modify files
            - TaskUpdate to mark each step done

            ### 6. Verify
            - Bash `anchor build`
            - Bash `anchor test` if tests exist
            - TaskUpdate to mark complete

            ### Tools
            - mcp__zkcompression__SearchLightProtocol("<query>") for API details
            - mcp__deepwiki__ask_question("Lightprotocol/light-protocol", "<q>") for architecture
            - Task subagent with Grep/Read/WebFetch for parallel lookups
            - TaskList to check remaining work
            ```
          </Tab>
        </Tabs>
      </Tab>
    </Tabs>
  </Tab>
</Tabs>

# Next Steps

<Card title="Learn how to mint tokens to Light Token accounts." icon="chevron-right" color="#0066ff" href="/light-token/cookbook/mint-to" horizontal />
