Skip to main content

API Reference

Exports

ExportKindDescription
ConfidentialAirdropFactoryClientclassTyped wrapper around the deployed factory. Methods: createConfidentialAirdrop (rich-return), createAndFundConfidentialAirdrop (rich-return), fundConfidentialAirdrop, predictAirdropAddress, fee management
createConfidentialAirdropFactoryClientfunctionConvenience constructor
ConfidentialAirdropFactoryClientConfiginterfaceConfig for the factory client
CreateAirdropArgsinterfaceArgs for factory.createConfidentialAirdrop
CreateAirdropResultinterface{ hash, airdrop } returned by factory.createConfidentialAirdrop / createAndFundConfidentialAirdrop
CreateAndFundAirdropArgsinterfaceArgs for factory.createAndFundConfidentialAirdrop
FundAirdropArgsinterfaceArgs for factory.fundConfidentialAirdrop
PredictAirdropArgsinterfaceArgs for factory.predictAirdropAddress (stable across blocks — block.number is not packed into immutable args)
ConfidentialAirdropClientclassTyped wrapper around a deployed airdrop clone
createConfidentialAirdropClientfunctionConvenience constructor
ConfidentialAirdropClientConfiginterfaceConfig for the airdrop client
ClaimArgsinterfaceArgs for airdrop.claim
GetClaimAmountArgsinterfaceArgs for airdrop.getClaimAmount
EncryptedViewResultinterface{ handle: Hex, hash: Hex } returned by getClaimAmount
signClaimAuthorizationfunctionBuild the EIP-712 admin signature for a claim
encryptUint64functionEncrypt a single uint64 to an externalEuint64 handle + input proof
encryptUint64BatchfunctionEncrypt N uint64 values under a single input proof
resolveEncryptorfunctionNormalize an EncryptorSource to the live Encryptor
EncryptorinterfaceStructural interface for Zama RelayerWeb / RelayerNode / mock
EncryptorSourcetypeEncryptor | (() => Encryptor | undefined) — eager or lazy
EncryptUint64ArgsinterfaceArgs for encryptUint64
EncryptUint64BatchArgsinterfaceArgs for encryptUint64Batch
FheValueInputtypeDiscriminated union of FHE input types
AirdropParamsinterfaceStruct passed to factory create methods
CustomFeeinterfacePer-creator fee override from factory.getCustomFee
EncryptedInputinterfaceSingle { handle: Hex, inputProof: Hex }
EncryptedInputsinterfaceBatch { handles: Hex[], inputProof: Hex }
confidentialAirdropFactoryAbiconstABI for the factory contract
confidentialAirdropCloneableAbiconstABI for the airdrop clone

API reference

ConfidentialAirdropFactoryClient

Wraps the deployed ConfidentialAirdropFactory. Read methods use publicClient; write methods require walletClient.

Constructor / factory function

new ConfidentialAirdropFactoryClient(config: ConfidentialAirdropFactoryClientConfig)
createConfidentialAirdropFactoryClient(config: ConfidentialAirdropFactoryClientConfig)

config.address overrides the on-chain factory address. Omit it once the factory is deployed on the target chain — the client resolves the address from publicClient.chain.id automatically.

Writes

MethodDescription
createConfidentialAirdrop(args)Deploy a new airdrop clone
createAndFundConfidentialAirdrop(args)Deploy and fund in a single tx; requires factory operator approval on token
fundConfidentialAirdrop(args)Fund an existing (or pre-deployed) clone; requires factory operator approval on token
setFeeCollector(newFeeCollector)Change the fee collector address (FEE_MANAGER_ROLE)
setDefaultGasFee(newGasFee)Change the default per-claim gas fee (FEE_MANAGER_ROLE)
setCustomFee(campaignCreator, gasFee)Set a per-creator gas fee override (FEE_MANAGER_ROLE)
disableCustomFee(campaignCreator)Remove a per-creator fee override (FEE_MANAGER_ROLE)

Reads

MethodReturnsDescription
predictAirdropAddress(args)AddressCompute deterministic clone address without deploying. gasFee must match what the factory would charge at deploy time.
implementation()AddressCurrent LibClone implementation address
defaultGasFee()bigintDefault per-claim gas fee in wei
feeCollector()AddressCurrent fee collector address
getCustomFee(campaignCreator)CustomFeePer-creator fee override
getInitCodeHash(args)HexInit-code hash for CREATE2 address derivation

ConfidentialAirdropClient

Wraps a deployed ConfidentialAirdropCloneable clone. Requires address (the clone address) in config.

Constructor / factory function

new ConfidentialAirdropClient(config: ConfidentialAirdropClientConfig)
createConfidentialAirdropClient(config: ConfidentialAirdropClientConfig)

config.aclAddress defaults to the chain's known FHEVM ACL address. Override only on custom networks.

Claim

MethodDescription
claim({ encryptedInput, signature, value?, account? })Claim tokens using an EIP-712 admin signature. Submits the admin-issued encryptedInput verbatim (no re-encryption — the signature commits to the handle). Sends GAS_FEE() as msg.value unless value is overridden.
getClaimAmount({ encryptedInput, signature, account? })Write transaction. Verify allocation and grant caller ACL on the encrypted handle. Returns { handle, hash }. Pass handle to Zama's userDecrypt to read the plaintext. See note below.

:::note getClaimAmount is a write transaction It submits a tx that calls FHE.allow(handle, msg.sender) on-chain so the Zama relayer can authorize your userDecrypt request. The handle is extracted from the receipt's ACL Allowed event — never from simulateContract (simulation returns a divergent handle that has no ACL grant). See FHEVM docs for the userDecrypt flow. :::

Status checks (free reads)

MethodReturnsDescription
isClaimWindowActive()booleantrue if within time window and not paused
hasClaimStarted()booleantrue if past START_TIME
hasClaimEnded()booleantrue if past endTime
isSignatureValid({ encryptedAmountHandle, signature, caller })booleanReturns false (not a revert) if already claimed, window inactive, or signer lacks admin role. Caller-specific (the on-chain check binds to msg.sender) — pass the address that will submit the claim. Use for pre-claim UI validation before paying gas.
isSignatureClaimed(user, encryptedAmountHandle)booleanFrontend default. Checks whether a claim has been consumed given the user address and the encrypted handle from the claim payload. The contract recomputes the struct hash internally.
claimedSignatures(signatureHash)booleanLow-level storage check. Takes a pre-computed EIP-712 struct hash (bytes32). Use isSignatureClaimed instead unless you already hold the hash.

Admin writes

MethodDescription
withdraw(recipient)Withdraw all confidential tokens. DEFAULT_ADMIN_ROLE only.
setPaused(paused)Pause or unpause claims. DEFAULT_ADMIN_ROLE only.
extendClaimWindow(newEndTime)Extend end time. Requires CAN_EXTEND_CLAIM_WINDOW == true. DEFAULT_ADMIN_ROLE only.
withdrawGasFee(recipient, amount)Withdraw ETH gas fees. Pass 0n to drain the full balance. FEE_COLLECTOR_ROLE only.
withdrawOtherToken(tokenAddress, recipient)Rescue accidentally sent ERC-20. DEFAULT_ADMIN_ROLE only.
withdrawOtherConfidentialToken(tokenAddress, recipient)Rescue accidentally sent ERC-7984. DEFAULT_ADMIN_ROLE only.

Role management

MethodDescription
hasRole(role, account)Check role membership
grantRole(role, accountTarget)Grant role (admin only)
revokeRole(role, accountTarget)Revoke role (admin only)

Immutable reads

MethodReturnsDescription
token()AddressERC-7984 token this airdrop was deployed for
gasFee()bigintGas fee in wei required per claim
startTime()numberClaim start timestamp (unix)
canExtendClaimWindow()booleanWhether admin can extend the claim window
endTime()numberClaim end timestamp (unix, mutable)
isPaused()booleanCurrent pause state
deploymentBlockNumber()bigintBlock at which the clone was deployed
domainSeparator()HexEIP-712 domain separator (for off-chain signing tools)

signClaimAuthorization

Build an EIP-712 admin signature authorizing a specific recipient to claim a specific encrypted amount.

import { signClaimAuthorization, encryptUint64 } from "@tokenops/sdk/fhe-airdrop";

const recipient = "0x0000000000000000000000000000000000000001" as `0x${string}`;

// Bind the input proof to the RECIPIENT. Zama proofs commit to
// (contractAddress, userAddress) at encrypt time, and `FHE.fromExternal`
// rejects proofs bound to any other address.
const encrypted = await encryptUint64({
encryptor,
contractAddress: airdropAddress,
userAddress: recipient,
value: 1_000_000n,
});

const signature = await signClaimAuthorization({
walletClient: adminWalletClient, // must have DEFAULT_ADMIN_ROLE on the clone
airdropAddress,
recipient,
encryptedAmountHandle: encrypted.handle,
});
// Deliver { encryptedInput: encrypted, signature } to the recipient.

The signature covers Claim(address recipient, bytes32 encryptedAmount) under the clone's EIP-712 domain (name: "ConfidentialAirdrop", version: "1", verifyingContract: airdropAddress). Each (recipient, encryptedAmountHandle) pair is single-use.


FHE and ACL pitfalls

These pitfalls apply to this subpath. See CLAUDE.md for the full list.

getClaimAmount uses a write transaction, not simulation. The contract calls FHE.allow(handle, msg.sender) inside the tx. The handle value is determined by an on-chain counter at execution time — simulation runs against a snapshot that diverges from the executed counter, so the simulated handle never receives an ACL grant. The SDK extracts the correct handle from the receipt's ACL.Allowed event. Do not use publicClient.simulateContract as a substitute.

ACL grants are append-only. Once getClaimAmount grants a handle to an address, that access cannot be revoked. Design your UX accordingly — do not call getClaimAmount speculatively.

euint64 overflow. Plaintext amount values must fit in uint64 (max 2^64 - 1 ≈ 1.8 × 10^19). For ERC-7984 tokens (6 decimals), this is a maximum of about 1.8 × 10^13 tokens — far above any realistic allocation. The SDK validates this and throws if the value overflows.

For the full FHE ACL specification and userDecrypt flow, see docs.zama.ai/fhevm.