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.
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@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.
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 });// 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 });// 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,
});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.
// 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);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.
Faucet hooks
11 wagmi + TanStack Query hooks — useMintUnderlying, useMintConfidential, useUnderlyingBalance, useConfidentialBalance, and more.
TestnetFaucetClient
The full headless surface: mintUnderlying, mintConfidential, balances, decimals, getMetadata, rate, supply, chain guards.