1

Apologies for the title. I'm not sure what would be a more descriptive way to name this question.

Assume there is a contract which has 3 potential parties. Buyers, sellers and shareholders. Buyers/sellers trade with the contract and shareholders earn 1% fees on such trades.

Let's say I bought 100 shares in the contract which then sells $1000 worth of tokens to someone. I now have a claim to $10 (1000*0.01) which were accounted for as fees.

Sound simple. But the problem is that anyone after me can purchase shares as well which will dilute my ownership.

Given the following scenario:

  • If I buy 100 shares in the contract
  • The contract sells to someone tokens for $1,000
  • I now have a claim to $10 (1000*0.01)
  • Someone else buys an additional 100 shares, diluting my ownership to just 50%
  • The contract sells more tokens for another $1,000. But out of this sale, I only have a claim to $5.
  • My final claim is now $10 + $5.

So the question is: since my ownership percentage can be different on each trade, how can the contract calculate my final balance after say 10,000 different trades in all of which I could have had a different ownership percentage?

There could be tens of thousands of shareholders and hundreds of thousands of trades. So updating balances for each shareholder on each trade event would be too costly.

Is there a cost-effective way to calculate my final balance? Or is the entire logic flawed and something like this is not possible?

0

In these cases (time variant share) there is one and one only strategy which works: to accumulate the fee for each shareholder on a single transaction basis, that is each time a fee is generated, it is accounted properly to the relevant shareholders in that moment. The next transaction shall be calculated on the basis of the new shares asset resulting from the previous transactions finalized.

If this is too much costly, you must have bigger granularity, giving constraints by law to the users. For instance that no less than 1000 shares can be moved, or that the transactions effects to the revenue calculation apply at a given time point only (every seven days?) Or whatever.

In such simplified hypothesis you can try to find some cheaper models able to represent the situation properly. Generally speaking this way you create “classes” of user and/or of transactions and can try to use batch rules for each classis.

As far as I know, a third way does not exists.

0

In your description, the investors will hold shares that are depreciated when new shares are bought which is a bit odd. (This is what happens in the EthPyramid contract- I think so)

Instead, you may specify the maximum number of shares at the beginning. This means that your system owns all the shares at the beginning.

Under that view, the calculation of the dividends becomes easy. Assume that out of 1000 shares a user A buys 100 (10%) and is the only share holder, every time a purchase is made the 90% of the 1% fee goes to the system and the rest goes to the user holding the 10%, If a new user B buys 100 shares then the first user does not see his/her investment depreciated as he continues to get 10% of the fee, the system gets now 80% (because the system sold 100 of its shares).

Your balances can be done with a mapping of structs:

struct userBalance{
    uint256 shares;
    uint256 prevBalance;
    uint256 prevTokens;
}

mapping(address => userBalance) balances;
uint256 totalShares;
uint256 tokensSold;

When a user increases his shares you can save the earning with the previous number of shares and store this information in prevBalance and prevTokens.

The advantages are that you do not need to calculate the profits of all the shareholders every time and calculating the dividents of a shareholder is also simple.

Hope this helps

  • The description doesn't hold the whole story since I didn't think it was relevant. When investors buy a share, they are technically supplying liquidity to the contract. The more liquidity (ETH), the more useful it becomes to traders. Each "share" is simply an accounting unit which represents how much liquidity they deposited. – Sergey Dec 30 '18 at 2:57
0

This is doable.

The implementation details will turn some assumptions upside down. You have identified the high cost of a brute force approach. This is a valid concern but it can be addressed.

There could be tens of thousands of shareholders and hundreds of thousands of trades. So updating balances for each shareholder on each trade event would be too costly.

Let us assume it's a standard ERC20 contract. There is a balanceOf(address) function that returns a uint. The interface standard is silent on the method of calculation. Given an initial state and a history of all the trades that have taken place it should be possible to compute a shareholder balance as the sum of the initial (stored) state and the dividends received since the state was last updated.

Iteration to process dividends could potentially exceed the block gas limit, so the state needs to be brought up-to-date periodically. In theory, this can be done in batches and the shareholder can be persuaded to pay for the gas required to do so. An opportune time to perform such housekeeping is any time they agree to pay for gas such as when they do a transfer() but there is no prohibition on getting them to trigger it for its own sake from time to time. It can be largely migrated to a UI/UX concern.

As transactions move through the transfer() function, you'll need to keep track of the sums that are vital to computing entitlements. Those will things like the total shares issued at that time (which gives the % for any given shareholder). You'll move the 1% into an administrative account.

  • Alice has 100 shares.
  • $1,000 was transferred from Bob to Carol, while 1,000 shares were issued.
  • Given the rule, Bob is debited 1,000 and Carol receives 9,990. 10 go in the admin account.
  • This is an opportunity to perform housekeeping on both Bob and Carol, and Bob might be persuaded to bang on the contract as many times as it takes to clean house so the system will permit the transaction to go through. Consider a new function (it's allowed) to perform housekeeping(address account)
  • The housekeeping function would move funds held in the admin account to the shareholder. It always exactly equals the unprocessed distributions to all shareholders who's stored balances are always a little bit behind, and to different degrees.

Keep in mind that read-only operations do not cost gas so as long as they complete within the gasLimit, the system can chug along and correctly report shareholder balances without updated account states.

A function that computes the net position of an account would crawl the unprocessed transactions to compute it. You would embed it in the balanceOf() function and arrange things so that if someone (anyone) is paying for gas, then store the result.

You will be able to optimize the process by grouping both the transactions and the shareholders. For example, shareholders commence receiving starting at the next cutoff time rather than immediately. Transactions grouped into periods like, say, 24 hours, would mean the same process could be applied to daily summaries rather than individual transactions, and this would reduce the number of iterations required, which is what you want. In case that isn't clear, it's the difference between "Alice is 23,000 transactions behind" and Alice is "7 days behind."

Importantly, every time Alice sends or receives, someone pays for the gas to update her stored balance so she is no longer behind. The idea here is to make it a rare case when any sort of manual intervention is needed. Obviously, Alice (and others) need a possibility to catch her up in steps in the case that she is so far behind it can no longer be computed in a single transaction.

You might think about minimum transaction size where these calculations should even apply, the appropriate duration of the accounting periods, the rate and whether those parameters should be changeable over time, and by whom. I'm not sure this ad-hoc description adequately explains the on-the-fly balance computations but hopefully it gives you a new way to consider it.

Hope it helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.