Testnet Faucet · Quickstart

Mint testnet TTT and CTTT in one client call.

Install the SDK, create a faucet client, mint plaintext test tokens on Sepolia, read a balance.

Testnet only — Sepolia (11155111) + local Anvil (31337)
The faucet has no mainnet deployment by design. Both test tokens mint openly to anyone — they carry no value and exist purely to feed the confidential products during development.

1. Connect wallet

Click Connect (top right) and choose Sepolia. Your wallet is your API key.

The SDK only ever consumes a publicClient plus an optional walletClient. Reads work with just the public client; the open mints need a connected wallet to sign.

2. Get testnet ETH

The mints are real transactions — they need gas.

Sepolia ETH is free. Google Cloud's Web3 faucet (cloud.google.com/…/sepolia) and Alchemy's (alchemy.com/faucets/ethereum-sepolia) both drip enough for a few faucet mints. This faucet itself dispenses TTT/CTTT, not ETH.

3. Install

Add the SDK alongside viem. @tokenops/sdk leaves viem as a peer dep. Add wagmi only if you also want the React hooks under @tokenops/sdk/testnet-faucet/react.

pnpm add @tokenops/sdk viem
No encryptor — simpler than the FHE products
Unlike @tokenops/sdk/fhe-vesting / fhe-airdrop / fhe-disperse, the faucet has no encryptor setup and no @zama-fhe dependency. Faucet mints take public plaintext bigint amounts.

4. Create the client & mint

One constructor call, then mint either token. TTT is a plain 18-decimal ERC-20; CTTT is its 6-decimal ERC-7984 confidential wrapper, minted backed by the underlying.

lib/faucet.ts
ts
import { createPublicClient, createWalletClient, custom, http } from "viem";
import { sepolia } from "viem/chains";
import { TestnetFaucetClient } from "@tokenops/sdk/testnet-faucet";

const publicClient = createPublicClient({ chain: sepolia, transport: http() });
const [account] = (await window.ethereum.request({ method: "eth_requestAccounts" })) as [`0x${string}`];
const walletClient = createWalletClient({
  account,
  chain: sepolia,
  transport: custom(window.ethereum),
});

// No encryptor, no @zama-fhe dependency — the faucet mints take plaintext bigint amounts.
// No on-chain factory or deploy step (unlike fhe-vesting / fhe-airdrop): the client
// wires straight to the pre-deployed CTTT proxy. createTestnetFaucetClient(config) is an
// equivalent thin factory helper if you prefer it. chainId / address are inferred from
// the clients + on-chain deployments (the TTT backing token is read from the CTTT wrapper).
const faucet = new TestnetFaucetClient({ publicClient, walletClient });
lib/mint-ttt.ts
ts
// TTT = TokenopsTestToken: plain 18-decimal ERC-20, open mint(to, amount).
const ttt = await faucet.mintUnderlying({
  amount: 100n * 10n ** 18n, // 100 TTT (18 decimals)
});

console.log({ mintHash: ttt.hash, to: ttt.to, amount: ttt.amount });
lib/mint-cttt.ts
ts
// CTTT = ConfidentialTokenopsTestToken: 6-decimal ERC-7984 confidential wrapper.
// The open faucet mint is *backed* — it mints the underlying TTT then wraps it.
// mintConfidential parses the ConfidentialMint event and returns the euint64 handle.
const cttt = await faucet.mintConfidential({
  amount: 50n * 10n ** 6n, // 50 CTTT (6 decimals — NOT 18)
});

console.log({
  mintHash: cttt.hash,
  to: cttt.to,
  amount: cttt.amount,
  handle: cttt.handle, // encrypted euint64 handle (Hex)
  underlyingMinted: cttt.underlyingMinted,
});
Watch the decimals: TTT is 18, CTTT is 6
underlyingDecimals() returns 18 (TTT); decimals() returns 6 (CTTT). Scale each amountagainst the right token or you'll be off by 1012×.

5. Read a balance

The TTT balance is a plain bigint. The CTTT balance is an encrypted euint64 handle — a Hex string, not a number.

lib/balances.ts
ts
// Plaintext TTT balance — a normal bigint.
const tttBalance = await faucet.underlyingBalanceOf();
console.log("TTT balance:", tttBalance);

// Confidential CTTT balance — an encrypted euint64 handle (Hex), not a number.
// Decrypting it is a separate, opt-in FHE step; the faucet never decrypts for you.
const ctttHandle = await faucet.confidentialBalanceOf();
console.log("CTTT handle:", ctttHandle);
Supply can run out
The backed CTTT mint reverts once maxTotalSupply() is hit; the SDK maps the on-chain ERC7984TotalSupplyOverflow revert to FaucetSupplyExhaustedError (TOKENOPS_FAUCET_SUPPLY_EXHAUSTED). There are no roles, pause, or RBAC to worry about — the mint is open.

Where to go next

Prefer React? Every read and mint here has a hook.

Tip: use the freshly-minted TTT/CTTT as the token input for the confidential vesting, airdrop, and disperse stories.