Delegation State Machine
What this covers
How to reason about deposit delegation, when to clear an existing vault assignment, and how the React helper automates the current EscrowV2 path.
When to use this
Use this when you are building vault tooling, delegation UIs, or any workflow where a depositor can switch a deposit between managers.
The current route
At the hook-utility level, the stable route is direct EscrowV2 delegation:
import { getDelegationRoute } from '@zkp2p/sdk';
const route = getDelegationRoute(client, escrowAddress);
console.log(route); // 'v2'
Low-level compatibility methods such as setDepositRateManager() still exist on the client, but useVaultDelegation() and the exported routing helpers currently target the direct V2 path.
Classify the current state
import { classifyDelegationState } from '@zkp2p/sdk';
const state = classifyDelegationState(
currentRateManagerId,
currentRegistry,
targetRateManagerId,
targetRegistry,
);
// 'not_delegated' | 'delegated_here' | 'delegated_elsewhere'
How to interpret it:
not_delegated: safe to delegate immediatelydelegated_here: already on the correct vault, so skip the writedelegated_elsewhere: switch required
Delegate one deposit
import { useVaultDelegation } from '@zkp2p/sdk/react';
const escrowAddress = '0x0000000000000000000000000000000000000001' as const;
const vaultAddress = '0x0000000000000000000000000000000000000002' as const;
const rateManagerId =
'0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' as const;
const { delegateDeposit, clearDelegation } = useVaultDelegation({
client,
sendTransaction: async ({ to, data, value }) => {
return walletClient.sendTransaction({ to, data, value, account });
},
});
await delegateDeposit({
escrow: escrowAddress,
depositId: 42n,
registry: vaultAddress,
rateManagerId,
currentRateManagerId,
currentRateManagerRegistry: currentRegistry,
});
Switching vaults
If a deposit is already delegated elsewhere:
- with
sendBatch:useVaultDelegation()can batch clear + set into one smart-account operation - without
sendBatch: clear first, then delegate in a second step
const { delegateDeposit } = useVaultDelegation({
client,
sendTransaction,
sendBatch: async (calls) => smartAccount.sendUserOperation({ calls }),
});
Useful helpers
import {
ZERO_RATE_MANAGER_ID,
isZeroRateManagerId,
normalizeRateManagerId,
normalizeRegistry,
} from '@zkp2p/sdk';
console.log(ZERO_RATE_MANAGER_ID);
console.log(isZeroRateManagerId(currentRateManagerId));
console.log(normalizeRateManagerId(currentRateManagerId));
console.log(normalizeRegistry(currentRegistry));
These helpers are useful when your UI stores values from several sources and you want one canonical comparison format.
Key points
- In the current SDK, the public routing helpers resolve to direct EscrowV2 delegation
delegated_elsewhereis the state that requires a clear-first path unless you batchuseVaultDelegation()is the easiest integration surface because it handles skips, clears, and smart-account batching logic for you- Keep delegation comparisons normalized; address casing differences should not produce false state changes