Troubleshooting common challenges such as rounding errors, sending duplicate dividends, gas optimization, unfixed total supply, and more.

Recap: In the previous tutorials we build a burnable token, we also learned and build a capped token, and make that token time sensitive. To learn more, check out our previous tutorials. In this tutorial, we will build a dividend token.

Prerequisite:

  • Understanding of basic concepts of solidity
  • Understanding of ERC20 standard

What is dividend token?

A dividend is the distribution of reward from a portion of company’s earnings and is paid to a class of its shareholders. So a token which pays out profits to its investor as dividends is a dividend token. There are multiple ways to pay the dividend in the token economy. Today we will discuss how to build this feature into a smart contract.

Why use dividend Token?

Dividends are standard practice to distribute earnings to investors. Dividends also create a passive income source for your investors. This will attract long-term investors and create incentives for holding tokens.

Problems we tackle in our Dividend token contract

We will tackle below main problems which we usually face while building a dividend token.

  • Rounding error problem — When we work with multiple account transfers and divide dividends, there can be a case where we lose tokens while dividing for investors. This will be solved using a bigger multiplier so we don’t get fraction values.
  • Getting a dividend multiple times— We also need to take care that someone can’t get a dividend more than one time. This case can happen when Bob gets the dividend on his tokens, then transfer tokens to another account and gets dividends again. This will be solved updating dividends for sender and receivers before any transfer.
  • **Gas optimization **— There are multiple ways to update dividends for investors, one way is to write a loop and update dividends to investors account but this design has a problem to run out of gas. We can optimize it by using a different design introduced by Nick Johnson.
  • Unfixed Total Supply — We will also create a token where total supply is not fixed and get increased when dividends get paid.

DividendToken.sol

Our code:

                                                                Source

Now let's breakdown our dividend Token code.

string public name = "Dividend Token";  
string public symbol = "DIV";  
uint8 public decimals = 0;    
uint256 public totalSupply\_ = 1000000;  
uint256 totalDividendPoints = 0;  
uint256 unclaimedDividends = 0;  
uint256 pointMultiplier = 1000000000000000000;  
address owner;  
  
struct account{  
     uint256 balance;  
     uint256 lastDividendPoints;  
}

mapping(address => account) public balanceOf;
  • name — Name of our toke
  • symbol — Our token symbol
  • decimal — We are using 0 decimal for simplicity
  • totalSupply_ — Total supply of our token
  • totalDividentPoints — Total dividend which is given till now
  • unclaimedDividends — Track unclaimed dividends.
  • pointMultiplier — 10¹⁸ as point multiplier to tackle rounding errors
  • owner — Owner address of our smart contract
  • account — Structure using to track balance and dividends
  • balanceOf — Mapping for address and account(above structure) to track accounts.

Dividend Logic

Now let's dive into our main dividend logic.

modifier updateDividend(address investor) {
    uint256 owing = dividendsOwing(investor);
    if(owing > 0) {
        unclaimedDividends = unclaimedDividends.sub(owing);
        balanceOf[investor].balance =             balanceOf[investor].balance.add(owing);
        balanceOf[investor].lastDividendPoints =  totalDividendPoints;
        }
     _;
    }

function dividendsOwing(address investor) internal returns(uint256){
       uint256 newDividendPoints = totalDividendPoints.sub(balanceOf[investor].lastDividendPoints);
        return (balanceOf[investor].balance.mul(newDividendPoints)).div(pointMultiplier);
 }

function disburse(uint256 amount) onlyOwner public {
    totalDividendPoints = totalDividendPoints.add((amount.mul(pointMultiplier)).div(totalSupply_));
    totalSupply_ = totalSupply_.add(amount);
    unclaimedDividends =  unclaimedDividends.add(amount);
 }


updateDividend — This will calculate dividends owed by an account. It will call dividendsOwing method, which we will see in a minute. After finding out what dividends owe to an account, we will update our unclaimedDividends variable and then we will update investor’s account balance and lastDividendPoints.

**_We will use this modifier with every transfer method for both sender and receiver._**

dividendsOwing — This function has our main logic to calculate dividends. it will calculate dividends using the following logic.

new dividend = totalDividendPoints - investor's lastDividnedPoint
 
investor's dividend = ( investor's balance * new dividend ) / points multiplier

You can see we are calculating dividends based on investor’s balance. This function will only be called by the contract owner.

disburse — This function will be called to pay dividends to the contract which will increase totalDividendPoints , totalSupply_ and unclaimedDividends. We are using pointmultiplier (10¹⁸) to steer clear of a rounding error.

totalDividendPoints += (amount * pointMultiplier ) / totalSupply_

The Basic Formula for the dividend for an investor according to his/her balance is-

investor's dividend = Total Dividend / investor’s balance

Other parts of the contract are implementing with standard ERC20 methods. Which we will not discuss in this tutorial.

Testing Dividend Contract

Now let’s write a test case where we will see if above is working properly.

it('dividend Test' , async() => { 
  await this.tokenInstance.transfer(web3.eth.accounts[1],100000, {from : web3.eth.accounts[0]});
  await this.tokenInstance.disburse(100000);
  await this.tokenInstance.transfer(web3.eth.accounts[2],100000, {from : web3.eth.accounts[1]});
  const investor_1_balance = await this.tokenInstance.balanceOf(web3.eth.accounts[1]);
  const investor_2_balance = await this.tokenInstance.balanceOf(web3.eth.accounts[2]);
  const totalSupply = await this.tokenInstance.totalSupply_();
  assert.equal( totalSupply, 1100000);
  assert.equal(investor_1_balance, 10000);
  assert.equal(investor_2_balance, 100000); 
 })

Below are the steps which are getting performed by the test case.

  • investor_0 → 100000 tokens → investor_1
  • 100000 tokens were given as dividends to the contract
  • investor_1 → 100000 tokens → investor_2
  • investor_1 balance should be 10000 tokens (This is the dividend amount because the account has 100000 tokens when the dividend was given)
  • investor_2 balance should be 100000 tokens (account should not have dividends tokens because there were no tokens when the dividend was given)
  • Total supply should increase to 1100000 (after getting 100000 tokens as a dividend)

Conclusion

So, today we created a dividend token. We learned how to tackle a rounding error problem and a multiple dividend problem. There are multiple ways to create dividends for your investors in the token economy. Proof of stake protocol also has similar properties, which we will discuss in future.

Notes & suggestions-

Do not use this code in production, this code is for educational purpose. If you don’t understand something or want to learn something else let us know in comments. You can view full code in my GitHub repository.