Per-holder rewards (setShare)
Dividend-style vaults need to know each holder's eligible balance. Don't read balanceOf yourself — return wantsShareUpdates() = true, and the platform token pushes setShare(holder, eligibleShare) to your vault after every transfer. eligibleShare is the holder's post-trade balance AFTER exclusions and the minimum-holding threshold (0 if excluded or below it). Trust only these pushed shares.
The golden rule: ALWAYS settle a holder's earned rewards BEFORE overwriting their stored share. Drive your accounting with a per-share accumulator (accRewardPerShare). On each setShare, first credit pending = storedShare * (accRewardPerShare - holderDebt), then update the stored share and debt. This is exactly what guarantees selling never retroactively shrinks already-earned rewards, and a fresh buyer cannot claim rewards that accrued before they held.
mapping(address => uint256) share; // last pushed eligible share
mapping(address => uint256) debt; // accRewardPerShare snapshot
mapping(address => uint256) pending; // settled, claimable
uint256 accRewardPerShare; // scaled by 1e18
modifier onlyTaxToken() { require(msg.sender == taxToken, "only token"); _; }
function setShare(address h, uint256 newShare) external onlyTaxToken {
_settle(h); // credit what they earned at the OLD share
totalShares = totalShares - share[h] + newShare;
share[h] = newShare;
debt[h] = accRewardPerShare; // reset baseline
}
function _settle(address h) internal {
pending[h] += share[h] * (accRewardPerShare - debt[h]) / 1e18;
debt[h] = accRewardPerShare;
}
// when a round buys 'reward' tokens: accRewardPerShare += reward * 1e18 / totalShares;