Dividend Payment Contract based on MiniMeToken

Adam Dossa
4 min readAug 8, 2017

--

Distributing capital back to token holders in one form or another is a common part of many ICO white papers. I have been working with EthBits on exactly this type of functionality recently.

One popular approach is for companies to pay a series of dividends via a smart contract. These dividend paying contracts distribute these individual dividends to token holders proportional to their balances.

One key functional requirement is that dividends should be paid out in proportion to token balances as of the block number that the dividend was paid (or some other pre-determined block number), rather than current balances. Using current balances (which vary over time) would allow for the below simple attack:

  1. There are 100 DIV tokens.
  2. Alice has a token balance of 20 DIV tokens.
  3. Dividend of 1 ETH is made.
  4. Alice claims her 20% share of the dividend (0.2 ETH).
  5. Alice transfers all of her tokens to Bob, so Bob now has 20 DIV tokens.
  6. Bob claims his 20% share of the dividend (0.2 ETH). Alice’s original 20% share has now been claimed twice.

There is a similar issue with the token’s total supply if this can vary over time.

I’ve seen a few ways to implement the above behaviour and go through the Etheroll and E4Row approach below, then give a new method based on the MiniMeToken which I think works well and overcomes some of the limitations of other approaches and discuss some of its specific drawbacks.

The Etheroll Approach

Etheroll has an ERC20 token which has allows token transfers to be frozen.

function transfer(address _to, uint _value) public
returns (bool success)
{
if(tokensFrozen && msg.sender != priviledgedAddress) return false; /* transfer only by priviledgedAddress during crowdfund or reward phases */
...
}

Their approach for dividend payments is to freeze token transfers during payment periods. This ensures that token balances are fixed and can’t move during the period a dividend is eligible to be claimed.

This approach is safe and simple. The obvious disadvantage is that trading in the token is paused periodically which could be tough to coordinate with exchanges (the pause is enforced by the token contract, but I would imagine some coordinate is still required to avoid error messages on exchanges and so on) and irritating to users.

The E4Row Approach

In the blog post below E4Row outline how they use a payrunID variable to stop any account which has been involved in a token transfer after a pay run has begun for a given dividend, from claiming this dividend.

This approach works, but may also unfairly penalise users transferring tokens during pay runs who weren’t trying to game the system.

Using the MiniMeToken for Dividend Payments

MiniMeToken.sol is a cloneable ERC20 token written by Jordi Baylina. In order to support ”cloneability” without relying on explicitly initialising balances on the new cloned token (which would mean an unbounded loop), it has a clever set of data structures that support querying token balances, and total supply, as of specific block numbers.

The below contract uses this functionality to implement a fair contract for dividend payments, that in particular:

  1. Doesn’t require trading in the token to be paused.
  2. Allows tokens to be withdrawn at any time after a dividend was paid.
  3. Supports recycling old unclaimed portions of dividends into new dividends.
  4. Has both a pull and push model for dividend payments.

In essence the contract is pretty straightforward — it tracks dividends, the block numbers as of which they were paid and the tokens total supply at these block numbers, then uses the “balanceOfAt” API of the MiniMeToken to query correct historical balances when users request their portions of the dividend.

function claimDividend(uint256 _dividendIndex) public
validDividendIndex(_dividendIndex)
{
Dividend dividend = dividends[_dividendIndex];
require(dividend.claimed[msg.sender] == false);
require(dividend.recycled == false);
uint256 balance = miniMeToken.balanceOfAt(msg.sender, dividend.blockNumber);
uint256 claim = balance.mul(dividend.amount).div(dividend.totalSupply);
dividend.claimed[msg.sender] = true;
dividend.claimedAmount = SafeMath.add(dividend.claimedAmount, claim);
if (claim > 0) {
msg.sender.transfer(claim);
DividendClaimed(msg.sender, _dividendIndex, claim);
}
}

Recycling dividend payments works by simply marking an old dividend (old being specified via the “RECYCLE_TIME” constant) as having been recycled (which stops any further claims on it) and generating a new dividend for the appropriate amount. The new dividend can then be claimed by token holders in proportion to their balances at the block number the dividend was recycled. This avoids the common problem of some minority of token holders not claiming their portion of dividends and those unclaimed amounts effectively being burnt.

function recycleDividend(uint256 _dividendIndex) public
onlyOwner
validDividendIndex(_dividendIndex)
{
Dividend dividend = dividends[_dividendIndex];
require(dividend.recycled == false);
require(dividend.timestamp < SafeMath.sub(now, RECYCLE_TIME));
dividends[_dividendIndex].recycled = true;
uint256 currentSupply = miniMeToken.totalSupplyAt(block.number);
uint256 remainingAmount = SafeMath.sub(dividend.amount, dividend.claimedAmount);
uint256 dividendIndex = dividends.length;
dividends.push(
Dividend(
block.number,
now,
remainingAmount,
0,
currentSupply,
false
)
);
DividendRecycled(msg.sender, block.number, remainingAmount, currentSupply, dividendIndex);
}

The contract implements a pull model approach to dividend payments (the claimer always needs to call into the contract to claim their dividend). It provides a claimDividendsAll function which is meant as a helper to use in the common case. It is possible, if enough dividends were paid, that the computations needed for the loop in this function would exceed the block gas limit and so the function would become uncallable, however the claimDividend function would remain usable and so dividends would not be lost.

function claimDividendAll() public {
require(dividendsClaimed[msg.sender] < dividends.length);
for (uint i = dividendsClaimed[msg.sender]; i < dividends.length; i++) {
if ((dividends[i].claimed[msg.sender] == false) && (dividends[i].recycled == false)) {
dividendsClaimed[msg.sender] = SafeMath.add(i, 1);
claimDividend(i);
}
}
}

Some downsides to using the MiniMeToken is that maintaining a history of balances and total supplies requires additional calculations every time a transfer is made, or a token minted / destroyed.

Please let me know any comments or thoughts on the above — the full contract with some test cases (built in the truffle framework) can be found at:

https://github.com/adamdossa/ProfitSharingContract

--

--

Adam Dossa

Head of Technology at Polymath, Founder of Enclaves DEX, Smart Contract Auditor and FinTech / Machine Learning Hacker.