Docs menu

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;
Keep setShare cheap and never let it revert hard — the token calls it best-effort, so a revert just skips that one update without blocking the transfer (a heavy setShare would make every trade expensive). Settle-on-claim, settle-on-update; never recompute from live balances.