> ## Documentation Index
> Fetch the complete documentation index at: https://seilabs-docs-hft-parallel-nonce-submission.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Sei Network Testnet Faucet

> Request testnet tokens for development and testing on Sei Network. The faucet distributes test tokens with no real-world value.

export const Faucet = () => {
  const FAUCET_API_URL = 'https://faucet-v3.seinetwork.io';
  const HCAPTCHA_SITEKEY = '39d88446-78f4-4f1e-8b88-9c7ce32cb10c';
  const EXPLORER_TX = 'https://testnet.seiscan.io/tx';
  const HAIRLINE = 'rgba(128, 128, 128, 0.25)';
  const SURFACE = 'rgba(128, 128, 128, 0.08)';
  const captchaNode = useRef(null);
  const widgetId = useRef(null);
  const pollRef = useRef(null);
  const timeoutRef = useRef(null);
  const pollGenRef = useRef(0);
  const pollInFlightRef = useRef(false);
  const [destAddress, setDestAddress] = useState('');
  const [captchaToken, setCaptchaToken] = useState(null);
  const [captchaReady, setCaptchaReady] = useState(false);
  const [sendingRequest, setSendingRequest] = useState(false);
  const [nextUseTime, setNextUseTime] = useState(null);
  const [txHash, setTxHash] = useState(null);
  const [isPolling, setIsPolling] = useState(false);
  const [pollingMessage, setPollingMessage] = useState('');
  const [errorMsg, setErrorMsg] = useState(null);
  const [verifyHover, setVerifyHover] = useState(false);
  const [requestHover, setRequestHover] = useState(false);
  const [nowMs, setNowMs] = useState(() => Date.now());
  const keccak256 = useCallback(bytes => {
    const MASK = 0xffffffffffffffffn;
    const RATE = 136;
    const RC = [0x0000000000000001n, 0x0000000000008082n, 0x800000000000808an, 0x8000000080008000n, 0x000000000000808bn, 0x0000000080000001n, 0x8000000080008081n, 0x8000000000008009n, 0x000000000000008an, 0x0000000000000088n, 0x0000000080008009n, 0x000000008000000an, 0x000000008000808bn, 0x800000000000008bn, 0x8000000000008089n, 0x8000000000008003n, 0x8000000000008002n, 0x8000000000000080n, 0x000000000000800an, 0x800000008000000an, 0x8000000080008081n, 0x8000000000008080n, 0x0000000080000001n, 0x8000000080008008n];
    const RHO = [0, 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39, 41, 45, 15, 21, 8, 18, 2, 61, 56, 14];
    const rotl64 = (x, n) => {
      if (n === 0) return x;
      const s = BigInt(n);
      return (x << s | x >> 64n - s) & MASK;
    };
    const keccakF = st => {
      for (let round = 0; round < 24; round++) {
        const C = [0n, 0n, 0n, 0n, 0n];
        for (let x = 0; x < 5; x++) {
          C[x] = st[x] ^ st[x + 5] ^ st[x + 10] ^ st[x + 15] ^ st[x + 20];
        }
        for (let x = 0; x < 5; x++) {
          const D = C[(x + 4) % 5] ^ rotl64(C[(x + 1) % 5], 1);
          for (let y = 0; y < 5; y++) st[x + 5 * y] ^= D;
        }
        const B = new Array(25);
        for (let x = 0; x < 5; x++) {
          for (let y = 0; y < 5; y++) {
            B[y + 5 * ((2 * x + 3 * y) % 5)] = rotl64(st[x + 5 * y], RHO[x + 5 * y]);
          }
        }
        for (let y = 0; y < 5; y++) {
          for (let x = 0; x < 5; x++) {
            const b0 = B[x + 5 * y];
            const b1 = B[(x + 1) % 5 + 5 * y];
            const b2 = B[(x + 2) % 5 + 5 * y];
            st[x + 5 * y] = (b0 ^ (b1 ^ MASK) & b2) & MASK;
          }
        }
        st[0] = (st[0] ^ RC[round]) & MASK;
      }
    };
    const state = [];
    for (let i = 0; i < 25; i++) state.push(0n);
    const padded = new Uint8Array(bytes.length + (RATE - bytes.length % RATE));
    padded.set(bytes);
    padded[bytes.length] = 0x01;
    padded[padded.length - 1] |= 0x80;
    for (let offset = 0; offset < padded.length; offset += RATE) {
      for (let i = 0; i < RATE; i++) {
        state[i / 8 | 0] ^= BigInt(padded[offset + i]) << BigInt(i % 8 * 8);
      }
      keccakF(state);
    }
    const out = new Uint8Array(32);
    for (let i = 0; i < 32; i++) {
      out[i] = Number(state[i / 8 | 0] >> BigInt(i % 8 * 8) & 0xffn);
    }
    return out;
  }, []);
  const addressChecksumOk = useCallback(address => {
    const body = address.slice(2);
    if (body === body.toLowerCase() || body === body.toUpperCase()) return true;
    const lower = body.toLowerCase();
    const ascii = new Uint8Array(40);
    for (let i = 0; i < 40; i++) ascii[i] = lower.charCodeAt(i);
    const hash = keccak256(ascii);
    for (let i = 0; i < 40; i++) {
      const ch = body[i];
      if (ch >= '0' && ch <= '9') continue;
      const byte = hash[i >> 1];
      const nibble = i % 2 === 0 ? byte >> 4 : byte & 0xf;
      const wantUpper = nibble >= 8;
      const isUpper = ch >= 'A' && ch <= 'F';
      if (wantUpper !== isUpper) return false;
    }
    return true;
  }, [keccak256]);
  const trimmed = destAddress.trim();
  const hasAddressShape = (/^0x[0-9a-fA-F]{40}$/).test(trimmed);
  const checksumSuspect = useMemo(() => hasAddressShape && !addressChecksumOk(trimmed), [hasAddressShape, trimmed, addressChecksumOk]);
  const looksLikeSeiBech32 = (/^sei1[a-z0-9]{10,}$/i).test(trimmed);
  const mono = {
    fontFamily: 'var(--sei-font-mono)'
  };
  const labelStyle = {
    fontFamily: 'var(--sei-font-mono)',
    fontSize: '12px',
    textTransform: 'uppercase',
    letterSpacing: '0.05em'
  };
  const buttonLabelStyle = {
    fontFamily: 'var(--sei-font-mono)',
    fontSize: '13px',
    textTransform: 'uppercase',
    letterSpacing: '0.05em'
  };
  const parseDeadline = iso => {
    const ms = new Date(iso).getTime();
    return isFinite(ms) ? ms : null;
  };
  const formatNextUseTime = deadlineMs => {
    const diffMs = deadlineMs - nowMs;
    if (diffMs <= 0) return 'now';
    const hours = Math.floor(diffMs / (1000 * 60 * 60));
    const mins = Math.floor(diffMs % (1000 * 60 * 60) / (1000 * 60));
    if (hours > 0 && mins > 0) return `${hours}h ${mins}m`;
    if (hours > 0) return `${hours}h`;
    if (mins > 0) return `${mins}m`;
    return 'less than a minute';
  };
  const stopPolling = () => {
    pollGenRef.current += 1;
    pollInFlightRef.current = false;
    if (pollRef.current) {
      clearInterval(pollRef.current);
      pollRef.current = null;
    }
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current);
      timeoutRef.current = null;
    }
    setIsPolling(false);
    setPollingMessage('');
  };
  const resetCaptcha = () => {
    setCaptchaToken(null);
    const id = widgetId.current;
    if (id != null && window.hcaptcha && typeof window.hcaptcha.reset === 'function') {
      try {
        window.hcaptcha.reset(id);
      } catch (e) {}
    }
  };
  useEffect(() => {
    return () => {
      if (pollRef.current) clearInterval(pollRef.current);
      if (timeoutRef.current) clearTimeout(timeoutRef.current);
    };
  }, []);
  useEffect(() => {
    if (!nextUseTime) return;
    const display = setInterval(() => setNowMs(Date.now()), 60000);
    const expiry = setTimeout(() => setNextUseTime(null), Math.max(0, nextUseTime - Date.now()));
    return () => {
      clearInterval(display);
      clearTimeout(expiry);
    };
  }, [nextUseTime]);
  useEffect(() => {
    let cancelled = false;
    const loadHCaptcha = () => {
      if (typeof window === 'undefined') return Promise.reject(new Error('no window'));
      if (window.hcaptcha && typeof window.hcaptcha.render === 'function') {
        return Promise.resolve(window.hcaptcha);
      }
      if (window.__seiHCaptchaPromise) return window.__seiHCaptchaPromise;
      window.__seiHCaptchaPromise = new Promise((resolve, reject) => {
        const waitForApi = () => {
          const started = Date.now();
          const tick = () => {
            if (window.hcaptcha && typeof window.hcaptcha.render === 'function') {
              resolve(window.hcaptcha);
              return;
            }
            if (Date.now() - started > 15000) {
              reject(new Error('hCaptcha failed to load'));
              return;
            }
            setTimeout(tick, 50);
          };
          tick();
        };
        if (document.querySelector('script[data-sei-hcaptcha]')) {
          waitForApi();
          return;
        }
        const script = document.createElement('script');
        script.src = 'https://js.hcaptcha.com/1/api.js?render=explicit';
        script.async = true;
        script.defer = true;
        script.setAttribute('data-sei-hcaptcha', '1');
        script.onload = waitForApi;
        script.onerror = () => reject(new Error('hCaptcha script blocked'));
        document.head.appendChild(script);
      });
      return window.__seiHCaptchaPromise;
    };
    loadHCaptcha().then(hcaptcha => {
      if (cancelled || !captchaNode.current) return;
      const id = hcaptcha.render(captchaNode.current, {
        sitekey: HCAPTCHA_SITEKEY,
        size: 'invisible',
        callback: token => {
          setCaptchaToken(token);
          setErrorMsg(null);
        },
        'expired-callback': () => setCaptchaToken(null),
        'error-callback': () => {
          setCaptchaToken(null);
          setErrorMsg('Captcha error. Click Verify Captcha and try again.');
        }
      });
      widgetId.current = id;
      setCaptchaReady(true);
    }).catch(() => {
      window.__seiHCaptchaPromise = null;
      const staleTag = document.querySelector('script[data-sei-hcaptcha]');
      if (staleTag) staleTag.remove();
      if (cancelled) return;
      setErrorMsg('Captcha failed to load. Refresh the page and try again.');
    });
    return () => {
      cancelled = true;
      setCaptchaReady(false);
      const id = widgetId.current;
      widgetId.current = null;
      if (id != null && window.hcaptcha) {
        try {
          if (typeof window.hcaptcha.remove === 'function') window.hcaptcha.remove(id); else window.hcaptcha.reset(id);
        } catch (e) {}
      }
    };
  }, []);
  const startPolling = messageId => {
    stopPolling();
    const generation = pollGenRef.current;
    setIsPolling(true);
    setPollingMessage('Transaction submitted, checking status...');
    const pollOnce = async () => {
      if (pollInFlightRef.current) return;
      pollInFlightRef.current = true;
      try {
        const response = await fetch(`${FAUCET_API_URL}/message/${messageId}`);
        if (!response.ok) throw new Error('Failed to fetch message status');
        const responseJson = await response.json();
        if (pollGenRef.current !== generation) return;
        if (responseJson && responseJson.status === 'success') {
          const data = responseJson.data || ({});
          if (data.status === 'success' && data.txHash) {
            setTxHash(data.txHash);
            stopPolling();
            return;
          }
          if (data.status === 'error') {
            stopPolling();
            setErrorMsg('Transaction failed. Please try again.');
            return;
          }
          if (data.status === 'processing' || data.status === 'pending') {
            setPollingMessage('Transaction is being processed...');
            return;
          }
        }
        setPollingMessage('Checking transaction status...');
      } catch (e) {
        if (pollGenRef.current !== generation) return;
        setPollingMessage('Checking transaction status...');
      } finally {
        if (pollGenRef.current === generation) pollInFlightRef.current = false;
      }
    };
    pollOnce();
    pollRef.current = setInterval(pollOnce, 3000);
    timeoutRef.current = setTimeout(() => {
      stopPolling();
      setErrorMsg('Timed out waiting for the faucet transaction. Check the explorer or try again.');
    }, 300000);
  };
  const handleAddressChange = e => {
    setDestAddress(e.target.value);
    setNextUseTime(null);
    setTxHash(null);
    setErrorMsg(null);
    stopPolling();
  };
  const handleCaptchaVerification = () => {
    setErrorMsg(null);
    const id = widgetId.current;
    if (!captchaReady || id == null || !window.hcaptcha) {
      setErrorMsg('Captcha is still loading. Try again in a moment.');
      return;
    }
    try {
      window.hcaptcha.execute(id);
    } catch (e) {
      setErrorMsg('Could not open captcha. Try again.');
    }
  };
  const faucetError = payload => {
    const data = payload && payload.data || ({});
    const candidates = [payload && payload.message, payload && payload.error, data.message, data.error, data.reason];
    for (const candidate of candidates) {
      if (typeof candidate === 'string' && candidate.trim()) return candidate.trim();
    }
    return 'Error requesting tokens. Please try again later.';
  };
  const handleSubmit = async () => {
    setSendingRequest(true);
    setErrorMsg(null);
    setTxHash(null);
    try {
      const response = await fetch(`${FAUCET_API_URL}/atlantic-2`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          address: trimmed,
          captchaToken
        })
      });
      let responseJson = null;
      try {
        responseJson = await response.json();
      } catch (e) {
        responseJson = null;
      }
      if (responseJson && responseJson.status === 'success' && responseJson.data && responseJson.data.messageId) {
        startPolling(responseJson.data.messageId);
      } else if (responseJson && responseJson.data && responseJson.data.nextAllowedUseDate) {
        const deadline = parseDeadline(responseJson.data.nextAllowedUseDate);
        if (deadline) {
          setNowMs(Date.now());
          setNextUseTime(deadline);
        } else {
          setErrorMsg('Rate limited by the faucet. Try again later.');
        }
      } else {
        setErrorMsg(faucetError(responseJson));
      }
      resetCaptcha();
    } catch (e) {
      setErrorMsg('Error requesting tokens. Please try again later.');
      resetCaptcha();
    } finally {
      setSendingRequest(false);
    }
  };
  const isSubmitDisabled = !!nextUseTime || !hasAddressShape || !captchaToken || isPolling || sendingRequest;
  const isVerifyDisabled = !captchaReady || !!captchaToken || sendingRequest || isPolling || !!nextUseTime;
  const describeBlocker = () => {
    if (nextUseTime || isPolling || sendingRequest || txHash) return null;
    if (looksLikeSeiBech32 && !hasAddressShape) return 'Use the 0x EVM address, not the sei1… address.';
    if (trimmed && !hasAddressShape) return 'Enter a valid EVM address: 0x followed by 40 hex characters.';
    if (hasAddressShape && !captchaToken) return 'Complete the captcha verification to enable the request.';
    return null;
  };
  const blockedReason = describeBlocker();
  const handleAddressKeyDown = e => {
    if (e.key !== 'Enter') return;
    e.preventDefault();
    if (!hasAddressShape) return;
    if (!captchaToken) handleCaptchaVerification(); else if (!isSubmitDisabled) handleSubmit();
  };
  const DropletIcon = ({className}) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
			<path d="M7.502 19.423c2.602 2.105 6.395 2.105 8.996 0c2.602 -2.105 3.262 -5.708 1.566 -8.546l-4.89 -7.26c-.42 -.625 -1.287 -.803 -1.936 -.397a1.376 1.376 0 0 0 -.41 .397l-4.893 7.26c-1.695 2.838 -1.035 6.441 1.567 8.546" />
		</svg>;
  const ShieldCheckIcon = ({className}) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
			<path d="M11.46 20.846a12 12 0 0 1 -7.96 -14.846a12 12 0 0 0 8.5 -3a12 12 0 0 0 8.5 3a12 12 0 0 1 -.09 7.06" />
			<path d="M15 19l2 2l4 -4" />
		</svg>;
  const SendIcon = ({className}) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
			<path d="M10 14l11 -11" />
			<path d="M21 3l-6.5 18a.55 .55 0 0 1 -1 0l-3.5 -7l-7 -3.5a.55 .55 0 0 1 0 -1l18 -6.5" />
		</svg>;
  const SpinnerIcon = ({className}) => <svg className={className} aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
			<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
			<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 0 1 8-8v4a4 4 0 0 0-4 4H4z" />
		</svg>;
  const HourglassIcon = ({className}) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
			<path d="M6.5 7h11" />
			<path d="M6.5 17h11" />
			<path d="M6 20v-2a6 6 0 1 1 12 0v2a1 1 0 0 1 -1 1h-10a1 1 0 0 1 -1 -1" />
			<path d="M6 4v2a6 6 0 1 0 12 0v-2a1 1 0 0 0 -1 -1h-10a1 1 0 0 0 -1 1" />
		</svg>;
  const CheckIcon = ({className}) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
			<path d="M5 12l5 5l10 -10" />
		</svg>;
  const AlertTriangleIcon = ({className}) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
			<path d="M12 9v4" />
			<path d="M10.363 3.591l-8.106 13.534a1.914 1.914 0 0 0 1.636 2.871h16.214a1.914 1.914 0 0 0 1.636 -2.87l-8.106 -13.536a1.914 1.914 0 0 0 -3.274 0" />
			<path d="M12 16h.01" />
		</svg>;
  const ExternalLinkIcon = ({className}) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
			<path d="M12 6h-6a2 2 0 0 0 -2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-6" />
			<path d="M11 13l9 -9" />
			<path d="M15 4h5v5" />
		</svg>;
  const requestEnabledStyle = {
    backgroundColor: requestHover ? 'var(--sei-maroon-200)' : 'var(--sei-maroon-100)',
    color: '#ffffff',
    cursor: 'pointer'
  };
  const requestDisabledStyle = {
    backgroundColor: SURFACE,
    color: 'rgba(128, 128, 128, 0.7)',
    cursor: 'not-allowed'
  };
  return <div className="not-prose w-full flex flex-col my-4" style={{
    position: 'relative'
  }}>
			{}
			<div ref={captchaNode} style={{
    position: 'absolute',
    width: 0,
    height: 0,
    overflow: 'hidden'
  }} />

			<div className="overflow-hidden" style={{
    border: `1px solid ${HAIRLINE}`
  }}>
				<div className="flex items-stretch" style={{
    borderBottom: `1px solid ${HAIRLINE}`
  }}>
					<div className="flex items-center px-5" style={{
    borderRight: `1px solid ${HAIRLINE}`
  }}>
						<DropletIcon className="w-5 h-5 text-neutral-400 dark:text-neutral-500" />
					</div>
					<input className="flex-1 min-w-0 px-5 py-5 outline-none bg-transparent text-neutral-900 dark:text-neutral-100 placeholder:text-neutral-400 dark:placeholder:text-neutral-500" placeholder="Enter your EVM (0x...) address" value={destAddress} onChange={handleAddressChange} onKeyDown={handleAddressKeyDown} disabled={sendingRequest || isPolling} autoComplete="off" autoCapitalize="off" autoCorrect="off" spellCheck={false} aria-label="Sei EVM address" style={{
    ...mono,
    fontSize: '14px'
  }} />
					<div className="px-4 flex items-center" style={{
    borderLeft: `1px solid ${HAIRLINE}`
  }}>
						<span className="text-neutral-600 dark:text-neutral-300 px-3 py-2" style={labelStyle}>
							Testnet
						</span>
					</div>
				</div>

				<div className="flex items-stretch">
					<button type="button" onClick={handleCaptchaVerification} disabled={isVerifyDisabled} onMouseEnter={() => setVerifyHover(true)} onMouseLeave={() => setVerifyHover(false)} className={`flex-1 flex items-center justify-center gap-3 px-5 py-5 ${captchaToken ? 'text-green-700 dark:text-green-400' : 'text-neutral-600 dark:text-neutral-400'}`} style={{
    ...buttonLabelStyle,
    borderRight: `1px solid ${HAIRLINE}`,
    backgroundColor: captchaToken ? 'rgba(16, 185, 129, 0.12)' : verifyHover && !isVerifyDisabled ? SURFACE : 'transparent',
    cursor: isVerifyDisabled ? 'not-allowed' : 'pointer',
    opacity: isVerifyDisabled && !captchaToken ? 0.6 : 1
  }}>
						<ShieldCheckIcon className="w-4 h-4" />
						{captchaToken ? 'Verified' : 'Verify Captcha'}
					</button>

					<button type="button" onClick={handleSubmit} disabled={isSubmitDisabled} onMouseEnter={() => setRequestHover(true)} onMouseLeave={() => setRequestHover(false)} className="flex-1 flex items-center justify-center gap-3 px-5 py-5" style={{
    ...buttonLabelStyle,
    ...isSubmitDisabled ? requestDisabledStyle : requestEnabledStyle
  }}>
						{sendingRequest || isPolling ? <SpinnerIcon className="animate-spin w-4 h-4" /> : <SendIcon className="w-4 h-4" />}
						{isPolling ? 'Processing...' : 'Request SEI'}
					</button>
				</div>
			</div>

			{}
			<div role="status">
				{blockedReason ? <p className="mt-4 text-sm text-neutral-600 dark:text-neutral-400">{blockedReason}</p> : null}

				{checksumSuspect ? <div className="mt-4 flex items-start gap-3 px-4 py-3 text-sm" style={{
    borderLeft: '3px solid var(--sei-gold-100)',
    backgroundColor: 'rgba(150, 111, 34, 0.08)'
  }}>
						<AlertTriangleIcon className="w-4 h-4 shrink-0 mt-0.5" />
						<p className="text-neutral-700 dark:text-neutral-300">
							This address fails its EIP-55 checksum, so a character may be wrong. Check it against your wallet. You can still request, and the faucet will reject it if the address is bad.
						</p>
					</div> : null}
			</div>

			<div role="alert">
				{errorMsg ? <div className="mt-4 flex items-start gap-3 px-4 py-3 text-sm" style={{
    borderLeft: '3px solid var(--sei-maroon-100)',
    backgroundColor: 'rgba(96, 0, 20, 0.08)'
  }}>
						<p className="text-neutral-700 dark:text-neutral-300">{errorMsg}</p>
					</div> : null}
			</div>

			<div role="status">
				{nextUseTime ? <div className="mt-4 flex items-center gap-3 px-4 py-3 text-sm" style={{
    borderLeft: '3px solid var(--sei-maroon-100)',
    backgroundColor: 'rgba(96, 0, 20, 0.08)'
  }}>
						<HourglassIcon className="w-4 h-4 shrink-0" />
						<p className="text-neutral-700 dark:text-neutral-300">
							You can request tokens again in{' '}
							<span style={{
    color: 'var(--sei-maroon-50)'
  }}>{formatNextUseTime(nextUseTime)}</span>
						</p>
					</div> : null}

				{isPolling || txHash ? <div className="mt-4 flex items-center gap-3 px-4 py-3 text-sm" style={{
    borderLeft: `3px solid ${isPolling ? 'var(--sei-gold-100)' : 'var(--sei-live)'}`,
    backgroundColor: isPolling ? 'rgba(150, 111, 34, 0.08)' : 'rgba(56, 223, 0, 0.08)'
  }}>
						{isPolling ? <SpinnerIcon className="animate-spin w-4 h-4 shrink-0" /> : <CheckIcon className="w-4 h-4 shrink-0" />}
						<div className="flex-1">
							{isPolling ? <p className="text-neutral-700 dark:text-neutral-300">{pollingMessage}</p> : <div className="flex flex-wrap items-center gap-x-3 gap-y-1">
									<p className="text-neutral-700 dark:text-neutral-300">Transaction confirmed</p>
									<a href={`${EXPLORER_TX}/${txHash}`} target="_blank" rel="noopener noreferrer" className="flex items-center gap-1.5 no-underline" style={{
    ...labelStyle,
    fontSize: '11px',
    color: 'var(--sei-maroon-50)'
  }}>
										View on Explorer
										<ExternalLinkIcon className="w-3 h-3" />
									</a>
								</div>}
						</div>
					</div> : null}
			</div>
		</div>;
};

<Warning>This faucet distributes testnet (atlantic-2) tokens only, which have no real-world value and are intended for testing purposes.</Warning>

## Request Tokens

Enter your EVM (`0x`) address below to receive testnet SEI.

<Faucet />

## Usage Notes

* **Rate Limits** — Each address can request tokens once per day. Wait 24 hours between requests.
* **Testnet Only** — Tokens are distributed on `atlantic-2` and cannot be used on mainnet.
* **Gas Coverage** — A single request provides enough SEI to cover hundreds of testnet transactions.

## Testnet USDC

Need testnet USDC for testing ERC-20 workflows? Use the Circle Faucet to receive USDC on Sei's testnet.

* [Circle Faucet](https://faucet.circle.com)
* [USDC on Sei Guide](/evm/usdc-on-sei)

<Info>If the faucet is temporarily unavailable, try again later or reach out in the [Sei Discord](https://discord.gg/sei) or [Telegram](https://t.me/+KZdhZ1eE-G01NmZk) developer channels for assistance.</Info>
