Skip to content

Commit

Permalink
♻️ Introduce a reward accounter to manage rewards
Browse files Browse the repository at this point in the history
  • Loading branch information
KONFeature committed Sep 4, 2023
1 parent 661f4f9 commit 8f68817
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 48 deletions.
14 changes: 7 additions & 7 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,10 @@ MultiVestingWalletsTest:test_name() (gas: 12064)
MultiVestingWalletsTest:test_symbol() (gas: 12073)
MultiVestingWalletsTest:test_transfer() (gas: 226564)
MultiVestingWalletsTest:test_transferAvailableReserve() (gas: 98269)
RewarderPayTest:testFuzz_payUser(uint16) (runs: 1000, μ: 202033, ~: 202281)
RewarderPayTest:testFuzz_payUser_WithFraktions(uint16) (runs: 1000, μ: 805503, ~: 805755)
RewarderPayTest:testFuzz_payUser_WithFraktionsAndLoadOfState(uint16) (runs: 1000, μ: 3072469, ~: 3072720)
RewarderPayTest:testFuzz_payUser_WithFraktions_ClaimRewards(uint16) (runs: 1000, μ: 863679, ~: 863918)
RewarderPayTest:testFuzz_payUser(uint16) (runs: 1000, μ: 202037, ~: 202272)
RewarderPayTest:testFuzz_payUser_WithFraktions(uint16) (runs: 1000, μ: 805502, ~: 805746)
RewarderPayTest:testFuzz_payUser_WithFraktionsAndLoadOfState(uint16) (runs: 1000, μ: 3072432, ~: 3072684)
RewarderPayTest:testFuzz_payUser_WithFraktions_ClaimRewards(uint16) (runs: 1000, μ: 863676, ~: 863909)
RewarderPayTest:test_fail_payCreatorDirectlyBatch_ContractPaused() (gas: 111700)
RewarderPayTest:test_fail_payCreatorDirectlyBatch_EmptyAmount() (gas: 87450)
RewarderPayTest:test_fail_payCreatorDirectlyBatch_InvalidArray() (gas: 85876)
Expand All @@ -144,10 +144,10 @@ RewarderPayTest:test_fail_payUser_InexistantContent() (gas: 120867)
RewarderPayTest:test_fail_payUser_NotRewarder() (gas: 84760)
RewarderPayTest:test_fail_payUser_TooLargeArray() (gas: 89413)
RewarderPayTest:test_payCreatorDirectlyBatch() (gas: 145498)
RewarderPayTest:test_payUser() (gas: 192272)
RewarderPayTest:test_payUser() (gas: 192263)
RewarderPayTest:test_payUserDirectly() (gas: 114006)
RewarderPayTest:test_payUser_ContentTypeImpactReward() (gas: 256264)
RewarderPayTest:test_payUser_LargeReward() (gas: 801082)
RewarderPayTest:test_payUser_ContentTypeImpactReward() (gas: 256228)
RewarderPayTest:test_payUser_LargeReward() (gas: 801073)
RewarderPayTest:test_payUser_NoReward_ContentTypeNotKnown() (gas: 124252)
RewarderTest:test_fail_InitTwice() (gas: 16263)
RewarderTest:test_fail_multicall_NotAuthorized() (gas: 168325)
Expand Down
11 changes: 0 additions & 11 deletions contracts/reward/IRewarder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,6 @@ interface IRewarder is IPausable {
/// @dev Error throwned when the reward is invalid
error InvalidReward();

/* -------------------------------------------------------------------------- */
/* Struct's */
/* -------------------------------------------------------------------------- */

/// @dev Struct representing the total reward for a user, used as an in memory accounting
struct TotalRewards {
uint256 user;
uint256 owners;
uint256 content;
}

/* -------------------------------------------------------------------------- */
/* Event's */
/* -------------------------------------------------------------------------- */
Expand Down
35 changes: 35 additions & 0 deletions contracts/reward/RewardAccounter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: GNU GPLv3
pragma solidity 0.8.21;

/// @dev Struct representing the accounter for the reward
struct RewardAccounter {
uint256 user;
uint256 owners;
uint256 content;
}

/// @dev Tell to use the lib below for every RewardAccounter instance
using RewardAccounterLib for RewardAccounter global;

/// @dev 1 ether in WAD
uint256 constant WAD = 1 ether;

/// @author @KONFeature
/// @title RewardAccounterLib
/// @notice Library to ease the usage of the RewardAccounter struct
/// @custom:security-contact [email protected]
library RewardAccounterLib {
/// @dev Apply the user `badge` booster on the user reward
function applyUserBadge(RewardAccounter memory self, uint256 badge) internal pure {
unchecked {
self.user = self.user * badge / WAD;
}
}

/// @dev Get the total amount in the accounter
function getTotal(RewardAccounter memory self) internal pure returns (uint256 total) {
unchecked {
total = self.user + self.owners + self.content;
}
}
}
58 changes: 28 additions & 30 deletions contracts/reward/Rewarder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ArrayLib } from "../libs/ArrayLib.sol";
import { ContentId } from "../libs/ContentId.sol";
import { FraktionId } from "../libs/FraktionId.sol";
import { FrakRoles } from "../roles/FrakRoles.sol";
import { RewardAccounter } from "./RewardAccounter.sol";
import { FraktionTokens } from "../fraktions/FraktionTokens.sol";
import { IFrakToken } from "../tokens/IFrakToken.sol";
import { FrakAccessControlUpgradeable } from "../roles/FrakAccessControlUpgradeable.sol";
Expand Down Expand Up @@ -71,6 +72,11 @@ contract Rewarder is
/* Event's */
/* -------------------------------------------------------------------------- */

/// @dev Event emitted when a user is rewarded for his listen
event RewardOnContent(
address indexed user, ContentId indexed contentId, uint256 baseUserReward, uint256 earningFactor
);

/// @dev 'keccak256(bytes("RewardOnContent(address,uint256,uint256,uint256"))'
uint256 private constant _REWARD_ON_CONTENT_EVENT_SELECTOR =
0x6396a2d965d9d843b0159912a8413f069bcce1830e320cd0b6cd5dc03d11eddf;
Expand Down Expand Up @@ -265,55 +271,47 @@ contract Rewarder is
}

// Get the data we will need in this level
TotalRewards memory totalRewards = TotalRewards(0, 0, 0);
RewardAccounter memory rewardsAccounter = RewardAccounter(0, 0, 0);

// Get all the payed fraktion types
uint256 rewardForContentType = baseRewardForContentType(contentType);

// Iterate over each content the user listened
uint256 length = contentIds.length;
for (uint256 i; i < length;) {
computeRewardForContent(contentIds[i], listenCounts[i], rewardForContentType, listener, totalRewards);
computeRewardForContent(contentIds[i], listenCounts[i], rewardForContentType, listener, rewardsAccounter);

// Finally, increase the counter
unchecked {
++i;
}
}

// Then outside of our loop find the user badge
uint256 listenerBadge = getListenerBadge(listener);

// Compute the total amount to mint, and ensure we don't exceed our cap
assembly {
// Update the total mint for user with his listener badges
mstore(totalRewards, div(mul(listenerBadge, mload(totalRewards)), 1000000000000000000))
unchecked {
// Apply the user badge
rewardsAccounter.applyUserBadge(getListenerBadge(listener));

// Compute the total to be minted
let userAndOwner := add(mload(totalRewards), mload(add(totalRewards, 0x20)))
let referralAndContent := add(mload(add(totalRewards, 0x40)), mload(add(totalRewards, 0x60)))
let totalMint := add(userAndOwner, referralAndContent)
// Compute the new total frk minted for reward
uint256 newTotalMint = totalFrakMinted + rewardsAccounter.getTotal();

// Compute the new total amount
let newTotalAmount := add(totalMint, sload(totalFrakMinted.slot))
// Ensure it's good
if gt(newTotalAmount, REWARD_MINT_CAP) {
mstore(0x00, _REWARD_TOO_LARGE_SELECTOR)
revert(0x1c, 0x04)
// Ensure we don't go past the mint cap
if (newTotalMint > REWARD_MINT_CAP) {
revert RewardTooLarge();
}

// Increase our total frak minted
sstore(totalFrakMinted.slot, newTotalAmount)
totalFrakMinted = newTotalMint;
}

// Register the amount for listener
_addFoundsUnchecked(listener, totalRewards.user);
_addFoundsUnchecked(listener, rewardsAccounter.user);

// If we got reward for the pool, transfer them
if (totalRewards.content > 0) {
token.transfer(address(contentPool), totalRewards.content);
if (rewardsAccounter.content > 0) {
token.transfer(address(contentPool), rewardsAccounter.content);
}
/*if (totalRewards.referral > 0) {
token.transfer(address(referralPool), totalRewards.referral);
/*if (rewardsAccounter.referral > 0) {
token.transfer(address(referralPool), rewardsAccounter.referral);
}*/
}

Expand Down Expand Up @@ -381,13 +379,13 @@ contract Rewarder is
/// @param listenCount The number of listen for the given content
/// @param rewardForContentType The base reward for the given content type
/// @param listener The listener address
/// @param totalRewards The current total rewards in memory accounting (that will be updated)
/// @param rewardsAccounter The current total rewards in memory accounting (that will be updated)
function computeRewardForContent(
ContentId contentId,
uint256 listenCount,
uint256 rewardForContentType,
address listener,
TotalRewards memory totalRewards
RewardAccounter memory rewardsAccounter
)
private
{
Expand Down Expand Up @@ -427,7 +425,7 @@ contract Rewarder is

// User reward at 35%
let userReward := div(mul(totalReward, 35), 100)
mstore(totalRewards, add(mload(totalRewards), userReward))
mstore(rewardsAccounter, add(mload(rewardsAccounter), userReward))

// Check if the user has got paid fraktion
switch hasOnePaidFraktion
Expand All @@ -441,11 +439,11 @@ contract Rewarder is
ownerReward := sub(sub(totalReward, userReward), contentPoolReward)

// Store the content pool reward
mstore(add(totalRewards, 0x40), add(mload(add(totalRewards, 0x40)), contentPoolReward))
mstore(add(rewardsAccounter, 0x40), add(mload(add(rewardsAccounter, 0x40)), contentPoolReward))
}

// Store the owner reward in memory
mstore(add(totalRewards, 0x20), add(mload(add(totalRewards, 0x20)), ownerReward))
mstore(add(rewardsAccounter, 0x20), add(mload(add(rewardsAccounter, 0x20)), ownerReward))

// Emit the reward on content event's
mstore(0, userReward)
Expand Down

0 comments on commit 8f68817

Please sign in to comment.