Skip to content

Commit

Permalink
refactor: add function to calculate total accrued rewards
Browse files Browse the repository at this point in the history
  • Loading branch information
jpgonzalezra committed May 27, 2024
1 parent ae4252a commit 4a17de5
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 15 deletions.
35 changes: 25 additions & 10 deletions src/CompotaToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ contract CompotaToken is ICompotaToken, ERC20Extended, Owned {

uint16 public yearlyRate;

uint256 public latestUpdateTimestamp;
uint256 internal _totalSupply;
address[] internal _earners;

mapping(address => uint256) internal _balances;
mapping(address => uint256) internal _lastUpdateTimestamp;
Expand Down Expand Up @@ -97,12 +97,7 @@ contract CompotaToken is ICompotaToken, ERC20Extended, Owned {
* @inheritdoc IERC20
*/
function totalSupply() external view returns (uint256 totalSupply_) {
totalSupply_ = _totalSupply;
uint256 length = _earners.length;
for (uint256 i = 0; i < length; i++) {
totalSupply_ += _calculateCurrentRewards(_earners[i]);
}
return totalSupply_;
return _totalSupply + _calculateTotalCurrentRewards();
}

/**
Expand Down Expand Up @@ -179,9 +174,9 @@ contract CompotaToken is ICompotaToken, ERC20Extended, Owned {
*/
function _updateRewards(address account_) internal {
uint256 timestamp = block.timestamp;
latestUpdateTimestamp = timestamp;
if (_lastUpdateTimestamp[account_] == 0) {
_lastUpdateTimestamp[account_] = timestamp;
_earners.push(account_);
emit StartedEarningRewards(account_);
return;
}
Expand All @@ -200,12 +195,32 @@ contract CompotaToken is ICompotaToken, ERC20Extended, Owned {
*/
function _calculateCurrentRewards(address account_) internal view returns (uint256) {
if (_lastUpdateTimestamp[account_] == 0) return 0;
return _calculateRewards(_balances[account_], _lastUpdateTimestamp[account_]);
}

/**
* @notice Calculates the total current accrued rewards for the entire supply since the last update.
* @return The amount of rewards accrued since the last update.
*/
function _calculateTotalCurrentRewards() internal view returns (uint256) {
if (latestUpdateTimestamp == 0) return 0;
return _calculateRewards(_totalSupply, latestUpdateTimestamp);
}

/**
* @notice Generalized function to calculate rewards based on an amount and a timestamp.
* @param amount_ The amount of tokens to calculate rewards for.
* @param timestamp_ The timestamp to calculate rewards from.
* @return The amount of rewards accrued since the last update.
*/
function _calculateRewards(uint256 amount_, uint256 timestamp_) internal view returns (uint256) {
if (timestamp_ == 0) return 0;
uint256 timeElapsed;
// Safe to use unchecked here, since `block.timestamp` is always greater than `_lastUpdateTimestamp[account_]`.
unchecked {
timeElapsed = block.timestamp - _lastUpdateTimestamp[account_];
timeElapsed = block.timestamp - timestamp_;
}
return (_balances[account_] * timeElapsed * yearlyRate) / (SCALE_FACTOR * uint256(SECONDS_PER_YEAR));
return (amount_ * timeElapsed * yearlyRate) / (SCALE_FACTOR * uint256(SECONDS_PER_YEAR));
}

/**
Expand Down
14 changes: 9 additions & 5 deletions test/CompotaToken.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -279,18 +279,22 @@ contract CompotaTokenTest is Test {
}

function testTotalSupplyWithUnclaimedRewards() external {
uint256 initialMint = 1000 * 10e6; // Initial mint amount
uint256 aliceInitialMint = 1000 * 10e6;
uint256 bobInitialMint = aliceInitialMint * 2;

_mint(owner, alice, initialMint);
assertEq(token.totalSupply(), initialMint);
_mint(owner, alice, aliceInitialMint);
_mint(owner, bob, bobInitialMint);

uint256 totalSupply = aliceInitialMint + bobInitialMint;
assertEq(token.totalSupply(), totalSupply);

vm.warp(block.timestamp + 180 days);

// Calculate expected rewards for 180 days with the initial rate
uint256 expectedRewards = (initialMint * INTEREST_RATE * 180 days) / (10_000 * 365 days);
uint256 expectedRewards = (totalSupply * INTEREST_RATE * 180 days) / (10_000 * 365 days);

// Verify total supply includes unclaimed rewards
assertEq(token.totalSupply(), initialMint + expectedRewards);
assertEq(token.totalSupply(), totalSupply + expectedRewards);
}

function testClaimRewardsWithNoRewards() public {
Expand Down

0 comments on commit 4a17de5

Please sign in to comment.