Calculating value of a vote in Javascript

VoteValues.png

I had expected to make more progress on my offchain content tool, but what I thought would be a quick little task got me bogged down for a while: calculating how much an individual's vote contributes to a post's rewards. I want to make sure to limit use of the tool to people who have a big enough vote to actually get rewards for the post even if nobody else votes for it, so I need to know how much an individual's vote is worth. And rather than relying on the folk wisdom of how to calculate that, I figured it would be beneficial for me to figure out the real process myself by digging through the blockchain's C++ code.

I found the relevant C++ code pretty hard to read, partly for understandable reasons (the code needs to be explicit about using some things in database data structures), partly due to bloat from a single codebase supporting all the hardforks (there are lots of "if hardfork X then do A, otherwise do B" blocks of code), partly because C++ tends to invite abstractions and spreading things across multiple files, and partly due to the way some of the particular code was written. But I eventually figured things out and now have a pretty good understanding of how vests, mana, rshares, the reward pool, and the "convergent linear" reward curve influence the value of a vote.

I decided to write up a little proof-of-concept script in Node.js to sanity check everything, which shows the value of various "sea creature" levels and how much their vote would be worth if it was the only vote on a post (the convergent linear curve works on the sum of all votes, not each individual vote, but if only one person votes then they're the same thing).

const dsteem = require('dsteem');
const client = new dsteem.Client('https://api.steemit.com');

var creatures = [{name: 'Redfish', vests:0, power:1},
                 {name: 'Minnow', vests:1000000, power:1},
                 {name: 'Dolphin', vests:10000000, power:1},
                 {name: 'Orca', vests:100000000, power:1},
                 {name: 'Whale', vests:1000000000, power:1}];

const accountsToQuery = ['danmaruschak', 'steemcurator01'];

// do api calls to get info
var dgp = client.database.getDynamicGlobalProperties();
var conf = client.database.getConfig();
var medianPrice = client.database.getCurrentMedianHistoryPrice();
var rewardFund = client.database.call('get_reward_fund', ['post']);
var account = client.database.getAccounts(accountsToQuery);

// wait until we have responses for all the API calls.
Promise.all([dgp, conf, medianPrice, rewardFund, account])
.then(([dgpResp, cfgResp, medianPriceResp, rewardFundResp, accountResp]) => {
  // biggest vote size = (# of seconds in 1 day) /
  //                     (steady state votes per day)*(time to regen 100% of mana)
  // should be 2% with current values.
  var fractionForMaxVote = (60 * 60 * 24) /
                           (dgpResp.vote_power_reserve_rate *
                            cfgResp.STEEM_VOTING_MANA_REGENERATION_SECONDS);

  // constant for "convergent linear" rewards curve
  var curveConstant = parseInt(rewardFundResp.content_constant);

  // calculate how much 1 rshare of the reward pool is worth.
  var steemInRewardPool = parseFloat(rewardFundResp.reward_balance.split(" ")[0]);
  var rsharesInRewardPool = parseInt(rewardFundResp.recent_claims);
  var steemPerRshare = steemInRewardPool / rsharesInRewardPool;

  // calculate "current" price of Steem
  var sbdInPrice = parseFloat(medianPriceResp.base.amount);
  var steemInPrice = parseFloat(medianPriceResp.quote.amount);
  // Price is num of SBDs for 1 steem. N SBDs = M STEEMs, divide both sides by M
  var currentMedianPrice = sbdInPrice / steemInPrice;

  // get information about specific accounts
  accountResp.forEach((acct) => {
    var effVests = parseFloat(acct.vesting_shares.split(" ")[0])
                 - parseFloat(acct.delegated_vesting_shares.split(" ")[0])
                 + parseFloat(acct.received_vesting_shares.split(" ")[0]);
    var maxMana = effVests * 1000000; // mana is on the scale of microvests
    var curTime = Math.floor(Date.now()/1000); // seconds since the epoch
    var secondsSinceManaUpdate = curTime - acct.voting_manabar.last_update_time;
    var manaGrowthRate = maxMana / cfgResp.STEEM_VOTING_MANA_REGENERATION_SECONDS;
    // can't gain more mana than your max.
    var curMana = Math.min(maxMana, parseInt(acct.voting_manabar.current_mana)
                                    + secondsSinceManaUpdate*manaGrowthRate);
    // put this info onto the list we'll present as a table.
    creatures.push({name: acct.name, vests:effVests, power:curMana/maxMana});
  });

  // output out a markdown table header
  console.log("| Name | Vests | SP | Mana % | Linear vote | Curved vote |");
  console.log("|:-----|------:|---:|-------:|------------:|------------:|");

  creatures.forEach((item, i) => {
    // convert vests into SteemPower
    var spPerVest = parseFloat(dgpResp.total_vesting_fund_steem.split(" ")[0]) /
                    parseFloat(dgpResp.total_vesting_shares.split(" ")[0]);
    var sp = Math.floor(item.vests*spPerVest);

    // rshares is based on microVests, 1,000,000 microVests per vest
    var rShares = item.vests * 1000000 * fractionForMaxVote;
    // apply "convergent linear" reward curve.
    var convergentRshares = ((rShares + curveConstant)**2 - (curveConstant**2)) /
                            (rShares + 4*curveConstant);
    var linearVote = rShares * steemPerRshare * currentMedianPrice;
    var curvedVote = convergentRshares * steemPerRshare * currentMedianPrice;

    // output a markdown table row
    console.log("|", item.name, "|", Math.floor(item.vests), "|", sp,
                "|", (item.power*100).toFixed(2)+"%",
                "|", "$"+linearVote.toFixed(3), "|", "$"+curvedVote.toFixed(3), "|");
  });
});

And here's the output of the script:

NameVestsSPMana %Linear voteCurved vote
Redfish00100.00%$0.000$0.000
Minnow1000000555100.00%$0.008$0.004
Dolphin100000005553100.00%$0.078$0.040
Orca10000000055533100.00%$0.777$0.466
Whale1000000000555332100.00%$7.766$6.656
danmaruschak13133576729399.73%$0.102$0.053
steemcurator01200120892371111335797.98%$155.404$153.882

Coin Marketplace

STEEM 0.30
TRX 0.12
JST 0.032
BTC 61227.60
ETH 3022.96
USDT 1.00
SBD 3.88