Bullish.social

Distribution Methods

Bullish Social supports four distribution methods to split club rewards among members. Each method corresponds to a different on-chain calculation, as defined in the BSReward smart contract.


🟢 1. Shared

All users receive the same reward amount, regardless of score or rank.

Contract Logic:

userReward = rewardPool / receiverCount;

Example:
If a club earns 1000 $BUSO and has 5 members, each member gets:

1000 / 5 = 200 $BUSO

🟡 2. Rank-Based

Users are rewarded based on their leaderboard rank using a reverse-rank weighting system.

Used Functions:

function calculateRankRewardPiece(uint64 receivers, uint256 totalReward, uint8 rankWeight) public pure returns(uint256) {
    //Calculate sum of ranks (∑) eg. 1+2+3+4...168+169+170
    uint256 totalVolume = receivers * (receivers + 1) / 2;

    //Calculate reward piece
    uint256 rewardPiece = totalReward / totalVolume;
    return (rewardPiece * rankWeight) / 100; // Get percentage of the piece
}

function calculateRankReward(uint64 totalReceivers, uint64 rank, uint256 rewardPiece) public pure returns(uint256) {
    return rewardPiece * (totalReceivers - rank + 1);
}

// 100 = %100 weight to rank
rewardPiece = calculateRankRewardPiece(receiverCount, rewardPool, 100);
reward = calculateRankReward(receiverCount, rank, rewardPiece);

Explanation:

  • Each user gets:
    reward = rewardPiece × (receiverCount - rank + 1)

Example:
5 users, 1000 $BUSO reward:

RankVolumeReward
15(5 / 15) × 1000 = 333.33
24= 266.66
33= 200
42= 133.33
51= 66.66

🔵 3. Score-Based

Rewards are distributed based on each user's score.

Used Functions:

function calculateScoreRewardPiece(uint64 totalScore, uint256 totalReward, uint8 scoreWeightPercent) public pure returns(uint256) {
    uint256 piece = totalReward / totalScore;
    return (piece * scoreWeightPercent) / 100; // Get percentage of the piece
}

function calculateScoreReward(uint64 score, uint256 rewardPiece) public pure returns(uint256) {
    return score * rewardPiece;
}

// 100 = %100 weight to score
scoreRewardPiece = calculateScoreRewardPiece(totalScore, rewardPool, 100);
clubMemberReward = calculateScoreReward(score, scoreRewardPiece);

Explanation:

  • Each user gets:
    reward = (score / totalScore) × rewardPool

Example:
Scores = [60, 30, 10], total = 100
Reward = 1000 $BUSO

UserScoreReward
A60600
B30300
C10100

🟣 4. Balanced

A hybrid method that combines both rank and score, using adjustable weights defined by the DAO.

Used Functions:

rankRewardPiece = calculateRankRewardPiece(receiverCount, rewardPool, rankWeight);
scoreRewardPiece = calculateScoreRewardPiece(totalScore, rewardPool, scoreWeight);

rankReward = calculateRankReward(receiverCount, rank, rankRewardPiece);
scoreReward = calculateScoreReward(score, scoreRewardPiece);

reward = rankReward + scoreReward;

Formula:

userReward = 
  (rankVolume / totalRankVolume) × (rankWeight × rewardPool) +
  (score / totalScore) × (scoreWeight × rewardPool)

Example:

Let's say we have a club with 5 members, and the variables are:

  • Club's Reward Pool: 1000 $BUSO
  • Rank Weight: 40%, Score Weight: 60%

This Club uses the Balanced method to distribute rewards to its members.

MemberRank VolumeRank RewardScoreScore RewardTotal
A (1.)5133.350300433.3
B (2.)4106.640240346.6
C (3.)380.030180260.0
D (4.)253.320120173.3
E (5.)126.6106086.6

⚙️ The Balanced method used in both individual and club rewards is influenced by two DAO-controlled parameters:

  • individualScoreWeight: Determines how much of an individual's reward is based on score versus rank.
  • clubScoreWeight: Determines the same for clubs.

For example, if individualScoreWeight is set to 60%, then 60% of the reward is calculated from the user’s score and 40% from their rank. These values can be updated at any time through a DAO proposal.


Where the calculation happens?

These calculations are handled fully on-chain inside the BSReward contract, and depend on real-time snapshot proofs and DAO-configured weights.

On this page