Skip to content

Commit

Permalink
Merge branch 'release/core-contracts/12' into soloseng/streamline-L2-…
Browse files Browse the repository at this point in the history
…validator-test
  • Loading branch information
m-chrzan committed Nov 1, 2024
2 parents 78939f5 + 03d20ee commit 1123827
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 41 deletions.
89 changes: 58 additions & 31 deletions packages/protocol/contracts/common/FeeHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ contract FeeHandler is
// Historical amounts burned by this contract
uint256 pastBurn;
uint256 lastLimitDay;
uint256 toBurn;
}

struct Beneficiary {
Expand All @@ -71,7 +72,7 @@ contract FeeHandler is

address public ignoreRenaming_carbonFeeBeneficiary;

uint256 public celoToBeBurned;
uint256 private deprecated_celoToBeBurned; // deprecated

// This mapping can not be public because it contains a FixidityLib.Fraction
// and that'd be only supported with experimental features in this
Expand Down Expand Up @@ -146,6 +147,10 @@ contract FeeHandler is
_setCarbonFraction(newFraction);
}

function setDistributionAndBurnAmounts(address tokenAddress) external {
return _setDistributionAndBurnAmounts(tokenStates[tokenAddress], IERC20(tokenAddress));
}

function changeOtherBeneficiaryAllocation(
address beneficiary,
uint256 _newFraction
Expand Down Expand Up @@ -251,6 +256,8 @@ contract FeeHandler is
@param tokenAddress The address of the token for which to distribute the available tokens.
*/
function distribute(address tokenAddress) external {
TokenState storage tokenState = tokenStates[tokenAddress];
_setDistributionAndBurnAmounts(tokenState, IERC20(tokenAddress));
return _distribute(tokenAddress);
}

Expand Down Expand Up @@ -315,6 +322,10 @@ contract FeeHandler is
return IERC20(token).transfer(recipient, value);
}

function getCeloToBeBurned() external view returns (uint256) {
return getCeloTokenState().toBurn;
}

/**
* @param token The address of the token to query.
* @return The amount burned for a token.
Expand Down Expand Up @@ -384,6 +395,10 @@ contract FeeHandler is
return tokenStates[tokenAddress].toDistribute;
}

function getTokenToBurn(address tokenAddress) external view returns (uint256) {
return tokenStates[tokenAddress].toBurn;
}

function getCarbonFraction() external view returns (uint256) {
return getCarbonFractionFixidity().unwrap();
}
Expand Down Expand Up @@ -445,19 +460,17 @@ contract FeeHandler is
function getActiveTokens() public view returns (address[] memory) {
return activeTokens.values;
}
// TODO to allow this function to be called publicly, it should
// keep track of amounts to be burned, like the Celo token does
function _setDistributionAmounts(
TokenState storage tokenState,
IERC20 token
) internal returns (uint256) {
uint256 balanceOfToken = token.balanceOf(address(this));
uint256 balanceToProcess = balanceOfToken.sub(tokenState.toDistribute);

uint256 balanceToBurn = _setDistributeAfterBurn(tokenState, balanceToProcess);
function _getBurnFraction() internal view returns (uint256) {
return getBurnFractionFixidity().unwrap();
}

function _setDistributionAndBurnAmounts(TokenState storage tokenState, IERC20 token) internal {
uint256 balanceOfToken = token.balanceOf(address(this));
uint256 balanceToProcess = balanceOfToken.sub(tokenState.toDistribute).sub(tokenState.toBurn);
_setDistributeAfterBurn(tokenState, balanceToProcess);

emit DistributionAmountSet(address(token), tokenState.toDistribute);
return balanceToBurn;
}

function _executePayment(address tokenAddress, address beneficiary, uint256 amount) internal {
Expand Down Expand Up @@ -485,21 +498,26 @@ contract FeeHandler is
uint256 balanceToBurn = FixidityLib
.newFixed(balanceToProcess)
.multiply(getBurnFractionFixidity())
.fromFixed(); //here2
.fromFixed();
tokenState.toBurn = tokenState.toBurn.add(balanceToBurn);
tokenState.toDistribute = tokenState.toDistribute.add(balanceToProcess.sub(balanceToBurn));
return balanceToBurn;
return tokenState.toBurn;
}

function shouldBurn() public view returns (bool) {
return _getBurnFraction() != 0;
}

function checkTotalBeneficiary() internal view {
require(
getTotalFractionOfOtherBeneficiariesAndCarbonFixidity().lt(FixidityLib.fixed1()),
getTotalFractionOfOtherBeneficiariesAndCarbonFixidity().lte(FixidityLib.fixed1()),
"Total beneficiaries fraction must be less than 1"
);
}

function _setCarbonFraction(uint256 _newFraction) internal {
FixidityLib.Fraction memory newFraction = FixidityLib.wrap(_newFraction);
require(newFraction.lt(FixidityLib.fixed1()), "New cargon fraction can't be greather than 1");
require(newFraction.lte(FixidityLib.fixed1()), "New cargon fraction can't be greather than 1");
ignoreRenaming_inverseCarbonFraction = FixidityLib.fixed1().subtract(newFraction);
checkTotalBeneficiary();
emit CarbonFractionSet(_newFraction);
Expand Down Expand Up @@ -552,17 +570,16 @@ contract FeeHandler is
* @notice Burns all the Celo balance of this contract.
*/
function _burnCelo() private {
TokenState storage tokenState = tokenStates[getCeloTokenAddress()];
ICeloToken celo = ICeloToken(getCeloTokenAddress());
address celoTokenAddress = getCeloTokenAddress();
TokenState storage tokenState = tokenStates[celoTokenAddress];
_setDistributionAndBurnAmounts(tokenState, IERC20(celoTokenAddress));

uint256 balanceOfCelo = address(this).balance;

uint256 balanceToProcess = balanceOfCelo.sub(tokenState.toDistribute).sub(celoToBeBurned);
uint256 balanceToBurn = _setDistributeAfterBurn(tokenState, balanceToProcess);
uint256 totalBalanceToBurn = balanceToBurn.add(celoToBeBurned);
celoToBeBurned = 0;
if (tokenState.toBurn == 0) {
return;
}

celo.burn(totalBalanceToBurn);
ICeloToken(celoTokenAddress).burn(tokenState.toBurn);
tokenState.toBurn = 0;
}

/**
Expand Down Expand Up @@ -622,6 +639,9 @@ contract FeeHandler is
}

function _sell(address tokenAddress) private onlyWhenNotFrozen nonReentrant {
if (!shouldBurn()) {
return;
}
IERC20 token = IERC20(tokenAddress);

TokenState storage tokenState = tokenStates[tokenAddress];
Expand All @@ -631,19 +651,21 @@ contract FeeHandler is
"Max slippage has to be set to sell token"
);

uint256 balanceToBurn = _setDistributionAmounts(tokenState, token);
_setDistributionAndBurnAmounts(tokenState, token);

// small numbers cause rounding errors and zero case should be skipped
if (balanceToBurn < MIN_BURN) {
return;
} // eso debería estar antes de quemar el storage
uint256 balanceToBurn = tokenState.toBurn;

if (dailySellLimitHit(tokenAddress, balanceToBurn)) {
// in case the limit is hit, burn the max possible
balanceToBurn = tokenState.currentDaySellLimit;
emit DailyLimitHit(tokenAddress, balanceToBurn);
}

// small numbers cause rounding errors and zero case should be skipped
if (balanceToBurn < MIN_BURN) {
return;
}

token.transfer(tokenState.handler, balanceToBurn);
IFeeHandlerSeller handler = IFeeHandlerSeller(tokenState.handler);

Expand All @@ -654,7 +676,9 @@ contract FeeHandler is
FixidityLib.unwrap(tokenState.maxSlippage)
);

celoToBeBurned = celoToBeBurned.add(celoReceived);
// substract from toBurn only the amount that was burned
tokenState.toBurn = tokenState.toBurn.sub(balanceToBurn);
getCeloTokenState().toBurn = getCeloTokenState().toBurn.add(celoReceived);
tokenState.pastBurn = tokenState.pastBurn.add(balanceToBurn);
updateLimits(tokenAddress, balanceToBurn);

Expand Down Expand Up @@ -757,7 +781,6 @@ contract FeeHandler is
_handleCelo();
}

// new handle
// tokenAddresses should not contain the Celo address
function _handle(address[] memory tokenAddresses) private {
// Celo doesn't have to be exchanged for anything
Expand All @@ -769,4 +792,8 @@ contract FeeHandler is

_handleCelo();
}

function getCeloTokenState() private view returns (TokenState storage) {
return tokenStates[getCeloTokenAddress()];
}
}
87 changes: 77 additions & 10 deletions packages/protocol/test-sol/unit/common/FeeHandler.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,15 @@ contract FeeHandlerTest_SetCarbonFraction is FeeHandlerTest {
function test_Reverts_WhenFractionsGreaterThanOne() public {
vm.expectRevert("New cargon fraction can't be greather than 1");
feeHandler.setCarbonFraction(FixidityLib.newFixedFraction(3, 2).unwrap());
// add another and then try to make carbon out of bounds
}

function test_WhenOtherBeneficiaryWouldAddToOne() public {
feeHandler.addOtherBeneficiary(
op,
(20 * 1e24) / 100, // TODO use fixidity
"OP revenue share"
);
vm.expectRevert("Total beneficiaries fraction must be less than 1");

feeHandler.setCarbonFraction(FixidityLib.newFixedFraction(8, 10).unwrap());
}

Expand Down Expand Up @@ -456,14 +458,15 @@ contract FeeHandlerTest_AddOtherBeneficiary is FeeHandlerTestAbstract {
assertEq(name, "OP revenue share");
}

function test_Reverts_WhenBurningFractionWouldBeZero() public {
function test_SetsWhenBurningFractionWouldBeZero() public {
setCarbonFraction(20, 100);
vm.expectRevert("Total beneficiaries fraction must be less than 1");
feeHandler.addOtherBeneficiary(
op,
(80 * 1e24) / 100, // TODO use fixidity
"OP revenue share"
);

assertFalse(feeHandler.shouldBurn());
}

function test_Reverts_WhenaddingSameTokenTwice() public {
Expand Down Expand Up @@ -532,15 +535,15 @@ contract FeeHandlerTest_Distribute is FeeHandlerTestAbstract {
vm.recordLogs();
feeHandler.distribute(address(stableToken));
Vm.Log[] memory entries = vm.getRecordedLogs();
assertEq(entries.length, 0);
assertEq(entries.length, 2);
}

function test_DoesntDistributeWhenBalanceIsZero() public {
addAndActivateToken(address(stableToken), address(mentoSeller));
vm.recordLogs();
feeHandler.distribute(address(stableToken));
Vm.Log[] memory entries = vm.getRecordedLogs();
assertEq(entries.length, 0);
assertEq(entries.length, 1); // TODO figure out why this is 1 and the above is 2
}

function test_Distribute() public {
Expand All @@ -553,6 +556,25 @@ contract FeeHandlerTest_Distribute is FeeHandlerTestAbstract {
assertEq(stableToken.balanceOf(address(feeHandler)), 0);
assertEq(stableToken.balanceOf(EXAMPLE_BENEFICIARY_ADDRESS), 2e17);
}

function test_distributesWithoutBurn() public {
fundFeeHandlerStable(1e18, address(stableToken), address(exchangeUSD));
addAndActivateToken(address(stableToken), address(mentoSeller));

feeHandler.distribute(address(stableToken));

assertEq(stableToken.balanceOf(address(feeHandler)), 8e17);
assertEq(stableToken.balanceOf(EXAMPLE_BENEFICIARY_ADDRESS), 2e17);
}

function test_WhenBurnFractionIsZero() public {
setCarbonFraction(100, 100);
fundFeeHandlerStable(1e18, address(stableToken), address(exchangeUSD));
addAndActivateToken(address(stableToken), address(mentoSeller));

feeHandler.distribute(address(stableToken));
assertEq(stableToken.balanceOf(address(feeHandler)), 0);
}
}

contract FeeHandlerTest_Distribute_WhenOtherBeneficiaries is FeeHandlerTestAbstract {
Expand Down Expand Up @@ -691,13 +713,17 @@ contract FeeHandlerTest_SellMentoTokens_WhenTokenEnabled is FeeHandlerTest_SellM
assertEq(stableToken.balanceOf(address(feeHandler)), balanceBefore);
}

function resetLimit() internal {
skip(DAY);
}

function test_ResetSellLimitDaily() public {
fundFeeHandlerStable(3000, address(stableToken), address(exchangeUSD));

feeHandler.setDailySellLimit(address(stableToken), 1000);
feeHandler.sell(address(stableToken));
assertEq(stableToken.balanceOf(address(feeHandler)), 2000);
skip(DAY);
resetLimit();
feeHandler.sell(address(stableToken));
assertEq(stableToken.balanceOf(address(feeHandler)), 1000);
}
Expand All @@ -712,6 +738,39 @@ contract FeeHandlerTest_SellMentoTokens_WhenTokenEnabled is FeeHandlerTest_SellM
assertEq(stableToken.balanceOf(address(feeHandler)), 2000);
}

function test_HitLimitDoesntAffectAccounting() public {
fundFeeHandlerStable(3000, address(stableToken), address(exchangeUSD));
feeHandler.setDailySellLimit(address(stableToken), 1000);

feeHandler.sell(address(stableToken));
assertEq(stableToken.balanceOf(address(feeHandler)), 2000);

assertEq(feeHandler.getTokenToDistribute(address(stableToken)), 600);

resetLimit();

feeHandler.sell(address(stableToken));
assertEq(feeHandler.getTokenToDistribute(address(stableToken)), 600);
assertEq(stableToken.balanceOf(address(feeHandler)), 1000);

resetLimit();

feeHandler.sell(address(stableToken));
assertEq(feeHandler.getTokenToDistribute(address(stableToken)), 600);
assertEq(stableToken.balanceOf(address(feeHandler)), 600);
}

function test_setDistributionAndBurnAmountsDoesntAffectBurn() public {
fundFeeHandlerStable(3000, address(stableToken), address(exchangeUSD));
feeHandler.setDailySellLimit(address(stableToken), 1000);

feeHandler.setDistributionAndBurnAmounts(address(stableToken));
feeHandler.sell(address(stableToken));

assertEq(stableToken.balanceOf(address(feeHandler)), 2000);
assertEq(feeHandler.getTokenToDistribute(address(stableToken)), 600);
}

function test_Sell_WhenOtherTokenHitLimit() public {
fundFeeHandlerStable(3000, address(stableToken), address(exchangeUSD));
feeHandler.setDailySellLimit(address(stableToken), 1000);
Expand All @@ -738,6 +797,14 @@ contract FeeHandlerTest_SellMentoTokens_WhenTokenEnabled is FeeHandlerTest_SellM
assertEq(stableTokenEUR.balanceOf(address(feeHandler)), 2000);
}

function test_WhenBurnFractionIsZero() public {
setCarbonFraction(100, 100);
fundFeeHandlerStable(3000, address(stableToken), address(exchangeUSD));
feeHandler.sell(address(stableToken));

assertEq(stableToken.balanceOf(address(feeHandler)), 3000);
}

function test_SellsWithMento() public {
fundFeeHandlerStable(1e18, address(stableToken), address(exchangeUSD));
assertEq(feeHandler.getPastBurnForToken(address(stableToken)), 0);
Expand All @@ -746,7 +813,7 @@ contract FeeHandlerTest_SellMentoTokens_WhenTokenEnabled is FeeHandlerTest_SellM
assertEq(feeHandler.getPastBurnForToken(address(stableToken)), 8e17);
assertEq(stableToken.balanceOf(address(feeHandler)), 2e17);
assertEq(feeHandler.getTokenToDistribute(address(stableToken)), 2e17);
assertEq(feeHandler.celoToBeBurned(), expectedCeloAmount);
assertEq(feeHandler.getCeloToBeBurned(), expectedCeloAmount);
}

function test_Reverts_WhenNotEnoughReports() public {
Expand Down Expand Up @@ -1001,6 +1068,7 @@ contract FeeHandlerTest_HandleMentoTokens is FeeHandlerTestAbstract {
celoToken.balanceOf(address(0x000000000000000000000000000000000000dEaD)),
398482170620712919
);

assertEq(stableToken.balanceOf(address(feeHandler)), 0);
}
}
Expand Down Expand Up @@ -1162,8 +1230,7 @@ contract FeeHandlerTest_SetBeneficiaryFraction is FeeHandlerTestAbstract {
assertEq(fraction, (30 * 1e24) / 100);
}

function test_Reverts_WhenFractionWouldBeZero() public {
vm.expectRevert("Total beneficiaries fraction must be less than 1");
function test_WhenFractionWouldBeZero() public {
feeHandler.setBeneficiaryFraction(op, (80 * 1e24) / 100);
}

Expand Down

0 comments on commit 1123827

Please sign in to comment.