BoardConfig
struct BoardConfig {
string version;
address owner;
address underlyingToken;
uint256 opensAt;
uint256 closesAt;
Metadata boardMetadata;
AcceptanceCriteria acceptanceCriteria;
IAuthorizer.ParticipantRequirements proposerRequirements;
IAuthorizer.ParticipantRequirements supporterRequirements;
LockingConfig lockingConfig;
DecayConfig decayConfig;
}version
Off-chain compatibility marker. Current version: "0.3.2".
owner
Board controller. Can accept initiatives, close/cancel the board, and adjust timing parameters. Use a multisig or DAO governor contract for decentralized operation.
underlyingToken
ERC20 token used for locking and weight calculation. Cannot be address(0). This is the token supporters lock when backing initiatives.
opensAt
Timestamp when the board starts accepting proposals and support. Set to block.timestamp to open immediately, or a future timestamp to schedule.
closesAt
Timestamp when the board stops accepting new activity. Must be ≥ opensAt. Set to 0 for a board that never closes.
boardMetadata
struct Metadata {
string title;
string body;
Attachment[] attachments;
}
struct Attachment {
string uri;
string mimeType;
string description;
}Title, body (markdown), and up to 5 attachments. Emitted in the BoardCreated event, not stored on-chain. uri must be non-empty (https://, ipfs://, or on-chain pointer). mimeType and description are optional hints for clients.
acceptanceCriteria
struct AcceptanceCriteria {
AcceptancePermissions permissions;
ThresholdOverride thresholdOverride;
uint256 thresholdPercentTotalSupplyWAD;
uint256 minThreshold;
}Threshold
The effective threshold is the greater of two values:
threshold = max(totalSupply * thresholdPercentTotalSupplyWAD / 1e18, minThreshold)thresholdPercentTotalSupplyWAD — Percentage of total token supply in WAD notation. 0.05e18 = 5%. Must be < 1e18.
minThreshold — Fixed minimum weight, used as a floor. Prevents dust attacks when total supply is low.
At least one must be non-zero.
Permissions
permissions and thresholdOverride control who can call acceptInitiative() and whether the threshold check applies:
permissions | thresholdOverride | Who can accept | Threshold required |
|---|---|---|---|
Permissionless | None | Anyone | Yes, for everyone |
Permissionless | OnlyOwner | Anyone | Yes, but owner bypasses |
OnlyOwner | None | Owner only | Yes |
OnlyOwner | OnlyOwner | Owner only | No (owner bypasses) |
proposerRequirements
struct ParticipantRequirements {
address token;
uint256 minBalance;
uint256 minHoldingDuration;
uint256 minLockAmount;
}Controls who can call proposeInitiative() and proposeInitiativeWithLock(). Immutable after deployment.
Fields
token — ERC20 checked for eligibility. Can differ from underlyingToken. Cannot be address(0).
minBalance — Minimum current balance required. When non-zero, checks IERC20(token).balanceOf(caller) >= minBalance.
minHoldingDuration — Minimum blocks the caller must have held minBalance. When non-zero, calls IVotes(token).getPastVotes(caller, block.number - minHoldingDuration). Requires minBalance > 0 and an ERC20Votes token.
minLockAmount — Minimum tokens to lock when proposing with lock. proposeInitiative() (without lock) reverts if minLockAmount > 0. Must be ≤ minBalance.
Eligibility modes
The mode is inferred from field values. There is no explicit enum.
minBalance | minHoldingDuration | Mode | Token type |
|---|---|---|---|
0 | 0 | Open access, anyone can participate | Any ERC20 |
> 0 | 0 | Current balance check | Any ERC20 |
> 0 | > 0 | Historical balance check (snapshot) | ERC20Votes required |
For the historical balance mode, users must delegate their tokens (even to themselves) to activate checkpoints. Without delegation,
getPastVotesreturns zero.
Block time reference for minHoldingDuration
| Chain | Block time | ~7 days | ~30 days |
|---|---|---|---|
| Ethereum | 12s | 50,400 | 216,000 |
| Base | 2s | 302,400 | 1,296,000 |
| Arbitrum | ~0.25s | 2,419,200 | 10,368,000 |
supporterRequirements
Same IAuthorizer.ParticipantRequirements struct as above. Controls who can call supportInitiative(). Immutable after deployment.
Configured independently. A board can have strict proposer requirements (high balance, holding duration) with open supporter requirements (anyone can lock tokens).
lockingConfig
struct LockingConfig {
uint256 lockInterval;
uint256 maxLockIntervals;
uint256 releaseLockDuration;
uint256 inactivityTimeout;
}lockInterval
Base time unit in seconds. Lock durations and decay calculations are measured in multiples of this value. Must be > 0.
maxLockIntervals
Maximum number of intervals a lock can span. Must be > 0. The maximum lock duration in seconds is lockInterval × maxLockIntervals.
Example: lockInterval = 1 days and maxLockIntervals = 365 allows locks up to 1 year.
releaseLockDuration
Additional delay in seconds after an initiative is accepted before supporters can redeem their tokens. Set to 0 for immediate release on acceptance.
This is separate from lock duration. A supporter's lock may have expired, but if the initiative was just accepted, they still wait releaseLockDuration before redeeming.
On a time-boxed board, setting
releaseLockDuration≥ the board duration prevents token recycling. Supporters cannot back one initiative, wait for acceptance, reclaim, and redirect to another before the board closes.
inactivityTimeout
Seconds of inactivity before the owner can expire an initiative via expireInitiative(). An initiative's lastActivity timestamp updates on creation and each time a supporter locks tokens.
expirable when: block.timestamp > lastActivity + inactivityTimeoutdecayConfig
struct DecayConfig {
DecayCurveType curveType;
uint256[] params;
}curveType — 0 = Linear, 1 = Exponential.
params — Curve-specific parameters:
- Linear (
0):params = [decayRate]. Rate of1e18= 1:1 decay per interval. - Exponential (
1):params = [decayMultiplier]. Multiplier of0.9e18= 10% reduction per interval.
