TAM: Architecture
Context. This section describes how the Tokenized Allocation mechanism is engineered using a Yearn‑style tokenized pattern: a lightweight proxy (the “mechanism”) that delegatecalls a shared implementation for lifecycle and accounting, while policy is injected via onlySelf‑gated hooks. The outcome is a small, auditable surface per mechanism and a single, rigorously tested core. Where the spec and code add nuance—such as permissionless queuing, window‑gated redemption, and other voting semantics.
If you have prior exposure to tokenized strategies, this will feel familiar: storage in the instance, logic in the shared implementation, hooks for policy. The same reasons this works well for yield apply here for governance—small deltas, strong invariants, and fewer places to make dangerous mistakes.
Yearn V3 in one paragraph (recap)
Context. The Yearn pattern centralizes shared logic in one implementation and keeps instance‑specific contracts thin. TAM adopts this exactly: the proxy holds storage, and the shared implementation runs logic against that storage via delegatecall. Hooks provide the extension points for policy without widening the audited core.
Why it matters. Centralizing lifecycle and accounting minimizes audit surface, reduces deployment gas, and enforces uniform invariants (windows, states, signatures) across mechanisms that otherwise differ in policy.
What TAM changes (and what it doesn’t)
Context. We preserve the delegatecall architecture, storage‑in‑proxy, and role gates. What changes is the domain: from “harvest/report” (yield) to register → propose → cast → finalize → queue → redeem (governance). The core enforces time windows and state transitions, and policy hooks (e.g., quorum, vote‑to‑shares mapping) decide how a proposal succeeds and how much it receives.
Important nuance. At API level the core supports VoteType {Against, For, Abstain}, but the QuadraticVotingMechanism (QV) hook admits only For and reverts otherwise; this is deliberate for QF semantics and is where you would re‑enable multi‑choice if you need it.
Why mechanisms are small (immutable‑proxy pattern)
Context. The proxy (BaseAllocationMechanism) exposes external wrappers for internal hooks and guards them with onlySelf so they are callable only when the implementation delegatecalls the proxy. All other unknown selectors fall through an assembly fallback to the shared implementation. This preserves a hard trust boundary: userland cannot poke your hooks directly.
Key properties.
- Storage locality: All state (proposals, power, shares, windows) lives in the proxy.
- Logic reuse: One implementation (TokenizedAllocationMechanism) for registration, proposals, voting, queuing, redemption, EIP‑712 signatures, pausing, sweeping.
- Deterministic access control: Roles (owner, management, keeper, emergency) are resolved through delegatecall so the implementation reads the proxy’s storage consistently.
Components & definitions
Context. These contracts are the building blocks you will touch. Understanding the split of responsibilities prevents accidental duplication and inconsistent behavior.
- TokenizedAllocationMechanism.sol (implementation). Runs the lifecycle and maintains the ERC‑20‑style share ledger with transfer restrictions (locked until redemption starts). Hosts signature flows (signupWithSignature,castVoteWithSignature) and enforces global windows.
- BaseAllocationMechanism.sol (proxy). Holds storage, defines 11+ hook wrappers protected by onlySelf, and forwards everything else to the implementation via fallback.
- QuadraticVotingMechanism.sol (mechanism base). Implements quadratic cost (W votes cost W² power), α‑weighted QF mapping, role‑based proposing (keeper/management), single‑vote rule, and decimal normalization to 18 decimals.
- ProperQF.sol (math utilities). Supplies incremental QF accounting and the optimal‑α formula to fit a 1:1 shares‑to‑assets budget, with clamps for edge cases.
System data‑flow (happy path)
Context. The diagram fixes two common mistakes: (1) finalization is owner → proxy → implementation, and (2) queuing is permissionless, with accounting corrected when a custom distribution transfers assets directly.
What to notice.
- queueProposalis permissionless by core design; if your governance requires a gate, embed it in the distribution hook and still report- assetsTransferredto keep- totalAssetsright.
- Share transfers (not redemptions) are allowed only during the global redemption window; this is enforced in _transfer.
Design invariants to internalize - QF implementation
Context. These are correctness anchors. Tests should prove them; code should make them hard to violate.
- Windows are law. Voting, finalization, timelock, redemption, and sweep windows are enforced; availableWithdrawLimitmust return 0 outside redemption, enablingsweeponly after grace expires.
- One vote / quadratic cost. Each voter has at most one vote per proposal; the hook must ensure newPower ≤ oldPowerand charge W². QVM reverts unlesschoice == For.
- Recipient uniqueness. Each recipient address is used at most once across proposals in the round.
- Permissionless queueing with exact accounting. If the distribution hook transfers assets directly, the implementation must reduce totalAssetsby the reported amount to keep shares‑to‑assets accounting consistent.
- ETH safety. QVM’s receive()reverts; assets must flow as ERC‑20 to avoid stuck ETH.
- Optimal‑α math is sound and bounded. The α formula fits budget, clamps to 1 when appropriate, and the rounding discrepancy is bounded by ≤ 2·(|P|−1) wei per project, preventing over‑allocation.