TAM: Architecture
Purpose: Explain how TAM is structured at the contract level—the proxy/implementation split, storage layout, hook architecture, roles, system flow, and key invariants.
Audience: Smart contract developers building mechanisms on TAM; assumes understanding of TAM's conceptual model and Yearn's tokenized-strategy pattern (or willingness to learn it here).
Level: Advanced
Source of truth: TAM implementation in
[email protected]; deployment addresses, where needed, come from the Deployed Addresses page.Use this page when: You are implementing a concrete mechanism and need to understand how BaseAllocationMechanism and TokenizedAllocationMechanism interact, how hooks fit into the flow, what the system state machine looks like, or what invariants to maintain.
Do not use this page for: Conceptual understanding of TAM without architecture context (start with Introduction to TAM); step-by-step lifecycle walkthroughs (see TAM: Mental Model & Lifecycle); or hook implementation patterns for specific use cases.
- Required: Introduction to TAM
- Helpful: DeFi Concepts Primer (especially delegatecall)
This page explains how Tokenized Allocation Mechanisms (TAMs) are structured in [email protected].
The core design uses a delegatecall-based proxy pattern:
- a thin mechanism instance holds storage,
- a shared implementation runs the lifecycle and accounting logic,
- hook wrappers on the proxy are protected by
onlySelf, so custom policy code can only be reached from inside the implementation flow.
That gives you a small mechanism-specific surface area without reimplementing proposal lifecycle, accounting, signatures, and redemption rules from scratch.
Yearn-style pattern, adapted for allocation
If you already know the Yearn V3 tokenized-strategy pattern, the mental model is similar:
- storage lives in the instance, not the implementation,
- shared logic lives in one implementation contract, and
- policy enters through hooks, not by rewriting the lifecycle.
What changes is the domain.
Yearn-style tokenized strategies are built around yield lifecycle and reporting. TAMs are built around:
- registration,
- proposal creation,
- voting,
- tally finalization,
- proposal queueing,
- redemption or custom distribution.
The shared implementation enforces time windows, proposal states, accounting consistency, and signature flows. Individual mechanisms decide policy through hooks, for example:
- who may sign up,
- how voting power is calculated,
- how votes are processed,
- what quorum means,
- how votes map to shares or direct transfers.
What TAM keeps, and what it changes
Unchanged from the tokenized pattern
- delegatecall architecture,
- storage in the proxy,
- shared implementation for lifecycle and accounting,
- policy-specific extension through hook wrappers,
- role-gated admin and operator functions.
Changed for allocation mechanisms
Instead of a yield lifecycle, TAM uses a governance and distribution lifecycle:
- users sign up,
- proposals are created,
- votes are cast,
- tally is finalized,
- proposals are queued,
- recipients redeem minted shares or receive custom distributions.
This is still a tokenized pattern, but the token now represents allocation outcome, not yield-bearing exposure.
Why mechanisms stay small
BaseAllocationMechanism is intentionally thin. It exists to:
- hold mechanism storage,
- expose hook wrappers protected by
onlySelf, - initialize the shared implementation,
- forward unknown selectors to that implementation through fallback.
TokenizedAllocationMechanism then provides the heavy lifting:
- proposal and voting lifecycle,
- ERC-20-compatible share accounting,
- transfer restrictions,
- EIP-712 signature flows,
- queueing and redemption rules,
- pause state,
- owner-managed post-redemption recovery via
sweep(...).
This split matters because it keeps policy code focused on mechanism rules rather than on lifecycle plumbing.
Main building blocks
BaseAllocationMechanism.sol
The proxy / delegator layer.
It:
- stores state in the deployed mechanism instance,
- defines the hook wrappers guarded by
onlySelf, - initializes the implementation by delegatecall,
- forwards normal user and operator calls to the shared implementation.
The important architectural point is not the exact number of wrappers, but the pattern: hooks are only callable from the implementation path, never directly by users.
TokenizedAllocationMechanism.sol
The shared implementation.
It handles:
- signup and proposal creation,
- vote casting and finalization,
- permissionless queueing after tally finalization,
- ERC-20 share minting and redemption,
- global redemption window logic,
- transfer restrictions outside the redemption window,
- pause/unpause,
- owner-only
sweep(...)after the grace period.
This contract is much more than a token ledger. It is the state machine and accounting core for TAM.
Mechanism-specific policy contracts
A concrete mechanism supplies the policy layer by implementing hooks.
One important example in the repo is QuadraticVotingMechanism.sol, which adds:
- quadratic vote cost,
- role-based proposing,
- quorum logic,
- vote-to-shares conversion specific to QF,
- the restriction that only
VoteType.Foris accepted.
That last point is a mechanism-level policy choice, not a TAM-wide rule. The core API supports Against, For, and Abstain; QVM narrows that set deliberately.
ProperQF.sol
A math helper used by the Quadratic Funding implementation.
It supports:
- incremental QF accounting,
- α-weighted funding math,
- budget-fitting helper logic,
- bounded rounding behavior for QF distribution.
It is important for the QF family, but it should be understood as a mechanism-specific utility, not a core requirement of TAM as a whole.
Roles and authority
At the shared TAM layer, the core roles are:
- owner — controls sensitive lifecycle actions such as tally finalization, pause / unpause, and
sweep(...), - management — mechanism-level administrative authority where the mechanism chooses to use it,
- keeper — optional operator role for mechanism-specific flows such as proposal creation in QVM.
This is different from strategy docs that use a separate emergency-admin role. In TAM, the checked core authority is centered on owner, management, keeper, and paused state.
System flow (happy path)
1. Signup
A participant signs up, optionally depositing assets if the mechanism uses deposits to derive voting power.
The implementation calls the relevant signup hooks through the proxy.
2. Proposal creation
A proposal is created with a recipient and metadata.
Who may propose is mechanism-specific. For example, QVM restricts this to keeper or management.
3. Voting
Participants cast votes.
The implementation validates proposal state, checks voter eligibility, and routes the mechanism-specific vote logic through hooks.
4. Tally finalization
After the voting window closes, the owner finalizes the tally.
This also sets the global redemption timestamps used later by the redemption phase.
5. Permissionless queueing
After tally finalization, anyone may call queueProposal(pid) as long as the redemption window has not yet started.
Queueing is where a successful proposal is turned into an allocative result.
The shared implementation:
- checks proposal state,
- checks quorum through hooks,
- converts votes to shares through hooks,
- either mints shares to the recipient,
- or accepts a custom distribution handled by a hook, while adjusting accounting by the exact
assetsTransferredreturned from that hook.
That last detail matters: permissionless queueing is safe only if accounting stays exact.
6. Redemption
Redemption uses a global redemption window, not a separate per-proposal one.
During that window:
- recipients can redeem shares,
- transfers are permitted,
- normal ERC-20-like movement is temporarily available.
Outside that window, the withdrawal limit returns 0, so shares are effectively non-redeemable.
7. Post-grace recovery
After the grace period expires, the shared implementation allows the owner to call sweep(...).
This is not a general runtime redistribution primitive. It is a specific post-redemption recovery path guarded by time conditions and ownership.
Core invariants to keep in mind
Windows are strict
Voting, tally finalization, queueing, redemption, and post-redemption recovery all depend on explicit timestamps and state checks.
Queueing is permissionless, not policy-free
Core TAM intentionally allows permissionless queueing after finalization. If your governance needs extra controls, enforce them inside your mechanism hooks, especially custom distribution logic.
Hook-driven distribution must keep accounting exact
If a hook transfers assets directly instead of using default share minting, it must report the exact assetsTransferred value so totalAssets remains correct.
Transferability is window-gated
Mechanism shares are not freely transferable all the time. The shared implementation allows transfers only during the global redemption window.
Core TAM is broader than QF
Quadratic Funding is one important mechanism family in the repo, but TAM itself is a more general framework. The architecture should be read as:
- shared lifecycle and accounting core,
- policy injected through hooks,
- QF as one concrete specialization, not the definition of TAM.
QF-specific notes
When reading QuadraticVotingMechanism and ProperQF, keep these details local to the QF implementation:
- quadratic vote cost,
VoteType.Foronly,- α-weighted allocation,
- QF rounding bounds,
- reverted
receive()to avoid stuck ETH.
Those are important design constraints for QF, but they are not universal invariants of every TAM.