Supply & Investor Limits
TokenSupplyLimit and InvestorCount modules — time-based and rolling supply caps with currency conversion, and unique holder limits with per-country granularity.
Purpose: Reference for supply and investor limit compliance modules.
- Doc type: Reference
- What you'll find here:
- TokenSupplyLimit — time-based and rolling supply caps with currency conversion
- InvestorCount — unique holder limits with per-country granularity
- Interface tables showing on-chain capabilities
- Key invariants and operational signals for both modules
- Related: Compliance Overview, Transfer Approval, Supply Cap & Collateral, Identity Verification, Asset Contracts
Where these modules apply
| Concern | TokenSupplyLimit | InvestorCount |
|---|---|---|
| Minting | Enforces cap (lifetime / fixed / rolling) | Tracks new holders |
| Transfers | — | Tracks new holders |
| Burns | — | Decrements count |
| Forced transfers | — | Tracks new holders |
TokenSupplyLimit
Enforces maximum token supply limits, with optional base-price conversion for EUR/USD-denominated caps.
Interface (capabilities)
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
|---|---|---|---|---|---|
setModuleParameters | Token admin (via compliance) | maxSupply, limitType, period, useBasePrice | Stores supply-limit config; validateParameters reverts if maxSupply = 0 | — | Calling with maxSupply = 0 reverts; the cap must be a positive value |
canTransfer (mint path) | Compliance engine | Sender, recipient, amount | Checks cumulative supply against limit (converts via price claim if useBasePrice) | — | Only enforced on mints (from == address(0)) |
Supply limit types
| Type | Behavior | Use case |
|---|---|---|
| LIFETIME | Total cap across the token's lifetime | MiCA EUR 8M asset-referenced token limit |
| FIXED_PERIOD | Cap resets at the start of each period | Quarterly fundraising caps |
| ROLLING_PERIOD | Sliding window of last N days | Continuous monitoring (rolling 12-month limits) |
Base currency conversion
When useBasePrice is enabled, the module converts token amounts using on-chain price claims before checking against the supply limit. This enables EUR- or USD-denominated caps (e.g., MiCA 8M EUR lifetime limit) on tokens priced in native units.
Key invariants
- Supply limit applies to minting operations only; transfers and burns are not checked
- ROLLING_PERIOD uses a sliding window — older mints fall off as time passes
- FIXED_PERIOD resets at the start of each new period, regardless of previous minting
- Calling
setModuleParameterswithmaxSupply = 0reverts; the cap must be a positive value
Operational signals
No events emitted by this module. Monitor for ComplianceCheckFailed revert errors in failed transactions when minting exceeds the configured limit.
Failure modes & edge cases
- Price feed stale or missing when
useBasePriceis enabled — minting reverts until a valid price claim exists - ROLLING_PERIOD window boundary — mints near the window edge may succeed or fail depending on when older mints age out
InvestorCount
Restricts the number of unique token holders. The topicFilter determines which investors are counted, not which are blocked.
Interface (capabilities)
| Capability | Who can call | Inputs | On-chain effect | Emits | Notes |
|---|---|---|---|---|---|
setModuleParameters | Token admin (via compliance) | globalLimit, countryLimits[], topicFilter, global flag | Stores investor-count config | — | topicFilter uses the same RPN expression system as identity verification |
canTransfer | Compliance engine | Sender, recipient, amount | Checks if adding recipient would exceed global or country limit | — | Skips burns (to == address(0)); applies to mints and transfers |
How it works
| Investor state | Identity module result | InvestorCount result | Transfer outcome |
|---|---|---|---|
| No KYC/AML claims | Blocked by identity module | N/A (never reaches count check) | Transfer blocked |
| Has qualifying claims, count < limit | Allowed | Counted | Transfer allowed |
| Has qualifying claims, count = limit | Allowed | Blocked (over limit) | Transfer blocked |
topicFilter and limits
The topicFilter is a claim expression (same RPN system as SMARTIdentityVerification) that determines which investors count toward the limit. InvestorCount can enforce a global limit across all investors and per-country limits for jurisdiction-specific restrictions (e.g., max 50 Singapore residents).
Key invariants
canTransferskips burns (to == address(0)) but applies to both mints and transfers- The global tracker accumulates regardless of the
globalflag setting — the flag controls enforcement, not tracking - Per-country limits are checked independently of the global limit; both must pass
topicFilterdetermines who is counted, not who is blocked — blocking is handled by identity verification
Operational signals
No events emitted by this module. Monitor for ComplianceCheckFailed revert errors in failed transactions when investor count exceeds limits.
Failure modes & edge cases
- Investor count may temporarily exceed the limit after forced transfers bypass the check
- Country code missing from identity registry — investor is not counted toward per-country limits but may still be counted globally
See also
- Compliance Overview — module architecture and regulatory templates
- Identity Verification —
topicFilteruses the same RPN expression system - Transfer Approval — pre-approval controls complement investor count limits
- Supply Cap & Collateral — CappedComplianceModule for hard circulating supply caps
Identity Verification
SMARTIdentityVerification module with full RPN expression system. Covers KYC/AML/ACCREDITED/CONTRACT claim topics, AND/OR/NOT operators, exemption support, and real-world expression examples.
Transfer Approval
TransferApproval module — pre-authorization workflow with expiry, one-time use, and identity-based exemptions for regulated transfer control.