2.0 RC docsView 1.x docs
Testnet Faucet 2.0 · Flows

The testnet faucet flows.

Sepolia only: mint plaintext TTT, mint backed CTTT, read both balances, inspect rate and supply, gate on the chain.

v2.0.0-rc.1 release candidate
The API is frozen and later candidates carry fixes only, except the receipt-free and Safe create surface of /fhe-airdrop, which is @beta. Published on the next dist-tag; latest stays on 1.6.0 until 2.0.0.
  1. 1

    Mint TTT underlying

    TokenopsTestToken (TTT) is a plain 18-decimal ERC-20 with an open mint(to, amount). Anyone on Sepolia can mint plaintext test tokens: no encryptor, no roles, no pause.

    1. useMintUnderlying().mutate({ amount, to?, gas? }) submits mint(to, amount) on TTT; amount is a plaintext bigint in 18-decimal units, gas an optional fixed limit
    2. MintUnderlyingResult is { hash, to, amount }, returned once the receipt confirms success
    3. Invalidate ["tokenops-sdk", "testnet-faucet"] and read it back with useUnderlyingBalance({ account })
  2. 2

    Mint CTTT backed-confidential

    ConfidentialTokenopsTestToken (CTTT) is a 6-decimal ERC-7984 wrapper behind a UUPS proxy with an open, fully-backed mint. The amount is public: the faucet mints the TTT behind it, then credits the confidential balance.

    1. useMintConfidential().mutate({ amount, to?, gas? }) submits mint(to, amount) on CTTT; amount is a plaintext uint64 in 6-decimal units
    2. MintConfidentialResult is { hash, to, amount, underlyingMinted, handle }, decoded from the ConfidentialMint event
    3. FaucetSupplyExhaustedError maps the ERC7984TotalSupplyOverflow revert once the backing reaches maxTotalSupply
  3. 3

    Read public + encrypted balances

    TTT exposes a plaintext balance; CTTT exposes a euint64 handle. The faucet is the producer side only: it returns the handle as-is, and you decrypt it with your own Zama SDK instance against the CTTT address.

    1. useUnderlyingBalance({ account, query }) returns the TTT balance as a bigint
    2. useConfidentialBalance({ account, query }) returns the CTTT balance handle (Hex); a never-credited account reads the zero handle
    3. Headless: client.underlyingBalanceOf(account) and client.confidentialBalanceOf(account)
  4. 4

    Read faucet metadata, rate + supply

    Static and dynamic faucet state in read-only calls: metadata for both tokens, the backing rate, and supply headroom against the CTTT cap. The static reads (metadata, rate, decimals, underlying decimals and address, max supply) already default to staleTime: Infinity; pass query: { staleTime: 0 } to refetch them.

    1. useFaucetMetadata() returns FaucetMetadata { confidential, underlying, rate, maxTotalSupply }
    2. useFaucetRate() / useMaxTotalSupply() / useInferredTotalSupply() return the rate and supply headroom as bigints
    3. useUnderlyingTokenAddress() returns the TTT address backing CTTT, read from the wrapper
  5. 5

    Gate on the chain

    The faucet refuses mainnet and every chain outside TESTNET_FAUCET_SUPPORTED_CHAIN_IDS. The hooks catch the constructor error as resolutionError, so the UI never crashes during render; gate the mint button on the chain instead of waiting for the rejection.

    1. isTestnetFaucetChainId(chainId) tells you up front whether to show the faucet or a switch-network prompt
    2. On an unsupported chain the read hooks stay disabled and useMintConfidential / useMintUnderlying reject with UnsupportedChainError
    3. err.context.method is "TestnetFaucetClient" and err.context.hint names the fix: connect the clients to Sepolia