TAM: Mental Model & Lifecycle
Purpose: Walk through TAM's complete lifecycle phase by phase, define the mental model of shared lifecycle vs. mechanism policy, describe the hook surface for customization, clarify what is general TAM behavior vs. what is QF-specific, and list safety properties to test.
Audience: Developers building mechanisms or tests on TAM; researchers designing voting and distribution rules; anyone needing a clear step-by-step view of how a TAM round flows from start to finish.
Level: Intermediate
Source of truth: TAM design in
[email protected]; deployment addresses, where needed, come from the Deployed Addresses page.Use this page when: You need to understand the complete round flow, see where your mechanism policy hooks in at each phase, clarify which behaviors are TAM-universal vs. QF-specific, or identify invariants and safety properties to test.
Do not use this page for: Implementation details of specific contracts (see TAM: Architecture); conceptual introduction without lifecycle context (see Introduction to TAM); or quadratic funding math and utilities (see the QF reference docs).
- Required: Introduction to TAM
- Helpful: TAM Architecture
Think of the system as two clean layers:
- a shared core that owns lifecycle, accounting, time windows, proposal states, signatures, and redemption rules,
- a mechanism that supplies policy through onlySelf-gated hooks.
The core decides when things may happen and preserves accounting invariants. Your mechanism decides who may participate, how voting power is computed or spent, what success means, and how successful proposals turn into funded outcomes.
That separation is the main mental model to keep in mind: shared state machine, mechanism-specific policy.
Lifecycle Summary
A round progresses through a strict sequence. The shared implementation enforces the high-level timing and proposal-state transitions; your mechanism hooks define the policy at each step.
At the core level, three operational facts matter most:
- finalization is owner-driven,
- queueing is permissionless after finalization,
- redemption is global-window-gated, not proposal-local.
Core round lifecycle
A TAM progresses through distinct phases from participant signup through final redemption. The following diagram illustrates the complete lifecycle:
1. Signup / registration
Participants call signup(...) or a signature-based variant.
The core:
- checks whether signup is currently allowed,
- moves assets if the mechanism requires deposits,
- calls the relevant hooks to validate participation and compute voting power,
- records the resulting power in mechanism state.
Whether repeated signups are meaningful, whether deposits are required, and how power is computed are mechanism choices, not universal TAM rules.
2. Proposal creation
A proposer creates a proposal with a recipient and proposal metadata.
The core:
- validates basic structural requirements,
- creates the proposal record,
- calls proposal-gating hooks.
Who may propose is mechanism-specific. Some mechanisms may restrict this to owner, management, or keeper; others may allow broader participation.
3. Voting
Voters cast votes on proposals during the voting window.
The core:
- enforces the voting window,
- prevents invalid or duplicate vote paths according to shared state,
- calls the vote-processing hook to apply mechanism policy,
- stores the updated state.
This is the most policy-heavy step. Examples:
- a linear mechanism may subtract voting power 1:1,
- a quadratic mechanism may charge
W²for weightW, - another mechanism may support multiple choices or abstentions.
The shared core does not force one universal voting rule. That logic lives in hooks.
4. Finalization
After the voting window closes, the owner finalizes the round tally.
The core:
- locks in the tally state,
- records the mechanism's total assets through the accounting hook,
- schedules the global redemption window using the configured timelock and grace parameters.
This is a one-way transition. Once finalization succeeds, the system moves from voting into the queueing / redemption phase.
5. Queueing successful proposals
After finalization, anyone may call queueProposal(pid) for a proposal that satisfies the mechanism's success conditions.
The core:
- checks that finalization has happened,
- checks the proposal state,
- asks the mechanism whether the proposal satisfies quorum or success conditions,
- converts the proposal outcome into shares or distribution intent,
- either mints shares to the recipient or applies a custom distribution hook.
If a custom distribution hook directly transfers assets, the hook must return the exact assetsTransferred amount so the core can reduce totalAssets accordingly.
That accounting step is critical. Without it, the ERC-4626-style asset/share accounting would drift.
6. Redemption
Recipients redeem only during the global redemption window created at finalization.
The core enforces this via redemption gating and withdrawal limits. Outside the redemption window, redemptions are blocked.
This is a global mechanism-level window, not a separate window per proposal.
7. Post-redemption recovery
After the redemption window and grace period have fully expired, the owner may recover unclaimed funds through sweep(...).
This is not part of the normal user flow. It is an owner-only post-redemption recovery path used after the recipient redemption opportunity has ended.
Required hooks: your real policy surface
Hooks are your policy engine. They should be deterministic, minimal, and explicit about what state they read or update. Some hooks are naturally view; others are state-changing by design.
The important distinction is not “hooks are view-only,” but rather:
- hooks should do policy work,
- shared implementation should continue to own lifecycle and accounting.
Below is the practical hook surface to think about.
Eligibility and power
-
_beforeSignupHook(user)Decide whether a user may sign up. -
_getVotingPowerHook(user, deposit)Map signup inputs to voting power.
These hooks define who can participate and how much influence they start with.
Proposal policy
-
_beforeProposeHook(proposer)Gate who may create proposals. -
_validateProposalHook(pid)Enforce any proposal-specific invariants.
These hooks define proposal eligibility and proposal integrity.
Vote policy
_processVoteHook(pid, voter, choice, weight, oldPower)Apply the mechanism's voting rule and return the updated power state.
This is where mechanism behavior diverges the most.
Examples:
- in a quadratic mechanism, weight may cost
W², - in a linear mechanism, weight may cost
W, - in a richer design, choices beyond
Formay be enabled.
The core expects this hook to preserve accounting consistency and not create voting power out of thin air.
Success and funding conversion
-
_hasQuorumHook(pid)Decide whether a proposal is queueable. -
_convertVotesToShares(pid)Convert the result of the mechanism into a share amount.
This hook should be described generically in docs because the conversion rule is mechanism-specific.
For Quadratic Funding, one concrete implementation is an α-weighted mapping from quadratic and linear contributions. But that is an example of a TAM policy, not the definition of TAM itself.
Recipient and distribution
-
_getRecipientAddressHook(pid)Resolve the stable recipient for a proposal. -
_requestCustomDistributionHook(recipient, shares)Optionally handle distribution without minting shares.
If the custom hook transfers assets directly, it must return (true, assetsTransferred) with the exact amount transferred so the shared implementation can update totalAssets precisely.
Finalization policy
_beforeFinalizeVoteTallyHook()Pre-finalization policy (for example, snapshotting budgets). This hook is abstract — your mechanism must implement it — and the shared core calls it fromfinalizeVoteTally()before queued proposals convert votes into shares.
Redemption and accounting
-
_availableWithdrawLimit(owner)Gate when redemptions are available. -
_calculateTotalAssetsHook()Report the mechanism's total assets for finalization-time accounting.
These hooks connect lifecycle timing to ERC-4626-style accounting.
What is general TAM behavior vs what is QF-specific?
This is the most important distinction to keep straight.
General TAM behavior
The following are general properties of the shared TAM core:
- proxy storage + shared implementation via delegatecall,
onlySelf-gated hook wrappers,- owner-driven finalization,
- permissionless queueing after finalization,
- global redemption window,
- owner-only
sweep(...)after redemption and grace have expired, - ERC-20-style shares with transfer restrictions tied to lifecycle.
QF-specific behavior
The following belong to a particular Quadratic Funding / Quadratic Voting mechanism, not to TAM as a whole:
- charging
W²voting-power cost, - restricting
choicetoFor, - α-weighted vote-to-funding conversion,
ProperQFincremental state and rounding bounds,- any additional role model chosen by
QuadraticVotingMechanism.
Use those as examples of what a mechanism can do, not as the universal semantics of TAM.
Safety properties to test
These are the invariants worth asserting in tests.
1. Windows are law
Voting, finalization, queueing, redemption, and sweep must only occur in the states and windows allowed by the shared core.
2. Voting power accounting is conserved
Vote processing must never create voting power from nowhere. The hook may leave power unchanged or reduce it according to mechanism policy, but it should not increase spendable power incorrectly.
3. Accounting stays exact
If shares are minted, share supply and totalAssets must remain consistent.
If a custom distribution hook transfers assets directly, totalAssets must be reduced by exactly the reported assetsTransferred amount.
4. Transfer restrictions hold
Share transfers should remain blocked outside the redemption window and behave according to the shared core's transfer rules.
5. Post-redemption recovery is admin-only
sweep(...) should only succeed for the owner and only after the full redemption and grace period has passed.
Practical reading rule
When reading TAM code or docs, ask this question for every behavior:
Is this a shared lifecycle rule, or is it mechanism policy?
If it concerns:
- timing,
- proposal states,
- redemption windows,
- queueing permissionlessness,
- core accounting,
then it usually belongs to TokenizedAllocationMechanism.
If it concerns:
- eligibility,
- power computation,
- vote pricing,
- quorum,
- vote-to-shares mapping,
- custom distribution behavior,
then it usually belongs to the mechanism hooks implemented on top of that shared core.