useRotateMerkleRoot
Rebuild a campaign against updated totals and publish the new root in one call.
@tokenops/sdk/fhe-airdrop/react{ mutate, mutateAsync, isPending, error, data }Description
Rebuild a campaign against updated totals and publish the new root in one call.
`recipients` must carry CUMULATIVE totals, not deltas. claimedAmount
survives a rotation and a claim pays
newTotal - min(newTotal, alreadyDelivered), so passing increments pays the
increment minus everything already delivered — encrypted zero for most
rosters. A total below what an account already received pays nothing; a
rotation cannot claw back.
One relayer request per 32 recipients, issued concurrently; every leaf is submittable by any address.
The root is published LAST, so a failed rebuild leaves the previous campaign intact and claimable. Once it lands the old proofs stop verifying: distribute the new entries to every recipient, including those whose total did not change, or they are stuck.
Requires MERKLE_ADMIN_ROLE and an instance created with
isMerkleRootMutable: true. Distinct from useSetMerkleRoot, which
publishes a root you already built.
Invalidates: this instance's useMerkleRoot entry only. Explicitly NOT
useClaimedAmount: a rotation changes no account's delivered total (it is
keyed by account, not by root), so invalidating it would fan out a pointless
RPC per mounted recipient row.
Signature
function useRotateMerkleRoot(options: AirdropInstanceClientOptions): UseMutationResult<RotatedCampaign, Error, RotateMerkleRootVariables>;Parameters
Shape of the object you pass to .mutate(args) is the SDK type RotateMerkleRootVariables. Inspect the type for the full shape (discriminated unions collapse to a tagged variant at call time).
Example
const rotate = useRotateMerkleRoot({ address: airdrop, encryptor: () => sdk });
const { root, entries, hash } = await rotate.mutateAsync({
recipients: [{ recipient: alice, cumulativeTotal: 1_500_000n }], // was 1_000_000n
});Pulled directly from the hook's TSDoc block — the same snippet your IDE shows on hover.
Errors
This mutation can reject with SDK-level, product-level, or generic-fallback errors. Product classes carry the offending value as fields — render them inline instead of a generic "transaction failed." See Airdrop v2 › Errors for the per-class recovery table.
Invalidation recipe
After this mutation succeeds, invalidate the queries it affects so consumer UI re-fetches fresh state. The SDK never auto-invalidates — that's a consumer decision (different apps cache different shapes).
import { useQueryClient } from "@tanstack/react-query";
const queryClient = useQueryClient();
const rotateMerkleRoot = useRotateMerkleRoot(/* options */);
rotateMerkleRoot.mutate(args, {
onSuccess() {
// Coarse invalidation: refresh every cached read on this product surface.
queryClient.invalidateQueries({
queryKey: ["tokenops-sdk", "fhe-airdrop"],
});
},
});See also
Other Write hooks in airdrop v2:
useFundAirdropTop up an already-deployed instance's pool through the factory.useEcdsaClaimClaim a signed encrypted allocation on an ECDSAConfidentialAirdrop instance.useMerkleClaimClaim the outstanding amount for a proof-bearing entry on a MerkleConfidentialAirdrop instance.useGrantInstanceRolesSplit an airdrop instance's roles after create — one grantRole / revokeRole transaction per requested assignment, submitted in the order planInstanceRoleSplit computes.