Why Steem curation reward is needlessly unfair and how to fix it

in #steem4 years ago (edited)

The problem

I believe most people would agree that it would be fair if curation rewards were roughly proportional to user's steem power. E.g. suppose one user has 1000 SP and another has 1 SP, then in an identical situation the first one should receive a reward which is approximately 1000 times bigger.

But that's not how it works in Steem. Steem squares percent increase and thus a user with 1000 SP will have roughly 1,000,000 bigger reward than a user with 1 SP in the otherwise identical situation.

Let's convert it to dollars to get a better understanding. Suppose Alice has $3500 worth of SP, while Bob has $3.5 (which is around what new steemit users get now). Whenever Alice gets $10 curation reward, Bob gets $10/1000000 = $0.00001, or 0.001 cents. In other words, Bob gets no reward at all, the number if negligible.

I believe this is a problem, because users with non-negligible Steem Power get negligible rewards and cannot accumulate power & money even if they do stellar curation job. So normal users (i.e. those who don't have a large amounts of SP) have no financial incentive to curate content.

Another way to formulate the problem: Suppose there is a hundred Bob1...Bob1000 users each having 1 SP. Their combine influence on content curation will be equal to influence of a single Alice user with 1000 SP. But their combined reward will be a tiny fraction of Alice's reward for curation. Influence is the same, but reward isn't, that's not right. (This happens because a square of a sum of positive numbers is bigger than sum of squares of same numbers.)

In the next section I will demonstrate the effect using the precise computations taken directly from Steem code. In the section after that I will explain why this effect is unnecessary and can be easily fixed.


Calculations related to vote weight are performed in vote_evaluator::do_apply. Let's consider a concrete example.

Suppose we have three users: Alice with 1000k vesting shares, Bob with 1k vesting shares and Claire with 1000k vesting shares. Suppose that their current voting power is equal (e.g. they are casting their first vote after a 24h wait) and they all vote for a single post. Claire votes first, then Bob and then Alice.

If voting power is 100%, then for 1k vesting shares we have 50 rshares added to the post.

Thus Claire adds 50000 rshares. Her weight is 50000. Post's total weight and total rshares is also 50000.

Then Bob adds 50 rshares. His weight is 50 * (50/50050)^2 = 0.00005. (This will actually be rounded down to 0, in code as calculations are done on integers, but for our purposes we can continue calculations with decimal numbers.) Now total weight is 50000.00005 and total rshares is 50050. (Note that Bob's contribution to post's total rshares is not negligible, even though his weight is.)

Then Alice adds 50000 rshares. Her weight is 50000 * (50000/100050)^2 = 12487, and total weight at that point is around 62487.

Code which calculates payout is in database::pay_curators. The formula is simple (max_rewards * weight) / c.total_vote_weight. Thus is max reward is $1000, then Claire will get $800, Bob will get $0.0000008 and Alice will get $200. Thus Bob's reward is 250,000,000 time's smaller than Alice's, even though Bob voted before Alice. (The exact ratio heavily depends on total weight at time of Bob's vote, the ratio can be between 1/1000 and 1/1,000,000,000.)

Note that Bob's contribution to post's rshares^2 (and thus to post's total reward) is 0.1%, but his reward is only 0.00000008% of post's reward.

If you don't trust my computations you can look up the actual weight displayed on steemd.com block explorer. E.g. let's go to some random post on steemd and open vote details. Killerstorm's weight is 18 while xeldal's weight is 2,189,025,913,558. killerstorm has ~10 SP while xeldal has 104197 SP. So killerstorm will get 121,612,550,753 smaller reward even though his SP is only 10419 times smaller. You can check any other post and find the same situation: users who don't get a lot of SP get negligible weight. The only way for a small-SP user to get a tiny reward is to be the first to vote.


Now one might ask: is there a deep reason for this effect? I mean, it wasn't done just to deprive low-SP user from rewards altogether? (If curation reward was proportional to rshares^2 contribution low-SP users would at least get some dollars or pennies.)

The reason is state in the documentation: Reward Vote Concentration. It is done to make sure that splitting one's Steem Power into multiple accounts would be unprofitable. Rewards are structured in such a way that user gets biggest reward if he votes using a single account which has all his SP.

But the formula which is used by Steem now is not the only one possible! It is possible to design a formula which will BOTH incentivize concentration and give people a fair reward roughly proportional to their SP.

Most likely devs tried a naive formula contribution/total contribution formula first, and when that didn't work well they just squared the percentage, which did the trick. But squaring is not the only thing you can do to a formula.

I'd say that the actual problem with the naive formula above is that it has no financial sense. If you square it, it doesn't start to make sense either: it works better in one way (disincentivizes splitting) but worse in other way (disincentivizes voting when you don't have a lot of SP). The cure is actually worse than the disease: the vast majority of users (i.e. all people who got free 10 SP upon registering on steemit) have NO incentive to vote.

A simple way to fix it is to use a formula which actually makes a financial sense. When user votes for a post, an exchange happens: user contributes rshares, and gets weight in return. Later he receives a reward proportional to weight. Thus we can model it as a process of buying weight for rshares.

What is a price of weight then? A fair price at which new voter doesn't dilute existing voters is total shares/total weight. Thus the amount of weight one gets is rshares * (total weight/total rshares) where total weight and total shares are computed before the vote is cast. However, in this case early voters get no advantage. We want new voter to buy weight at a price above the current price.

Thus we arrive to a formula weight = rshares * (1/k) * (total_weight/total rshares) where k is a price increase factor, it should be >= 1. High k will result in quickly diminishing voting rewards, while low k will result in smoother reward distribution. k = 2 will result in a fast reward decay similar to what Steem has now, but without the disproportionate distortion for low-SP voters.

This formula also discourages users from splitting their SP into multiple accounts: each time somebody votes, weight's price increases, thus casting multiple votes is an inferior strategy.

Let's try it with numbers for k = 2:

  1. User#1 casts a vote with 1 rshare, he gets 1 weight in return (this is a special case).
  2. User#2 casts a vote with 1 rshare, he gets 1 * (1/2) * 1/1 = 0.5 weight in return
  3. User#3 casts a vote with 1 rshare, he gets 1 * (1/2) * (1.5/2) = 0.375 weight

Total weight after 3 votes is 1.875 and payout percentages are 53%, 27% and 20%.

Now suppose user#2 and user#3 are actually the same person. In that case by casting a vote with 2 rshares he could get a weight of 1, and his payout would be 50% rather than 47%. Thus splitting votes isn't profitable.


I don't think it solves the voting problem entirely.

Looks like the proposed approach encourages people to vote on already popular contents rather than to look for new & good contents.

For example, if there is a post with already total_weight = 10 and rshares = 10 and reward = 100, when a new voter up-vote with 1 r_shares, she will get

  • weight = 1*0.5*10/10 = 0.5, so
  • new_total_weight = 10.5, and
  • new_total_rshares = 11, and
  • new_total_reward = 121, so the new voter will get
  • reward = 121*0.5/10.5/2 ~= 2.9, which is much higher than voting on a new post (so reward = 1/2 = 0.5).

Above example is voting after a whale. Here is another example of voting after crowd:

  • assume that a post which already got some up-votes have total_weight = 10 and rshares = 20 and reward = 400, then a new voter up-vote with 1 rshare, so her
  • weight = 1 * 0.5 * 10 / 20 = 0.25, so
  • new_total_weight = 10.25, and
  • new_total_rshares = 21, and
  • new_total_reward = 21*21 = 441, so the new voter will get
  • reward = 441*0.25/10.25/2 ~= 5.4, which is even higher than above example.

Above examples are based on using rshares * rshares as weight for reward distribution among posts. That said, if post A has 2x of rshares in comparison to post B, then total reward to post A is 4x of reward to post B.

While one's weight% to a post is near linear to current rshares of the post if she votes late, the reward she can get is also near linear to current rshares of the post. That said, by adding a vote to post A the voter will get around 2x reward in return in comparison to voting on post B.

This is the reason why the algorithm described in OP encourages voting on already popular posts. If we want to encourage people to find new quality post, it's likely we need to make the weight for reward distribution among posts linear to current rshares or less.

can you not add to the equations "time weight"?

What's "time weight"?

I mean add a time depended variable that will favour new posts over old posts...
if it's a new post multiply the reward results from @killerstorm by 1
and for older post by < 1 (time weighted)... Sorry for the inability to express it better.

I understand now. It's probably a solution, but 1. we need to find a good curve for the time weight, 2. "new" content doesn't always mean a post made later, sometime it means a post got ignored by mass.

@killerstorm on behalf of all low-SP users, I thank you! This is a brilliant post that actually deserves a massive payout, I hope you get it :)

You have me convinced, this is a superior strategy. Will look into it more closely.

steemit will succeed first of all because their founders are open-minded and accept others opinions and ideas if they are fairly reasonable ...

Allelujah! This thing seems likes it's mostly solve. We pretty much all knew something was wrong with curation reward or at least could be a lot better. It great to see you agree Dan. I hope this or something inspired out of it will be implemented soon.

@dantheman it's not good enough, as it encourages people to vote on already popular contents but not new contents. See the examples I posted around.


A simple way to fix it is to use a formula which actually makes a financial sense. When user votes for a post, an exchange happens: user contributes rshares, and gets weight in return. Later he receives a reward proportional to weight. Thus we can model it as a process of buying weight for rshares.

This is a brilliant idea and is way easier to market!!

That's how I always thought curation rewards worked. I guess have accumulated nothing...
Thanks for your effort! I hope it gets fixed.

Yes, I fear low-SP holders (i.e. most new users) might be disappointed with their curation payout 7/4 and leave. It is imperative we fix this asap!

Is there a chance that the system is designed this way in order to foster steem tokens Demand, that for an increased price against the $, e.g.?
I mean users have an incentive to power up, buying on exchanges or "mining" by setting up nodes.
I may be wrong, but this is how it looks like where I am standing.

Hi @killerstorm , this post is really helpful. Do you know if there have been any changes to the curation payout calculations since your analysis and proposal? I am trying to replicate some of the manually, but I'm running into some issues and I'm wondering if it's because the underlying logic has been updated. One issue is that the total_vote_weight value is not equal to the sum of the individual vote weights. For example, if you look at this random post:


You'll see that the total_vote_weight value is 17,372,172,123,681,100,000 but the sum of the weights associated with each vote is only 11,883,041,865,956,000,000. So total_vote_weight is almost 50% higher than the sum of the individual vote weights. The wgt% values on each vote appear to be using the total_vote_weight as the denominator, so the sum of all the wgt% values isn't 100% (as I think is reasonable to expect).

If anyone else votes on that post, or it goes past the 12h payout threshold, the numbers won't line up with this post, but any other <12h old post should have the same pattern.

Sorry if this is slightly off-topic, but this is the clearest post that I've found on the mechanics (and issues) with the curation rewards calculations. Any thoughts would be appreciated. Thanks!

After typing out that long comment, I've been looking at the numbers some more and think I've figured out the total_vote_weight issue. It looks like that total is scaled up by the author_curate_amount so that total_vote_weight = (sum of vote weights)/(1 - author_curate_amount). Then the numbers seem to work out. Hopefully this comment and reply can save someone else some time.

I'm still curious if you know of any changes to the curation calculations since this post ~2 months ago. Thanks again.

Curation reward formula was changed about a month ago, see here: https://steemit.com/steem/@dantheman/changes-to-curation-reward-allocation

It's a bit too thin on details, though.