BaseAllocationMechanism
Inherits: IBaseAllocationStrategy
Title: Base Allocation Mechanism
Author: Golem Foundation
Abstract base for allocation/voting mechanisms using lightweight proxy pattern
Follows Yearn V3 architecture: minimal proxy delegating to shared TokenizedAllocationMechanism
ARCHITECTURE PATTERN:
- Inheritors only implement custom hooks (no shared logic duplication)
- Shared logic lives in TokenizedAllocationMechanism (implementation)
- Each mechanism is a lightweight proxy with custom behavior via hooks
- Delegatecall pattern: proxy storage, implementation logic
HOOK SYSTEM:
13 abstract hooks to implement:
Registration:
- _beforeSignupHook: Allow/block user registration
- _getVotingPowerHook: Calculate voting power on signup
Proposing:
- _beforeProposeHook: Allow/block proposal creation
- _validateProposalHook: Validate proposal exists
Voting:
- _processVoteHook: Process vote and update voting power
- _hasQuorumHook: Check if proposal reached quorum
Distribution:
- _convertVotesToShares: Convert votes to vault shares
- _getRecipientAddressHook: Get recipient for proposal
- _requestCustomDistributionHook: Custom share distribution
Finalization:
- _beforeFinalizeVoteTallyHook: Pre-finalization checks
Withdrawal:
- _availableWithdrawLimit: Enforce timelock/grace period
Accounting:
- _calculateTotalAssetsHook: Total assets including matching pools
LIFECYCLE:
- Deploy: Constructor calls TokenizedAllocationMechanism.initialize()
- Registration: Users deposit assets and receive voting power
- Propose: Create proposals for funding allocation
- Vote: Users cast votes (Against/For/Abstain)
- Finalize: Tally votes, determine winning proposals
- Queue: Mint shares for successful proposals (after timelock)
- Redeem: Recipients redeem shares for assets (during grace period)
TIMELOCK & GRACE PERIOD:
- Timelock: Security delay before redemptions begin
- Grace Period: Window during which redemptions are allowed
- After grace period: Unredeemed shares become non-withdrawable
Notes:
-
security-contact: [email protected]
-
security: All hooks called via delegatecall from implementation
-
security: onlySelf modifier prevents direct external calls to hooks
Quick Start — What Matters Most
This is the proxy contract that mechanism authors inherit. Storage lives here; lifecycle logic is in the shared TokenizedAllocationMechanism.
Implement these five hook functions (the most critical ones):
_beforeSignupHook— Allow/block user registration_getVotingPowerHook— Calculate voting power per user_processVoteHook— Process votes and update voting power_convertVotesToShares— Convert votes into vault share amounts_getRecipientAddressHook— Determine who receives minted shares
State Variables
tokenizedAllocationAddress
Address of the shared TokenizedAllocationMechanism implementation
address internal immutable tokenizedAllocationAddress
asset
Underlying asset for the allocation mechanism
IERC20 internal immutable asset
Functions
constructor
Initializes the allocation mechanism with implementation and configuration
constructor(address _implementation, AllocationConfig memory _config) ;
Parameters
| Name | Type | Description |
|---|---|---|
_implementation | address | Address of the TokenizedAllocationMechanism implementation |
_config | AllocationConfig | Configuration parameters for the allocation mechanism |
_beforeSignupHook
REQUIRED: Determines if a user can register
Called before signup to implement access control IMPLEMENTATION GUIDANCE:
- Return true to allow registration
- Return false to block (will revert signup)
- Can check allowlists, blocklists, KYC status, etc. COMMON PATTERNS:
- Open: Always return true (permissionless)
- Allowlist: Check mapping(address => bool) allowedUsers
- Minimum deposit: Check deposit >= minimumAmount
function _beforeSignupHook(address user) internal virtual returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
user | address | Address attempting to register |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | allow True to allow registration, false to block |
_beforeProposeHook
REQUIRED: Determines if an address can create proposals
Called before propose to implement proposer restrictions IMPLEMENTATION GUIDANCE:
- Return true to allow proposal creation
- Return false to block (will revert)
- Can check minimum voting power, specific roles, etc. COMMON PATTERNS:
- Open: Always return true
- Minimum power: Check votingPower >= threshold
- Registered only: Check user has deposited
function _beforeProposeHook(address proposer) internal view virtual returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
proposer | address | Address attempting to create proposal |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | allow True to allow proposal, false to block |
_getVotingPowerHook
REQUIRED: Calculates voting power assigned on registration
Called during signup to determine user's initial voting power IMPLEMENTATION GUIDANCE:
- Must return value > 0 for successful registration
- Should be deterministic based on deposit amount
- Consider manipulation resistance (flash loan protection) COMMON PATTERNS:
- Linear: power = deposit (1 token = 1 vote)
- Quadratic: power = sqrt(deposit) (reduces whale influence)
- Capped: power = min(deposit, maxPower)
- Time-weighted: power = f(deposit, stakeDuration) SECURITY CONSIDERATIONS:
- Prevent flash loan manipulation (use checkpoints/locks)
- Validate deposit amount > 0
- Ensure calculation cannot overflow
function _getVotingPowerHook(address user, uint256 deposit) internal view virtual returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
user | address | Address registering (can be used for user-specific logic) |
deposit | uint256 | Amount of underlying tokens deposited (in asset base units) |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | power Voting power to assign (0 = registration fails) |
_validateProposalHook
REQUIRED: Validates that a proposal ID exists
Called to verify proposal exists before operations
function _validateProposalHook(uint256 pid) internal view virtual returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
pid | uint256 | Proposal ID to validate |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | valid True if proposal exists and is valid |
_processVoteHook
REQUIRED: Processes a vote and updates voting power
Called when user casts a vote. Must update vote tallies and return updated voting power IMPLEMENTATION GUIDANCE:
- Record vote in proposal's tally
- Deduct used voting power from voter's available power
- Return new voting power (must be
<= oldPower) - Prevent double voting or power overflow VOTING MODELS:
- One-time: Use full power once (newPower = 0)
- Proportional: Deduct weight from power (newPower = oldPower - weight)
- Quadratic: Deduct weight squared (cost increases with usage)
function _processVoteHook(
uint256 pid,
address voter,
TokenizedAllocationMechanism.VoteType choice,
uint256 weight,
uint256 oldPower
) internal virtual returns (uint256 newPower);
Parameters
| Name | Type | Description |
|---|---|---|
pid | uint256 | Proposal ID being voted on |
voter | address | Address casting the vote |
choice | TokenizedAllocationMechanism.VoteType | Vote type (Against=0, For=1, Abstain=2) |
weight | uint256 | Amount of voting power to use |
oldPower | uint256 | Voter's current voting power before this vote |
Returns
| Name | Type | Description |
|---|---|---|
newPower | uint256 | Voter's voting power after this vote (must be <= oldPower) |
_hasQuorumHook
REQUIRED: Checks if proposal reached quorum
Called to determine if proposal has enough votes to pass
function _hasQuorumHook(uint256 pid) internal view virtual returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
pid | uint256 | Proposal ID |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | hasQuorum True if proposal has sufficient votes |
_convertVotesToShares
REQUIRED: Converts votes to vault shares for winning proposal
Called when queuing successful proposal. Determines funding allocation COMMON PATTERNS:
- Direct: shares = votes (1 vote = 1 asset)
- Quadratic: shares = votes^2 (quadratic funding)
- Matched: shares = votes + matching(votes)
function _convertVotesToShares(uint256 pid) internal view virtual returns (uint256 sharesToMint);
Parameters
| Name | Type | Description |
|---|---|---|
pid | uint256 | Proposal ID being queued |
Returns
| Name | Type | Description |
|---|---|---|
sharesToMint | uint256 | Vault shares to allocate (in share base units) |
_beforeFinalizeVoteTallyHook
REQUIRED: Pre-finalization validation hook
Called before finalizeVoteTally to enforce any custom rules
function _beforeFinalizeVoteTallyHook() internal virtual returns (bool);
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | allow True to proceed with finalization |
_getRecipientAddressHook
REQUIRED: Returns recipient address for a proposal
Called during redemption to determine where shares go
function _getRecipientAddressHook(uint256 pid) internal view virtual returns (address recipient);
Parameters
| Name | Type | Description |
|---|---|---|
pid | uint256 | Proposal ID |
Returns
| Name | Type | Description |
|---|---|---|
recipient | address | Address to receive the minted shares |
_requestCustomDistributionHook
OPTIONAL: Custom distribution instead of standard share minting
Called during queue. Return (true, amount) to skip default minting USE CASES:
- Direct asset transfer instead of shares
- Multi-recipient distribution
- Custom vesting schedules DEFAULT BEHAVIOR: Return (false, 0) to use standard share minting
function _requestCustomDistributionHook(address recipient, uint256 sharesToMint)
internal
virtual
returns (bool handled, uint256 assetsTransferred);
Parameters
| Name | Type | Description |
|---|---|---|
recipient | address | Address of the recipient |
sharesToMint | uint256 | Number of shares to distribute |
Returns
| Name | Type | Description |
|---|---|---|
handled | bool | True if custom distribution performed (skips default minting) |
assetsTransferred | uint256 | Amount of assets transferred (for totalAssets accounting) |
_availableWithdrawLimit
Hook to get the available withdraw limit for a share owner
Default implementation enforces timelock and grace period boundaries
Can be overridden for custom withdrawal limit logic
function _availableWithdrawLimit(
address /* shareOwner */
)
internal
view
virtual
returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | limit Available withdraw limit (type(uint256).max for unlimited, 0 for blocked) |
_calculateTotalAssetsHook
Hook to calculate total assets including any matching pools or custom logic
function _calculateTotalAssetsHook() internal view virtual returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | totalAssets Total assets for this allocation mechanism |
onlySelf
Ensures function can only be called via delegatecall from TokenizedAllocationMechanism
In delegatecall context, msg.sender is the proxy address (address(this)), not the caller
modifier onlySelf() ;
beforeSignupHook
External wrapper for _beforeSignupHook, called by TokenizedAllocationMechanism via delegatecall
function beforeSignupHook(address user) external onlySelf returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
user | address | Address attempting to register |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | allow True to allow registration, false to block |
beforeProposeHook
External wrapper for _beforeProposeHook, called by TokenizedAllocationMechanism via delegatecall
function beforeProposeHook(address proposer) external view onlySelf returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
proposer | address | Address attempting to create proposal |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | allow True to allow proposal, false to block |
getVotingPowerHook
External wrapper for _getVotingPowerHook, called by TokenizedAllocationMechanism via delegatecall
function getVotingPowerHook(address user, uint256 deposit) external view onlySelf returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
user | address | Address registering |
deposit | uint256 | Amount deposited in asset base units |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | power Voting power to assign |
validateProposalHook
External wrapper for _validateProposalHook, called by TokenizedAllocationMechanism via delegatecall
function validateProposalHook(uint256 pid) external view onlySelf returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
pid | uint256 | Proposal ID to validate |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | valid True if proposal exists and is valid |
processVoteHook
External wrapper for _processVoteHook, called by TokenizedAllocationMechanism via delegatecall
function processVoteHook(uint256 pid, address voter, uint8 choice, uint256 weight, uint256 oldPower)
external
onlySelf
returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
pid | uint256 | Proposal ID being voted on |
voter | address | Address casting the vote |
choice | uint8 | Vote type (0=Against, 1=For, 2=Abstain) |
weight | uint256 | Amount of voting power to use |
oldPower | uint256 | Voter's current voting power |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | newPower Voter's voting power after this vote |
hasQuorumHook
External wrapper for _hasQuorumHook, called by TokenizedAllocationMechanism via delegatecall
function hasQuorumHook(uint256 pid) external view onlySelf returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
pid | uint256 | Proposal ID |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | hasQuorum True if proposal has sufficient votes |
convertVotesToShares
External wrapper for _convertVotesToShares, called by TokenizedAllocationMechanism via delegatecall
function convertVotesToShares(uint256 pid) external view onlySelf returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
pid | uint256 | Proposal ID being queued |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | sharesToMint Vault shares to allocate in share base units |
beforeFinalizeVoteTallyHook
External wrapper for _beforeFinalizeVoteTallyHook, called by TokenizedAllocationMechanism via delegatecall
function beforeFinalizeVoteTallyHook() external onlySelf returns (bool);
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | allow True to proceed with finalization |
getRecipientAddressHook
External wrapper for _getRecipientAddressHook, called by TokenizedAllocationMechanism via delegatecall
function getRecipientAddressHook(uint256 pid) external view onlySelf returns (address);
Parameters
| Name | Type | Description |
|---|---|---|
pid | uint256 | Proposal ID |
Returns
| Name | Type | Description |
|---|---|---|
<none> | address | recipient Address to receive the minted shares |
requestCustomDistributionHook
External wrapper for _requestCustomDistributionHook, called by TokenizedAllocationMechanism via delegatecall
function requestCustomDistributionHook(address recipient, uint256 sharesToMint)
external
onlySelf
returns (bool handled, uint256 assetsTransferred);
Parameters
| Name | Type | Description |
|---|---|---|
recipient | address | Address of the recipient |
sharesToMint | uint256 | Number of shares to distribute |
Returns
| Name | Type | Description |
|---|---|---|
handled | bool | True if custom distribution performed |
assetsTransferred | uint256 | Amount of assets transferred |
availableWithdrawLimit
External wrapper for _availableWithdrawLimit, called by TokenizedAllocationMechanism via delegatecall
function availableWithdrawLimit(address shareOwner) external view onlySelf returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
shareOwner | address | Address to check withdraw limit for |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | limit Available withdraw limit (type(uint256).max for unlimited, 0 for blocked) |
calculateTotalAssetsHook
External wrapper for _calculateTotalAssetsHook, called by TokenizedAllocationMechanism via delegatecall
function calculateTotalAssetsHook() external view onlySelf returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | totalAssets Total assets for this allocation mechanism |
_tokenizedAllocation
Access TokenizedAllocationMechanism interface for internal calls
Uses current contract address since storage is local
function _tokenizedAllocation() internal view returns (TokenizedAllocationMechanism);
_getGracePeriod
Get grace period from configuration
function _getGracePeriod() internal view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Grace period in seconds |
_getGlobalRedemptionStart
Get global redemption start timestamp
function _getGlobalRedemptionStart() internal view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Timestamp when redemption window opens (0 if not finalized) |
fallback
Delegates all undefined function calls to TokenizedAllocationMechanism
This enables the proxy pattern where shared logic lives in the implementation
fallback() external payable virtual;
receive
Receive function to accept ETH
receive() external payable virtual;
_getProposalCount
Get the current proposal count
Helper for concrete implementations to access storage
function _getProposalCount() internal view returns (uint256);
_proposalExists
Check if a proposal exists
Helper for concrete implementations
function _proposalExists(uint256 pid) internal view returns (bool);
_getProposal
Get proposal details
Helper for concrete implementations
function _getProposal(uint256 pid) internal view returns (TokenizedAllocationMechanism.Proposal memory);
_getVotingPower
Get voting power for an address
Helper for concrete implementations
function _getVotingPower(address user) internal view returns (uint256);
_getQuorumShares
Get quorum shares requirement
Helper for concrete implementations
function _getQuorumShares() internal view returns (uint256);
Events
AllocationMechanismInitialized
Emitted when the allocation mechanism is initialized
event AllocationMechanismInitialized(
address indexed implementation, address indexed asset, string name, string symbol
);