DDF is a dividend bearing ERC-20 token. Every quarter, 50% of net profits are distributed to investors.
Implementing Dividend Payments
A first, naive proposed solution to the payment problem is to have a smart contract that disburses funds to token holders. This is risky for two reasons:
- It could run into gas limits, or cases where the gas consumed is higher than the actual dividend.
- Since the payment would call the receive function on the destination wallet, it could invoke malicious code. This is in fact how the DAO hack happened.
For this reason, a better proposed design pattern is “Pull Over Push”:
Favor pull over push for external calls
As we’ve seen, external calls can fail for a number of reasons, including external errors and malicious Call Depth Attacks. To minimize the damage caused by such failures, it is often better to isolate each external call into its own transaction that can be initiated by the recipient of the call. This is especially relevant for payments, where it is better to let users withdraw funds rather than push funds to them automatically. (This also reduces the chance of problems with the gas limit.)
In a financial environment like the DDF operates in, a Pull solution has unfortunately drawbacks as well:
- The smart contract accumulates funds, potentially over a long term; this is a risk per se.
- If payees do not request payouts (“Pull”), the funds continue to accumulate.
This leads to long-term liabilities with unclaimed funds. Different countries have different regulations for unclaimed funds; these regulations are generally complicated to deal with.
We have chosen a different approach to handle dividend payments. We split the task into two parts:
- A smart contract that we call “Splitter Contract”.
- The actual payment model
The Splitter Contract is updated from the token contract whenever a token is transfer. It keeps an up-to-date list all token holders and the amount of tokens they hold.
To get a list of all token holders, the contract can be read by an external program to traverse through the list.
- The freeze() function is invoked. This locks the token holder list in time.
- With next() and previous() functions, the address can traverse the linked list to retrieve all data.
- The unfreeze() function resumes updates to the list.
While the list is frozen, no token transfers are added to it. They are added to a queue and then automatically applied once the list is unfrozen, so that the next payout has an up-to-date list.
Using the list that the Splitter Contract provides, the Payment Model is an off-chain program to issue payments. This has the advantage that the payment can be performed at a chosen time (for example, when there is low network latency) and paused/resumed as needed.
Since payments are handed normally from a designated dividend wallets, the entire payment process is auditable via the Blockchain.