The CCA calls UmiaValidationHook.validate on every bid, and validate is the only way a credential reaches the hook. These charts follow the contract branch by branch, so every revert a bid can hit appears once. Nodes that start with Revert name the error; rounded nodes let the bid through. For the hookData formats and the owner functions in detail, see Validation Hook .
The CCA wraps any revert from the hook as ValidationHookCallFailed(reason), so a failed bid carries the hook's error inside.
yes
no
Deploy UmiaValidationHook(owner, reclaim, signer) Unpaired, validate() allows every bid
Create the CCA with this hook as its validationHook The CCA's hook is immutable
setCCA(cca), once, later calls are no-ops Caches the CCA step schedule, reverting on empty, misaligned, zero-length or more than 256 steps
Configure steps: owner calls, in any order, at any time (see the table below)
Optional, per step: multicall(requireStepNotStarted(step), the step's setters, setVerifyEveryBid(step, true))
Step already started, disabled, or without a proof or permit gate?
Revert the whole batch: StepAlreadyStarted or NoGateConfigured
The step verifies every bid
Cleared by setVerifyEveryBid(step, false) at any time, or by disableStep / disableStepBatch
Owner call Effect setCCA(cca)Pairs once and caches the CCA schedule. Later calls are no-ops. enableStep / enableStepBatchTurns enforcement on and sets the step's providers, which are its proof gate. An empty list means no proof gate. disableStep / disableStepBatchTurns enforcement off and clears the step's verify-every-bid flag. setStepProviders / setStepProvidersBatchReplaces a step's providers. addStepProviders / removeStepProvidersAdds or removes single providers. Removing one that is absent reverts ProviderNotFound. enableStepPermit / disableStepPermitTurns a step's permit gate on or off. setSigner(signer)Sets the permit signer. address(0) disables every permit. setVerifyEveryBid(step, on)On only before the step starts, with the step enabled and gated. Off at any time. setStepMaxBidAmount(step, amount)Per-wallet zk cap at a step. 0 means no cap. setZkGlobalMaxBidAmount(amount)Auction-wide zk cap. 0 means no cap. Settable before pairing. setMaxBidPrice(q96)Bid price cap, which must sit on the CCA tick grid. 0 means no cap. unregister / unregisterBatchClears standing registration (see below). clearIdentity(providerHash, identityHash)Frees an identity slot. multicall(calls)Runs owner calls atomically. Leading with requireStepNotStarted(step) makes a late-mined batch revert.
Everything except setSigner, setZkGlobalMaxBidAmount, setCCA, clearIdentity and multicall reverts NoCCA before pairing.
no
yes
no
yes
yes
no
no
yes
no
yes
neither
yes
no
proof, permit or both
yes
no
yes
no
yes
no
Anyone calls CCA.submitBid(maxPrice, amount, owner, hookData) The CCA calls validate(maxPrice, amount, owner, sender, hookData) sender pays, credentials and caps bind to owner
Hook paired with a CCA?
msg.sender is the paired CCA?
maxBidPrice set and maxPrice above it?
Resolve the current step: the CCA's step, or a forward scan of the cached schedule when the CCA's step is stale at a boundary
Current block inside a step?
Step enabled?
Which gates does the step have? proof gate: providers set permit gate: permit enabled
Step verifies every bid?
Standing registration applies? step does not verify every bid, has a proof gate, and owner is registered at this or an earlier step
hookData empty?
Take the permit path? permit gate, and hookData starts 0x01 or the step has no proof gate
Revert CallerNotCCA
Revert MaxBidPriceExceeded
Revert NoGateConfigured
Revert ServerPermitRequired (permit gate only), ProofRequired (proof gate only) or NotVerified (both gates)
Enforce zk caps
Allow
Allow
Allow
Allow: public step
Allow
Permit path
Proof path
no
yes
yes
no
yes
no
no
yes
no
yes
Permit path hookData = 0x01 + abi.encode(permitStep, nonce, deadline, signature)
Payload well-formed? 0x01 prefix, canonical ABI head, signature within the payload
Current step or permitStep verifies every bid, and permitStep is not the current step?
permitStep after the current step?
permitStep has the permit gate, signer set, deadline not passed, nonce unused?
EIP-712 ServerPermit(owner, permitStep, nonce, deadline, amount) signed by the signer?
Burn the nonce, emit PermitConsumed
Revert ServerPermitRequired
Revert CredentialStepMismatch
Revert PermitStepTooHigh
Revert ServerPermitNotEnabled, SignerNotSet, ExpiredDeadline or PermitAlreadyUsed
Revert InvalidSignature
Allow, permit bids skip the zk caps
no
yes
yes
no
yes
no
yes
no
Proof path hookData = proofStep (32 bytes) + abi.encode(Reclaim.Proof)
Payload is a proof? not 0x01, at least 32 bytes
Current step or proofStep verifies every bid, and proofStep is not the current step?
proofStep after the current step?
Current step verifies every bid?
Consume a bid proof at the current step (see proof checks)
Register the owner from proofStep (see proof checks)
Enforce zk caps
Revert ProofRequired
Revert CredentialStepMismatch
Revert ProofStepTooHigh
Allow
Proof bids and bids covered by standing registration count toward both caps. Permit bids never do: the API enforces permit caps when it signs.
yes
no
yes
no
Enforce zk caps
Global cap set, and auction-wide total + amount above it?
Accrue the auction-wide total, even while uncapped, and emit ZkGlobalBidAccrued
Step cap set, and the owner's step total + amount above it?
Accrue the owner's step total (only while the step is capped)
Revert ZkBidExceedsGlobalCap
Revert ZkBidExceedsStepCap
A proof is either a registration proof, sent inline on an ordinary step, or a bid proof, sent inline on a step that verifies every bid. The signed context message tells them apart, so neither can stand in for the other.
Shared checks for both kinds
yes
no
yes
no
no
no
yes
no
yes
yes
yes, bid proof
yes, registration proof
no
yes
no
yes
yes
no
no
Bid proof: inline on a step that verifies every bid
Registration proof: inline on an ordinary step
Dated in the future?
Older than 10 minutes?
Signed context message is bid:CHAIN:AUCTION:STEP:AMOUNT:NONCE for this chain, auction, step and amount?
Signed context message is register:CHAIN:AUCTION:NONCE for this chain and auction?
Revert BidProofFromFuture
Revert BidProofExpired
Revert BidProofContextMismatch
Revert RegistrationProofContextMismatch
Bid proof already consumed?
Revert ProofAlreadyUsed
Mark it consumed, emit ProofVerified Never registers the wallet
Registered from the proof's step, emit Registered and ProofVerified Reusable: registration keeps no used-proof record
contextAddress is the owner?
The proof's step has providers, and the proof's providerHash is one of them?
Identity (provider + extractedParameters) claimed by another wallet?
Claim the identity for the owner on first use (skipped when the proof has no extractedParameters)
Reclaim witness signatures valid? Stateless, no shared replay registry is written
Revert ContextAddressMismatch
Revert ProviderNotFound or ProviderHashMismatch
Revert IdentityAlreadyClaimed
Revert from the Reclaim verifier
An ordinary proof-gated step registers the owner the first time their bid carries a valid registration proof
Later bids at this and later ordinary proof-gated steps need no credential
unregister(user) / unregisterBatch(users): owner, needs pairing
Standing registration cleared
The next bid needs a proof again. Any valid registration proof, including the earlier one, registers the wallet again
Not cleared: the identity slot, consumed bid proofs, burned permit nonces, zk bid totals
clearIdentity(providerHash, identityHash): owner
Frees the identity slot so another wallet can claim it
The Hub picks hookData from the step's gates. A step that verifies every bid gets a new permit while the wallet's allowance covers the bid, and a new bid proof otherwise. An ordinary step gets nothing once the wallet is registered onchain, then a stored registration proof, then a permit.
opt [Registration proof on an ordinary step]
alt [Server permit]
[zkTLS proof]
[Already registered, ordinary proof-gated step]
POST /api/v1/hub/server-permit/sign (auction, step hint, amount, walletAddress)
EIP-712 ServerPermit(wallet, step, nonce, deadline in 5 min, amount)
submitBid(maxPrice, amount, owner, 0x01 + permit)
POST /api/v1/hub/zktls/proof-request (provider, contextMessage, walletAddress)
Signed Reclaim request with contextAddress = wallet
Prove the account, contextMessage register:... or bid:...
Reclaim proof
POST /api/v1/hub/zktls/verify (stores registration proofs only)
submitBid(maxPrice, amount, owner, proofStep + proof)
submitBid(maxPrice, amount, owner, empty hookData)
validate(maxPrice, amount, owner, sender, hookData)
Returns, or reverts and the bid fails
walletAddress must be linked in Privy (a read at most 30 s old, outage returns 503), screening, server-resolved step, cap policy, live onchain gate check
walletAddress must be one of the caller's linked wallets
Wallet (Hub)
Umia API
Reclaim
CCA
UmiaValidationHook
View Answer authorizesWithoutCredential(step, user)False out of range. True on a disabled step. False on a step that verifies every bid. Otherwise isVerified(step, user) when the step has a proof gate, else true only when it has no permit gate. isVerified(step, user)Whether the user is registered at step or an earlier step. verifiesEveryBid(step)The step's verify-every-bid flag. isStepEnabled(step) / isStepPermitEnabled(step) / getStepProviders(step)The step's gates. expirationBlock()End block of the highest gated step, meaning enabled with providers, a permit gate or verify every bid. 0 when no step is gated. getSteps() / cca() / signer() / maxBidPrice()The cached schedule and settings. stepMaxBidAmount(step) / zkBidTotal(wallet, step) / zkGlobalMaxBidAmount() / zkGlobalBidTotal()zk caps and running totals. isPermitNonceUsed(nonce) / identityOwner(providerHash, identityHash)Burned permit nonces and identity slots. requireStepNotStarted(step)Reverts StepAlreadyStarted once the step has started. supportsInterface(id)CCA validation-hook introspection, IUmiaValidationHook, IMaxBidPriceValidationHook, IGatedValidationHook and IVerifyEveryBidHook.
PreviousValidation Hook Next Base MCP Plugin