HQ20 / contracts

A set of reusable smart-contracts

Home Page:https://hq20-contracts.netlify.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Include votes in `DAO.sol` for distributing dividends

alcueca opened this issue · comments

Now that we have a Voting contract, I would like to add two corporate actions to DAO.sol

  1. Dividends from the invested ventures now get added to the DAO investment pool. A vote can be taken to distribute ether dividends from the investment pool to DAO stakeholders.
  2. A vote should be allowed to allow further funding rounds in the DAO at a given price.

Now, I agree that point 2. is needed and feasible, but point 1. is definitely unfeasible, but without that having anything to do with voting. The mechanism of the DAO is that of the ERC20DividendableEth, where each stakeholder updates his account by his initiative because having the DAO distribute the dividends would be problematic from a gas usage perspective. Do you have another solution in mind, or perhaps you were just mistaken with this?

DAO is an ERC20DividendableEth, and so are the ventures it invests in.

The DAO invests in Ventures, and when the Ventures distribute dividends, the DAO collects them (using venture.updateAccount(this)) and adds them to the investment pool (just by increasing investmentPool by the amount of the claimed dividends).

A Voting can be done so that the DAO distributes dividends to investors. The voting would be to call dao.increasePool() (sending an ether amount to itself, which is a bit weird, we might need to refactor instead), decreasing investmentPool accordingly. After that DAO investors would have to call dao.updateAccount(investor) to claim their dividends.

By the way, what happens with the venture dividends currently? They seem to just accumulate in DAO.sol.

Does this make sense?

Alright, well, first, there is no such thing as an investmentPool in the DAO. It's just a left-over var that I really should have deleted (will do that in the following PR). Second, there is no call to increasePool() simply because it is a payable (external) function, and hence, you cannot call it from the same contract. What there is, instead, is a variation of the content of increasePool() inside profitFromVenture(). And, finally, third, the venture dividends accumulate in the venture.
So, no, it still doesn't make sense to me.

The profits from the ventures seem to go to DAO.sol:

    /**
     * @notice Profit from an investment by claiming dividends for the DAO on the venture.
     * @param venture The address of the VentureEth contract to profit from.
     */
    function profitFromVenture(address venture) public {
        totalDividends = totalDividends.add(
            VentureEth(venture).updateAccount(address(this))
        );
        totalDividendPoints = totalDividends
            .mul(pointMultiplier).div(this.totalSupply());
    }

DAO.sol inherits from ERC20Dividendable.sol, so this function could be implemented:

    function releaseDividends(uint256 amount) {
        totalDividends = totalDividends.add(amount);
        totalDividendPoints = totalDividends
            .mul(pointMultiplier).div(this.totalSupply());
    }

That function is exactly the same as increasePool. Anyway, what does that have to do with the dividend distribution scheme? So, if I get it correctly, you don't want the DAO to be able to disburse the dividends to all its shareholders through a Voting, all that you want is to have the DAO profit from the ventures (that is, to increase the pool) through a Voting, right?

It's exactly the same except it's not external payable. It assumes that the contract already has some ether to distribute as dividends.

The DAO already profits from the ventures (via profitFromVenture). So the DAO has ether that didn't come from the stakeholders. It has profits to either reinvest (funding more ventures through Votings) or distribute.

What I want is exactly the opposite of what you said:

I want for the DAO to be able to disburse the dividends to all its shareholders through a Voting. The DAO calls

        Voting voting = new Voting(address(this), threshold);
        voting.registerProposal(
            address(this),
            abi.encodeWithSignature("releaseDividends(uint256)", amount)
        );

And the DAO stakeholders would call dao.updateAccount(msg.sender).

Alberto, your proposal above is the exact same way the things work now, with except for a Voting when profiting from the ventures (which is what I said you want and you said you want the exact opposite). I guess I really don't get what you want.

  1. Right now, ether profits from the ventures accumulate in DAO.sol (via profitFromVenture).
  2. Right now, DAO stakeholders receive no ether dividends because no one calls dao.increasePool() with an ether amount.

Right?

  1. Right.
  2. Wrong! An investor can just call dao.updateAccount(myself). The increasePool functionality, as stated above, is included in profitFromVenture. Just take a look at profitFromVenture and be made full.

Aaaah, I see:

        totalDividendPoints = totalDividends
            .mul(pointMultiplier).div(this.totalSupply());

So currently, each time that the dao profits from a venture, all collected dividends are marked for distribution to dao stakeholders. That line could use a comment.

Cool, so what I want to do is to remove that line from profitFromVenture, and subject dividend distributions to a Voting instead.

That way, dao stakeholders decide what to do with the venture dividends:
a. Invest them in more ventures.
b. Distribute them as dividends to dao stakeholders.

Ok, now I get it. I'll start working on the PR.

I knew you would get it 🤸‍♂

Suppose the following scenario:

  1. dao votes to fund a venture
  2. dao gets tokens from venture
  3. dao gets profits from tokens of venture
  4. dao disburses dividends.
  5. dao votes to fund a venture
  6. dao gets tokens from venture
  7. dao gets profits from tokens of venture
  8. dao raises funds from new investors
  9. dao disburses dividends
    Now, this:
#### Notes 
1. Changes in the token supply will affect any dividend distribution events. Any ongoing distribution events for which the contract has received the `ether` before the total supply change are unaffected.

would not hold true anymore for the second disbursment, would it?

Can't we just have profitFromVenture subject to Voting? Why do we have to break it down?

I think we need to do some renaming in ERC20DividendableEth at least. It's a bit confusing what each variable is:
totalDividendPoints -> totalDividendsPerToken
lastDividendPoints -> claimedDividends

Would that be right?

No, it wouldn't. The names I think are fine. totalDividendPoints is just that: the points representing the total dividends now. They are not dividends per se, or tokens, they are just points.

They indeed are the ether per token that should be distributed as dividends. And in trying to prove it I found two bugs in the original code 👍

Check this scenario. Round 1:
Supply: 100 tokens
IncreasePool: 10 ether
TotalDividends: 10 (ether)
TotalDividendsPerToken: 10/100 = 0.1 (ether per token)

That's right, if we have 100 tokens and distribute 10 ether, each stakeholder has the right to 0.1 ether. Next round, let's distribute another 10 ether:

Round 2
Supply: 100 tokens
IncreasePool: 10 ether
TotalDividends: 10 + 10 (ether)
TotalDividendsPerToken: 20/100 = 0.2 (ether per token)

Still right, each stakeholder should have got by now 0.2 ether per token. If they claimed after the first round and got 0.1 per token then, when they claim now they will get 0.1 ether per token again, still right.

Let's do another round, but this time we mint and additional 200 tokens, before distributing another 10 ether.

Round 3 (wrong)
Supply: 300 tokens
IncreasePool: 10 ether
TotalDividends: 20 + 10 (ether)
TotalDividendsPerToken: 30/200 = 0.1 (ether per token)

That wouldn't be right. It would mean stakeholders that were in the first funding round can't get dividends until TotalDividendsPerToken crosses the 0.2 mark again.

Fixing the first bug: However, if we modify mint to modify TotalDividends as in TokenDividends = TokenDividends*(newTotalSupply/oldTotalSupply) things would match:

Round 3(right)
Supply: 300 tokens
IncreasePool: 10 ether
TotalDividends: (20*300/100) + 10 (ether)
TotalDividendsPerToken: 70 / 300 = 0.2333... (ether per token)

That is right, people that already had tokens now get a third of the etherPerToken that they used to get (now 10 ether will give them only 0.03333 ether per token instead of 0.1).

Fixing the second bug: When minting straight into an account with 0 balance, their lastDividendPoints (now claimedDividends) should become totalDividendsPerToken.
When doing a transfer both the sender and the receiver should call updateAccount first.

I'll open an issue to fix ERC20DividendableEth.

Let's fix ERC20DividendableEth first, then we continue with this.

Can't we just have profitFromVenture subject to Voting? Why do we have to break it down?

Because the decision to distribute dividends and how much to distribute should be independent from what the ventures do. In an extreme case, the DAO stakeholders might want to liquidate all the venture portfolio, release all the ether as dividends, and with that liquidate the fund.

In less extreme cases, the DAO stakeholders would just look at the funds at the DAO, and decide how much to pay themselves (as dividends that are first released, and then claimed).

Now that there is an internal releaseDividends(amount) function for ERC20DividendableEth you can continue with the implementation of this. It should be a plain vote on whether to execute releaseDividends(amount).

How can this be, if ERC20DividendableEth cannot be fixed until we migrate to solidity 0.6? I remind you, ERC20DividendableETh, as it is on the fix/erc20-dividends branch, does not pass tests. One of the tests fails for unknown reasons, the other, because of this line:

uint256 differentialDPT = lastDPT[sender]
            .subd(lastDPT[recipient]);

which overflows in most of the cases (this was the .abs() issue).
On the other side, would it be ok to use ERC20DividendableEth as it is? I just wanted to know your informed opinion, after all, the releaseDividends part is working outside of the token transferring context.

You can resolve this issue working from master. As you said all that we needed was for releaseDividends(amount) to work.

We will fix transfer of dividends after we migrate to solidity 0.6.