The first 5 days of the EOS token sale will be unfair. Here's how to fix it.

in #eos8 years ago (edited)

EDIT: This post is for informational/educational purposes; my proposal is apparently not implementable on Ethereum due to the need to sort the list of bids.

Ever since the EOS token sale was announced yesterday, there has been much hand-wringing and off-the-cuff analysis. I wrote an article with a few of my first thoughts, and I haven't been able to get the sale out of my head since.


In my article, I wrote the following:

The fact that the optimal bidding strategy is not obvious means that whoever gets the game theory right is going to make a killing off this crowdsale. You should ask the team why they think a weird non-traditional auction is fair.

In this article, I'll argue that the distribution model in the first 5 days is, in fact, unfair.

Let's say Alice is a hyper-bull on EOS, and thinks it's going to hit $10,000 someday. As far as she's concerned, any ICO price is a good price.

On the other hand, Bob believes the long-term potential is wonderful and could easily see $100 per token someday, but he's confident that it's going to take a while to get there. He suspects the crypto bubble will probably burst in the next couple months as the ICO calendar thins out, and thinks EOS could easily fall to $0.10 during the crash before the chain is actually launched. This leads him to say that $1 per token is the most he'd want to pay in the token sale. Any higher price than that and he'll wait for greener pastures down the line.

So Alice has no maximum price, but Bob does: Bob doesn't want to pay more than $1 per token in the first 5-day period of the sale.

What are Alice and Bob's optimal contribution strategies?

For Alice, it's easy. She just contributes ETH. Since she has no maximum price, she doesn't care how much ETH gets pushed into the sale. She'll take whatever she can get at any price.

For Bob, the optimal strategy is completely non-obvious. Let's see why. The way the auction works, everybody who contributes in the first 5 days gets the same price. If the total amount of ETH contributed in that time is total_ETH_contributed, that price is simply

total_ETH_contributed / 200,000,000.

The more who contribute, the higher that price. But Bob has a maximum price that he's willing to contribute for! How can he be sure he doesn't pay more than his maximum price?

Bob cannot be sure that he'll pay less than his maximum price.

It's mathematically impossible for him to be sure. The only way he'd know for sure is if he could somehow wait until after everybody else has bid, check to make sure that the implied price is below his max, and then decide whether to contribute. But Bob's not special - and there are probably a lot of Bobs out there with a max price in mind who don't want to be caught with their pants down when all the other Bob's contribute right in the last few minutes of the sale.

The only way Bob can protect himself is to contribute less than he'd like to. So a side-effect of this auction format is that people should self-limit their contributions to mitigate the pricing uncertainty. I don't know how much FOMO will counteract this.

The auction format is unfair to Bob.

If you're a voluntaryist/anarchist/believer in the non-aggression principle, pay close attention to this: Subsequent bidders can force Bob to pay more than he's willing to pay.

How do we fix this?

Fortunately, there's an easy fix. We give Bob the ability to set a maximum price. It's so easy it hurts. We're running the token sale on Ethereum, the smart contract platform, so this should actually be possible to do.

In my proposed format (for those who care, it's a Simulated Descending Price Auction), people submit a bid as a pair of two numbers:

  1. Amount of ETH they're contributing
  2. Maximum ETH/EOS price they're willing to pay

At the end of the 5-day period, we sort all the bids in descending order of max price. We iterate through the bids, calculate an implied price based on the total ETH seen so far (divided by EOS to be minted), and increment the iteration whenever the implied price is less than the most-recently-seen maximum price. If the implied price is ever greater than the current maximum price bid, the iteration terminates and all orders seen so far are filled at the implied price. Everybody receives a price no greater than their maximum price, and everybody who entered a max price less than the final price is happy their order didn't get filled.

The algorithm terminates in finite time with a deterministic outcome because the list of bids is finite, the implied price is an increasing sequence, and the maximum prices are sorted descending.

Why is this important?

It's important because now Bob has total control over protecting his maximum price. Now, nobody can force him to pay a higher price than he wanted to. Now, he has no uncertainty about what his optimal bidding strategy should be. Now, he doesn't have to stay up till 3 AM when the 5-day period ends in his time zone to make sure that he's not paying too much.

Nothing has changed for our hyper-bull Alice! She can just enter an obscene max price, or we can design the smart contract so that she can enter a "no-max" option where she is guaranteed to get part of the distribution.

Caveat: implementability

I don't know Solidity, and it's possible that sorting the list of bids could be annoying/challenging/costly. Need dev comments on that one.

Sort:  

Unfortunately ethereum cannot implement this algorithm due to gas constraints and lack of sorted database.

What about the following implementation? Keep in mind, I haven't spent a lot of time fully thinking through everything and there could be some errors or other problems (dealing with Ethereum performance limitations making this too costly) with this design.

Each bid identified uniquely by the address of sender and bid period sequence number (first 5 day period counts as bid period sequence 0, and the remaining days just increment that sequence number) has the following state associated with it: bid_id, eth_amount, time, limit_price, eth_filled. For each bid period, a bid ID sequence counter is maintained. When creating a new bid, that sequence number is incremented and used as the bid_id of the new bid, and the eth_filled value is set to 0. A user can add or remove ether from their bid before the end of that bid period; such modifications only change the eth_amount and time (which is always updated to the current time when the bid is changed by the owner of the bid) but not any of the remaining fields. When creating a new bid, the smart contract also constructs an index: state consisting of the address of the bidder that is identified uniquely by the bid_id and bid period sequence number. The idea of that index is that the smart contract can lookup the state associated with a user's bid using the bid period sequence number and the bid_idthrough an indirect process which involves first using the bid period sequence number and the bid_id to get the address associated with that bid, and then using the address and the bid period sequence number to get access to the state associated with the bid.

When the bid period ends, it enters a new stage (stage 2) that will last for at most a day. During the stage all the bids are locked to their owners (they cannot add or remove ETH from them). After stage 2 finishes, the smart contract can either enter stage 3a or stage 3b depending on what happens in stage 2. If a special address (controlled by the managers of this crowdfund) is able to provide a complete proof (I will explain this shortly) to the smart contract that all bids were processed correctly in order of descending limit_price (and tie breakers done by ascending time) before the end of that 1 day deadline, then the smart contract enters stage 3a in which the owners of the bids that were "filled" can claim their appropriate amount of EOS tokens, while those whose bids were not "filled" can take back their ETH. If the crowdfund manager is not able to complete the proof by the deadline, the smart contract automatically enters stage 3b which basically allows all the bidders in that bid period to take back their ETH.

So how does this proof work? The crowdfund manager is authorized (during stage 2 of a particular bid period) to send certain transactions that will allow the smart contract to iterate through the bids of that bid period in a certain order. First, there is some state associated with each bid period. This state includes status, eth_accumulator, eth_collected, eth_to_keep, and last_processed_bid. During stage 1, each bid and bid update would also be tracking the total ETH collected for that bid period in eth_collected; the other fields would all be initialized to 0. Then in stage 2, the crowdfund manager can initiate the proof process with a special transaction that allows them to set the starting bid_id (which is what last_processed_bid is set to), and it also resets eth_accumulator and eth_to_keep to 0 and sets status to 1.

Then, the crowdfund manager uses another special transaction (only valid if status is either 1 or 2) which supplies a array of bid_ids which causes the smart contract to do the following. It uses last_processed_bid to get the limit_price and time of the corresponding bid, and then it iterates through the input array of bid_ids in order ensuring that limit_price and time of each processed bid follows the order requirements. As it processes each bid it will update last_processed_bid to bid_id of the processed bid and it will add their eth_amount to eth_accumulator. It will also add the eth_amount of the bid to eth_to_keep and set the bid's eth_filled value to the appropriate amount (usually equal to eth_amount but it could be less if the bid was only partially filled) if the bid's limit_price is large enough (based on EOS to distribute for that bid period and the accumulated eth_to_keep); once the limit_price threshold has been passed, it will set status to 2 so that it can use a shortcut and avoid calculating the price to compare with limit_price for the remaining bids to be processed since it knows that none of them will be filled. Finally, at the end of the transaction, the smart contract will check to see if eth_accumulator == eth_collected. If so, that will mark the transition to stage 3a by setting status to 3.

In stage 3a, any bid that was fully "filled" (meaning eth_filled == eth_amount) can claim the appropriate amount of EOS. But if they were not filled, then the owner of that bid can simply reclaim their ETH from the bid. At most one of the bids in a bid period may be partially filled (eth_filled < eth_amount); for this bid, the owner can claim the appropriate amount of EOS based on the eth_filled amount and can retrieve the remaining eth_amount - eth_filled amount of ETH back.

So, the idea is to have the expensive sorting be done off-chain by the crowdfund manager, and then to have them supply that sequence via a special operation only they are allowed to use in stage 2. The smart contract will ensure that the crowdfund manager cannot cheat by using an invalid order or leaving off certain bids. The only thing the crowdfund manager can do is refuse to complete the proof in time (within a day), which would be as if that bid period never happened (the smart contract could automatically extend the number of bid periods to try again and ensure 360 1-day bid periods do in fact occur). Also, this design provides a lot of flexibility to the crowdfund manager in providing the bid sequence proof. First, it allows them to break up the proof into multiple transactions so that there isn't an issue of a single transaction which provides the entire sequence of bids for the bid period being too computationally demanding to be accepted by the Ethereum network. Second, it is forgiving. If for some reason the crowdfund manager messes up by leaving off a bid (although I don't see why this should happen in the first place since they would be using bots to do it), they aren't forced to basically allow the entire bid period to become void; they can simply use the special transaction to re-initiate the proof process and start the proof over again.

I was afraid of that, and just before you commented I had updated my post to mention that possible problem.

I guess to implement it you'd need to do a literal descending price auction a la Gnosis, which I wouldn't be so quick to support.

Thanks for the quick answer.

hey @biophil, just noticed this on github and posted about it in the new EOS slack. thought you may also very much appreciate hearing about it! :D

wow that's great I was just looking more into how a buy limit and even cancels could be implemented and it seems you guys are already in the process of adding it in a recent pull request! from what I've been reading, I was also coming to the conclusion that "mapping" and "memory" were the best "tricks" to help get there... buyWithLimit pullreq .. https://github.com/EOSIO/eos-token-sale/commit/e9280da6a34bf571e0122c4c7ad47c5c599490e6

Actually, just wrote a post about it too: "EOS Token Sale Smart Contract May Offer BUY LIMITS after all!"

This example by Vitalik Buterin is from 2014, but he does show an implementation of a generic quicksort algorithm that can sort object pairs. Now, I'm not sure of the "gas constraints" involved, but with a sorted object pair it should be relatively straightforward to sort by "limit price", "database ID".

For added utility, we will make our sorting function generic: we will sort pairs instead of integers. Thus, for examples, [30, 1, 90, 2, 70, 3, 50, 4] would become [ 30, 1, 50, 4, 70, 3, 90, 2 ]. Using this function, one can sort a list containing any kind of object simply by making an array of pairs where the first number is the key to sort by and the second number is a pointer to the object in parent memory or storage.

Link: Advanced Contract Programming Example: SchellingCoin
Link: GitHub SchellingCoin implementation from Ethereum Repo

I admit I'm not well-versed in EC20 contracts or serpent/solidity, but technically, shouldn't we also be able to see all the current bidders and make an outside calculation of what the expected price would be before the window closes? If so, even better if someone could "cancel their order" before the window closes if they are displeased with the expected closing price.

Maybe for the first 5 days stage we can use regular servers, I mean without smart contract? We can launch a transparent site (ethereum blockchain explorer which is filtering one particular ETH address), so everyone could see all the bids. Then site would filter these bids as it shown in biophil's post. After 5 days stage is over, the smart contract starts.

BTW, if crowdsale will last for a year, when will EOS network itself start? After that year of eairlier?

Well, there ya go. Thanks for the quick answer. It would have been a great solution.

EOS has a future, that's for sure. But it will be long haul of atleast two years for investors to start making some revenues. I will start small time investments to the end of this year. Thank you for this insight!

This post received a 10% upvote from @randowhale thanks to @biophil! For more information, click here!

I think this is a great part of the solution and we should definitely use cryptocurrency as smart money. Thanks for writing this up, and I think the industry will only grow by leaps and bounds when programmable smart money is used to protect the 99%.

Meanwhile an ICO reminds me to celebrating an underground party; everyone is happy, having a good time, some drinks and meeting some awesome people but suddenly a fire breaks out and there is only one emergency exit and at once everyone is running to this exit at the same time. Those who are reaching the exit first will survive, those who won't are catching fire.

There are some parties you should be a part of and some you better don't.

After I watched the Veritaseum ICO and its price rising like madness in a few hours/days and nearly everyone's gone nuts, I said to myself... - wait, let them clean up the stage first; I'll be back later to stay in strong.
Remember: there are always two kinds of investors: those who only want to earn a quick buck, and those who want to hold for the long run! I belong to the boring ones - the long term holders, therefore I see no need for me to get in early, because prices are attached to supply and demand... - always; meaning when the quick ones sell, prices losing ground and then I'm in!

THNX for the great posting! Cheers!

Someone setup a testEOS token so we can discover the price now before EOS launches. :)

they are planning on a test run first on an ethereum testnet:

We will be conducting a 2 week public trial run of the sale on the Ethereum test network. We welcome anyone interested in learning how the sale works to participate in the trial. A follow up email will be sent with instructions on how to participate in this trial when we deploy the trial contract to the test network.

Link: Draft EOS Token Sale Smart Contract

Dam this implementation would have been so good for Bob's. Thanks for putting your idea forward to try and help the mass community.

I'll keep thinking about ways around it. Fairness is complicated when you're constrained to linear-time algorithms.

True, appreciate your ongoing efforts!

Great proposal. Would love to see this in test mode. I also wrote a blogpost on why the current ICO model could damage the future of EOS in terms of dApp developers: https://steemit.com/beyondbitcoin/@deanpress/the-eos-ico-one-major-concern
Your proposal might be a potential solution to the issue!

It takes money to make money ... or in this case, eos.

was wondering what the outcome would look for during ETH/EOS. thanx for being clear on this and what possible solution can there be.