-
Notifications
You must be signed in to change notification settings - Fork 163
Issuance Allocator (IA) (rebased) #1257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Cherry-picked from ec0c984 (issuance-baseline-2/3) Rebased onto main with regenerated lockfile
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Issuance Allocator (IA) (rebased)
🚨 Report Summary
For more details view the full report in OpenZeppelin Code Inspector |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1257 +/- ##
==========================================
+ Coverage 84.05% 86.41% +2.36%
==========================================
Files 42 45 +3
Lines 2070 2393 +323
Branches 615 714 +99
==========================================
+ Hits 1740 2068 +328
+ Misses 330 325 -5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
The default allocation receives issuance that is not explicitly allocated to another target. - Added setDefaultAllocationAddress() to set. - getTotalAllocation() excludes default when it is the zero address. - Tests and documentation updated.
Added two configurable reclaim addresses: - indexerEligibilityReclaimAddress - subgraphDeniedReclaimAddress When rewards are denied and the reclaim address is set (non-zero), tokens are minted to that address instead of not being minted at all. Defaults to address(0) (original behavior). Also updated associated documentation and tests.
- Add clarity to availablePPM calculation explaining it comprises default allocation's allocator-minting PPM, target's allocator-minting PPM, and target's self-minting PPM to maintain 100% allocation invariant - Refine reentrancy comment to explicitly reference that calculations occur after notifications to prevent reentrancy issues Addresses PR feedback from code review
…wing (#1268) Replace vm.assume with bounded inputs to fix "vm.assume rejected too many inputs" error. The previous implementation used overly restrictive constraints that caused the fuzzer to reject most random inputs. Now limits amountThawing and amountCollected to half of MAX_STAKING_TOKENS, guaranteeing valid deposit ranges while maintaining test coverage.
…address Change setDefaultAllocationAddress to distribute any pending issuance to the old default address before changing to the new address. Add evenIfDistributionPending parameter to handle the case when distribution cannot proceed (e.g., when paused). This allows callers to either: - Return false when distribution is pending (evenIfDistributionPending=false) - Force the change anyway (evenIfDistributionPending=true) Implementation: - Calls _handleDistributionBeforeAllocation before changing default address - Distributes pending issuance to old default address up to current block - Then changes allocation to new default address - When paused and evenIfDistributionPending=false, returns false instead of changing - Original setDefaultAllocationAddress(address) signature wraps new implementation with evenIfDistributionPending=false
Refactor _removeTargetFromList to _removeTarget to logically group target removal operations (array removal and mapping deletion) in a single function. Loop starts at index 1 to protect default allocation at index 0. Also fixes MILLION/PPM terminology, adds missing natspec, and adds test for address(0) default allocation.
When changing default allocation address, allocation data is copied from oldAddress to newAddress, which was overwriting newAddress's lastChangeNotifiedBlock. - If newAddress was just notified, the notification record was lost - If newAddress had a future block set (via forceTargetNoChangeNotificationBlock), that restriction was lost - When changing from address(0) (which is never notified), newAddress's lastChangeNotifiedBlock was incorrectly set to 0 Fix: preserve newAddress's lastChangeNotifiedBlock when copying allocation data. Also clarifies that notification blocks are target-specific, not about the default in general.
IA: Audit fix/response for Issuance Allocator Incorporated into audit report (Graph_IssuanceAllocator_v01.pdf) and now part of baseline for subsequent.
Response to TRST-L-3 "Inaccurate view functions for the zero-address target" The current behaviour of getTargetAllocation() and getTargetIssuancePerBlock() is deliberate and regarded as correct. These functions return assigned allocations rather than effective minting amounts. This design choice is supported by the following reasoning: 1. **Semantically correct**: The functions answer "what allocation is assigned to this target?" rather than "what does this target receive?". 2. **Consistent interface**: Callers can query the default target allocation uniformly, regardless of whether it's address(0) or not. 3. **Accounting utility**: When address(0) is used, the returned values represent the unallocated/unminted portion of issuance. 4. **Separation of concerns**: getTotalAllocation() provides the effective system-wide allocation that excludes unmintable portions. The documentation has been enhanced to make this behaviour explicit for the address(0) edge case, to avoid potential confusion.
Addresses TRST-R-1: Follow naming conventions for non-public functions Updated two private functions for consistency with the rest of the codebase: - notifyAllTargets() → _notifyAllTargets() - accumulatePendingIssuance() → _accumulatePendingIssuance() All private and internal functions now consistently use underscore prefixes, improving code clarity and reducing potential for errors.
…nce for TRST-R-5
Addresses TRST-L-1: "Reward reclaiming efficiency is significantly limited"
Addresses TRST-R-5: "Clarify reclaim precedence"
The TRST-L-1 audit finding identified that reward reclaiming through takeRewards()
is flawed because a rational indexer who would not receive rewards would not
trigger the rewarding logic in the first place. The recommended mitigation is to
add calls from SubgraphService so that any entity can force a reclaim of denied
rewards. (Not adding to StakingExtension as it is deprecated and will soon not
be used.)
The TRST-R-5 finding noted that the precedence when both denial reasons apply
should be documented. Added explicit documentation that subgraph denylist
reclaim is prioritized over indexer eligibility reclaim.
Key changes:
1. Created RewardsReclaim library with canonical reclaim reason constants
using bytes32 identifiers (like OpenZeppelin roles) for extensibility
2. Migrated RewardsManagerStorage from separate reclaim address storage to
extensible mapping:
- Old: indexerEligibilityReclaimAddress, subgraphDeniedReclaimAddress
- New: mapping(bytes32 => address) reclaimAddresses
3. Replaced type-specific setters with generic setReclaimAddress(bytes32, address)
4. Refactored RewardsManager.takeRewards() with early return for zero rewards
and extracted helper functions:
- _calcAllocationRewards(): extract allocation data calculation
- _reclaimRewards(): common reclaim logic with named return value
- _deniedRewards(): implements short-circuit pattern with precedence
(SUBGRAPH_DENIED checked before INDEXER_INELIGIBLE)
5. Added public reclaimRewards() for external contracts to reclaim rewards
with custom reasons and data
6. Updated AllocationManager._presentPOI() to explicitly call reclaimRewards()
with specific reasons (STALE_POI, ALTRUISTIC_ALLOCATION, ZERO_POI,
ALLOCATION_TOO_YOUNG) instead of relying on takeRewards(). This enables
forced reclaiming of denied rewards, addressing the core TRST-L-1 issue.
7. Updated tests and mocks to use new bytes32-based API with RewardsReclaim
constants
This allows any entity to force reclaim of denied rewards through SubgraphService,
and enables future extension with new reclaim reasons without contract changes.
…d security improvements Major refactoring to improve clarity and address audit findings TRST-R-2, TRST-R-3, and TRST-R-4. Core Changes PPM to Absolute Rates Migration - Changed from percentage-based allocations (PPM) to absolute token rates (tokens per block) - Updated all allocation logic to use rates instead of percentages - Maintains 100% allocation invariant: totalAllocatorRate + totalSelfMintingRate == issuancePerBlock - Default target automatically adjusted to maintain invariant Improved Naming - Renamed `accumulatedSelfMinting` to `selfMintingOffset` for clarity - Renamed `setDefaultAllocationAddress` to `setDefaultTarget` - Event renamed: `DefaultAllocationAddressUpdated` to `DefaultTargetUpdated` - Field names: `*PPM` to `*Rate` (totalAllocationRate, allocatorMintingRate, selfMintingRate) API Changes - Replaced `bool evenIfDistributionPending` parameter with `uint256 minDistributedBlock` - More flexible control: allows requiring distribution to specific block before config changes - Translation: `evenIfDistributionPending=true` → `minDistributedBlock=0` Security Improvements TRST-R-2: Event Visibility for Self-Minting - Added `IssuanceSelfMintAllowance` event emitted when self-minting allowances are calculated - Provides visibility into all minting activity, not just allocator-minting TRST-R-3: Security Assumption Documentation - Documented overflow safety assumptions with concrete examples - Documented initialization block range safety (lastDistributionBlock starts at block.number) - Added inline comments explaining division-by-zero impossibility - Clarified pause behavior and block tracking divergence TRST-R-4: Reentrancy Protection - Added `ReentrancyGuardTransientUpgradeable` using EIP-1153 transient storage - Applied to all governance functions that modify configuration - `distributeIssuance()` intentionally NOT guarded (documented why it's safe) - Protection against multi-sig signature exploitation vectors Fix TRST-R-7: Correct pause behavior documentation for setter functions. Rate changes now correctly documented as applying immediately and being used retroactively when distribution resumes, rather than applying from lastDistributionBlock. Update documentation to reflect migration from PPM-based proportional allocation to absolute rate-based system (tokens per block): - Update all storage variable names and descriptions - Replace PPM terminology with rate-based terminology throughout - Document new default target mechanism and 100% allocation invariant - Update function signatures to use minDistributedBlock instead of evenIfDistributionPending parameter - Add documentation for new setDefaultTarget functions - Update all event signatures with new parameters - Add new error conditions and update existing ones - Update view function return types and descriptions - Clarify that notifications occur even when paused
… and add security improvements
… and add security improvements
Addresses TRST-M-3 audit finding by documenting the mathematical relationship between self-minting and allocator-minting. Changes: - Add comprehensive documentation of 5 key invariants in IssuanceAllocator.sol proving total issuance never exceeds Σ(issuancePerBlock_b) for any period - Remove duplicate sections (Events, Error Conditions, Storage) from IssuanceAllocator.md that are better documented in NatSpec - Add WIP Initial Setup deployment sequence to IssuanceAllocator.md
Addresses TRST-M-3 audit finding. Events for offset adjustments of accumulation and during pending distribution, emitting whenever offset changes and showing both before and after states for complete accounting transparency.
Addresses TRST-L-5 by adding configurable event emission to prevent potential gas limit deadlock in _advanceSelfMintingBlock(). Adds three modes controllable by governance: - None (0): Skip event emission entirely (lowest gas) - Aggregate (1): Single aggregated event (medium gas) - PerTarget (2): Per-target events (highest gas, default) Defaults to PerTarget to provide event visibility by default. Governance can switch to None or Aggregate to reduce gas costs as target count grows. Implementation: - Add SelfMintingEventMode enum to IIssuanceAllocatorTypes - Add selfMintingEventMode field to storage struct - Add IssuanceSelfMintAllowanceAggregate event - Add SelfMintingEventModeUpdated event - Add setSelfMintingEventMode(mode) governance function - Add getSelfMintingEventMode() view function - Modify _advanceSelfMintingBlock() to respect emission mode - Initialize to PerTarget in initialize()
…adeable version Replace ReentrancyGuardTransientUpgradeable (deprecated in OpenZeppelin 5.5+) with ReentrancyGuardTransient. The non-upgradeable version is appropriate since the guard only uses transient storage and has no upgradeable state. Resolves TRST-R-10
Add comments clarifying that accRewardsPending tracking is incomplete due to _presentPOI clearing pending even when rewards are not consumed. Addresses audit finding TRST-L-4 as acknowledged limitation.
Audited with all issues reported fixed in Graph_IssuanceAllocator_v03.
|
Audited with all issues marked fixed or (for one low severity issue) acknowledged in Graph_IssuanceAllocator_v03 for commit hash f283815. |
Rebase of: #1245
Issuance Allocator contracts, see: packages/issuance/contracts/allocate/IssuanceAllocator.md