Building the Interactive Token Sale

Joshua Hannan
Modular-Network
Published in
16 min readJan 16, 2018

Many high-profile ICOs launched last year amidst a swarm of criticism. The entire community was looking for a method to run token sales on Ethereum in a more fair and safe manner. To achieve these goals, Jason Teutsch and Vitalik Buterin authored the technical paper for the Interactive Token Sale protocol, which got a lot of attention. This was finally an ICO model that had the potential to give participants a guarantee of valuation and participation. Here are a couple different articles providing a rundown of how it works.

In this article, I want to walk you through how we at Modular, with the help of the great team at TrueBit and a few other amazing developers and thinkers in the community, built and optimized the first implementation of the protocol. I’ll also explore the challenges we faced along the way.

Prerequisites

Token sales in the past have faced many issues, most notably the tradeoffs between capped and uncapped sales. In a capped sale, there is a chance that some buyers will not get to the sale in time and be excluded. In an uncapped sale, the buyers have no idea what the total valuation will be and thus have no guarantee of what percentage of the total tokens they will be buying.

The interactive coin offering protocol aims to solve both of these issues by allowing the market to freely and openly decide the valuation through a series of bids that everyone is allowed to participate in.

The Interactive initial coin offering protocol (IICO) allows participants to submit a bid with what is called a personal valuation cap, in addition to the regular ETH payment for tokens. These bids ensure that the user gets a fair token purchase or refund based on their expectations for the sale valuation. Users also can withdraw their bids throughout the first stage of the sale for a partial refund.

Through these mechanisms, the market narrows the valuation of the sale to a value that all interested buyers are comfortable with. This value will have an effect on either the token purchase price or the total supply of tokens, depending on how the implementer configures the sale. If the implementers set a fixed number of total tokens available in the sale, then the resulting valuation determines the price of the tokens. If the implementers choose a fixed token price, the resulting valuation determines the total supply of tokens.

After the sale ends, the sale contract deploys the tokens automatically in order to ensure trustless execution. Token buyers are allocated their share of tokens based on the results of the sale.

ETH Contribution: The Ether that the participant sends to the sale with the expectation of receiving tokens in return. These ETH payments are summed throughout the sale to equal the total sale valuation.

Total Sale Valuation: The sum of all the active bids in the sale or total amount of ETH raised, not to be confused with the total valuation of the token or project itself, as the sale will only handle a percentage of the total tokens minted. This will fluctuate throughout the sale until the end, when it will reach an equilibrium determined by the participants.

Personal valuation cap: A value that, if exceeded by the total sale valuation, will remove the participant’s bid from the sale. It allows the participant to indicate what total sale valuation they are comfortable with. If, at any point in the sale, the total sale valuation is greater than a participant’s personal valuation cap, the participant’s bid is removed. This is important because the participant can ensure that they will have at least a certain percentage of the total tokens in the sale, based on this formula:

minimum percentage = (ETH Payment)/(personal valuation cap)

First Stage

In the first stage, participants are allowed to submit and withdraw bids at will. In this stage, the total sale valuation will fluctuate as bids are submitted and withdrawn.

The price of tokens starts low and then increases linearly until the end of the first stage, where it reaches a stable price for the rest of the sale.

During the first stage, manual withdrawals are subject to a withdrawal penalty that starts at 0% and increases linearly up to 100% at the end of the first stage. This is to protect against large buyers attempting to falsely signal large bids, withdraw, and manipulate the valuation. The ETH that is withheld from this refund still goes towards purchasing tokens for this participant.

A manual withdrawal also forfeits a percentage of the bonus tokens received from a lower purchase price on the remaining, permanently committed tokens.

Since the total sale valuation can move up and down in the first stage due to deposits and withdrawals, every bid is still ‘active’ even if a personal valuation cap is exceeded, meaning that it still has or may have an effect on the sale.

Second Stage

The transition between the first and second stage is termed the “withdrawal lock.” In the second stage, submissions are still allowed, but all active bids are committed for the duration of the sale. Withdrawing ETH refunds are only allowed if the participant has a minimal bid, meaning their personal cap is less than the total sale valuation — in other words, excluded from the sale.

Post-Sale:

After the sale is over the token smart contract is deployed automatically and tokens are allocated based on the results of the sale. Any participant can withdraw their purchased tokens or refunded ether. Tokens cannot be withdrawn during the sale because of the fluctuating valuation and possibility that the purchases will be refunded. The withdrawn tokens and/or ether are then sent to the participant’s address.

Manual Withdrawal Details

Now that we know what the two stages are, lets look at the process of manually withdrawing. During the first stage, participants can manually withdraw their submitted bid and receive a linearly decreasing percentage of their ETH back. This is to protect against large buyers attempting to falsely signal and manipulate the valuation. The ETH that is withheld from this refund still goes towards purchasing tokens for this participant and the rest is refunded.

Additionally, a manual withdrawal forfeits a percentage of the early contribution bonus on the remaining, permanently committed tokens. See section 5.3 of the technical paper for an explanation of why these two penalties are necessary. The ETH that was withheld still goes towards the crowdsale in the form of a token purchase — this penalized ETH is no longer able to be withdrawn.

In the second stage, like what is mentioned above, manual withdrawals are only allowed if the participants cap has been exceeded by the total valuation. These withdrawals receive complete refunds because their contributions have no effect on the sale. After the withdrawal lock, the total sale valuation will monotonically increase, meaning it can only stay the same or increase.

To recap:

That was only a basic explanation to provide some background information on interactive token sales. In this article, I am assuming that readers know the general basics of how the sale works. You can read some of the articles I linked above in addition to the technical paper to get a better handle on some intricacies of the protocol. Rob Bent wrote a great summary here:

Modular Libraries

If you have read any of my or C. Brown’s posts or checked out our repository or website, you know that we’re creating secure standard libraries that are easy to understand and use right away. In that vein, we decided to build the Interactive Token Sale implementation on top of our base Crowdsale Library, which contains proven functionality and storage structs that are common to almost all crowdsale models, so that anyone can utilize them as library functions. You can read more about this library in my first post:

Linked List Library

To store the sorted personal valuation caps, we needed a data structure that could be sorted in increasing order that also allows easy insertions and deletions. Sounds like the description of a linked list! We have a library for that also that we made based on the great work Darryl Morris did in his implementation here, creating a linked list that is built using a uint256 => bool mapping. You can read more about how that works in my last post:

First Draft Details

When we first built the Interactive Token Sale implementation, we took what was written in the technical paper and directly translated it to smart contract form.

Obvious Issues

Implementing theoretical work in code comes with tradeoffs and issues that don’t necessarily apply in theory. Also, smart contract programming is a new paradigm which creates issues that normal code doesn’t necessarily have, most notably around cost of execution, i.e. gas use.

Removing Bids from the sale costs too much gas:

In the technical paper, it was specified that when the total sale valuation surpasses a bidder’s personal valuation cap, their bid should be removed from the sale. This seemed fairly simple, and we implemented this functionality in the submitBid function as a loop that traverses the submitted bids and removes any bids with personal valuation caps below the total sale valuation, and sends refunds until the total sale valuation has gotten back to be less than all bids that have been submitted. If you are interested in seeing the code, check out the implementation here.

As you can probably see, this poses a potential problem of unchecked gas usage. If a user submits a large bid, there could be many bids that need to be removed, potentially costing the bidder a lot in gas fees or even exceeding the block gas limit!

It’s imperative that all participants in the IICO protocol are treated equally. Participants should not be penalized with high gas usage for the behavior of other users. Therefore, we needed to completely redesign how the sale keeps track of bids.

Redesign

The redesign constantly calculates the cutoff for participation in the sale and keeps a record of which bids are valid and which aren’t, without automatic refunds. After the sale ends, participants can withdraw their tokens if their cap was not exceeded by the total value. If their valuation cap was exceeded, they were essentially removed from the sale, so their total amount of ETH they contributed to the sale is refunded and available for withdrawal. This avoids costly looping and calculations that were needed in the previous version when bids were sorted and removed/partially removed throughout the sale, cutting down on gas price. Sending ETH is a costly transaction, so removing all the unnecessary ETH transfers is very important. Both versions enforce the monotonically increasing valuation variant discussed in section 5.2 of the technical paper, but this version significantly cuts down on gas costs.

This version also enforces that personal valuation caps can only be submitted in evenly spaced valuations. These are spaced by the 3 most significant digits of each personal valuation cap. These make it so there is even spacing in the linked list struct that holds the personal valuations, meaning the bids are spaced in a more efficient fashion, further saving gas on searching the linked list.

Valuation Cutoff Pointer

To accomplish this, the library utilizes a pointer throughout the sale that indicates which personal valuation cap in the linked list is the cutoff point for being allowed in the sale. It only counts bids towards the total sale valuation that have personal valuation caps that are greater than the total sale valuation. This pointer will be correlated to the total sale valuation, but since it will always point to a submitted personal valuation cap, it will often not be exactly equal to the total sale valuation, but will be relatively close.

At the beginning of the sale, the pointer is set at 0, indicating that the valuation of the sale is 0 and there are no bids that have been submitted or “discarded” from the sale.

The state at the beginning of the sale. The box on the left is the empty linked list representing the sorted submitted personal valuation caps.

Bid Process:

(This process is taken from the submitBid function in InteractiveCrowdsaleLib.sol)

Every time a new bid is submitted, its personal valuation cap is added to the sorted linked list of personal valuation caps. Then the contract adds its bid to a sum of all bids submitted with that same personal valuation cap. After that, it records the amount of ETH submitted in that bid and the token price bonus at the time the bid was submitted.

Then, if the personal valuation cap is less than the current total sale valuation, the bid is still registered by the sale, but it is not counted towards the total sale valuation. If the personal valuation cap is greater than the current total sale valuation, then the bid is added to the current valuation and the cutoff pointer is potentially increased to show which bids have been “kicked out” of the sale. Bids that are overtaken by the pointer are subtracted from the total sale valuation but still remain recorded in case the pointer decreases.

This valuation cutoff pointer mechanism is active throughout the entire sale, but will increase and decrease during the first stage of the sale. After the withdrawal lock, the pointer, like the total sale valuation, is monotonically increasing, meaning it cannot decrease, only increase or stay the same, since manual withdrawals are not possible in that stage.

If you reference the technical paper, you’ll see in Section 4, step 3, part 3 that bids that are equal to the current valuation pointer are refunded a portion of their bids instead of being kicked out to keep the valuation monotonically increasing as bids are removed. This is now only necessary to do once, at the very end of the sale. Bids with personal valuation caps equal to the valuation pointer will be partially refunded ETH and receive the rest of their purchase in tokens. This still satisfies the requirement in section 5.2.

Valuation Pointer Mechanics Walkthrough

I’ll do a quick walkthrough with some diagrams to show the correct pointer movement. We’ll start with the first bid in the sale, Josh sending 9 ETH with a personal valuation cap of 186 ETH. Here is the state after the first bid.

As you can see, Josh has contributed 9 ETH, so the total sale valuation has increased to 9. His personal valuation cap was added to the sorted list of caps, but since the total sale valuation is still less than his cap, the pointer is still pointing at zero, showing that all bids still remain in the sale.

You might be confused that the pointer isn’t equal to the current valuation. The reason for this is that the pointer needs to always point at one of the personal valuation caps, indicating which cap is the cutoff to remain in the sale. This is slightly different than what is specified in section 4 of the technical paper, but necessary for proper functionality of the pointer mechanism and ensuring monotonically increasing sale valuation.

A similar state happens when the second bid is submitted with a high personal valuation cap:

Going smoothly so far! Now lets see what happens when a bid is submitted with a personal valuation cap that is less than the total sale valuation:

The bid has a personal valuation cap below the total sale valuation, so it doesn’t affect the sale at all
Pointer is fixed when another bid comes in

As you can see, Robbie’s bid with a personal valuation cap less than the total sale valuation is still recorded in the list and storage, but does not affect the total sale valuation or pointer at all. When another bid comes in, the pointer is moved to 12 to show that personal valuation caps of 12 and lower are not involved in the sale.

Now lets see what happens when a bidders cap is exceeded:

As you can see from the next two bids, the total sale valuation would increase to more than Gus’ cap of 38, but removing his bid would decrease the total sale valuation to below 38. This creates an issue with deciding whether or not he should remain in the sale. The sale accounts for this by moving the pointer to 38, his cap, and setting the total sale valuation to the same number. This indicates that he will get a partial purchase of his tokens when he withdraws his tokens at the end of the sale.

Withdrawals During the First Stage of the Sale

At some point, some of the participants might want to manually withdraw their bid. To protect from blackout attacks (detailed in section 5.3 of the technical paper) contributors that manually withdraw will forfeit part of their early contribution bonus and a portion of their contribution will be locked in to the sale. When a contributor withdraws, they will get a partial refund, the rest will be converted to tokens, minus part of the early contribution bonus. You can read about the reasoning for this choice in section 5.3 of the paper. Please remember, this is only for manual withdrawals before the “withdrawal lock”. Automated withdrawals due to personal cap being lower than the total valuation will not be penalized.

Let’s look at how it would affect our example sale.

In this example, JG wants to withdraw his 5 ETH contribution from the sale. The figure shows the state of the sale after the withdrawal.

The penalty increases linearly throughout the first stage of the sale. For simplicity’s sake, we are assuming that the penalty at this point is 40%. JG gets 3 ETH back and the remaining 2 is still committed to the sale. This decreases the total sale valuation to 35, and the pointer stays at 38. If there was a bid with a personal valuation cap of 35, the pointer would be pointing at that, but since there isn’t, it stays at 38.

As you can probably imagine, manual withdrawals can lead to a reduction in the Total Valuation and as a result, bids that were previously excluded can once again be included.

For clarification, bids that have been passed by the valuation pointer are still subject to the withdrawal penalty before the withdrawal lock, but not after. You can read the reasoning for this in section 5.3 of the technical paper.

Most of the algorithms for tracking bids require changing only a few values and iterating over a small number of nodes in the linked list. This significantly decreases the potential gas cost compared to the costly operations required for looping through the list, removing nodes, giving bidders with minimal bids partial refunds after each loop, and resetting bidders stored bid history.

Potential Improvements

Buyers currently do not have a way to enforce a minimum valuation:

The goal of the technical paper is to give participants as much control and comfort in participating in the sale at their desired valuation. Some participants might be uncomfortable participating in the sale if the valuation doesn’t exceed a certain value. A solution was proposed for this as a possible addition to the core protocol. Similarly to the personal valuation caps, users would also be able to include personal valuation minimums where bids are kicked out of the sale if the total sale valuation doesn’t meet their minimum.

We devised an algorithm for this, but eventually came to the conclusion we would hold off on including it in the current implementation. The complexity of the interactions between personal minimums and personal valuation caps would be too challenging to reasonably build and test at this point. Modular cares about building secure systems, so ensuring that the base functionality of the Interactive token sale is functional and secure was our top priority.

Instead, we included a minimum valuation that the team running the crowdsale sets. If this minimum valuation is not met, the sale is cancelled and all bids are refunded. This is intended for the project to determine what the minimum amount of money is for them to successfully build the protocol. Participants can use this to decide if they are ok with the minimum valuation before they submit their bids.

We hope to include personal minimums in a future version of the implementation.

Current Status

We have finished the first draft and it is being audited! We’re currently working on writing as many tests as possible, expanding our coverage and hitting as many possible sale configurations and test cases as we can.

You can get involved in the development of this protocol! The development of the contract is open source and we are always looking for contributors, especially with writing tests for the contracts to account for all possible configurations and test cases. If you want to get involved, you can get on the repo and comment on a current issue or open an issue.

Issues that currently need addressing are:

  • 100% Code coverage (triggering all possible throws, conditionals, etc)
  • testing all varieties of different sale configurations and participant behaviors to ensure behavior stays consistent throughout every scenario.
  • Updating documentation, adding a FAQ, and adding more diagrams to more accurately describe the behavior of the contracts.

Discord: https://discord.gg/sMu6Des

The contracts will be used in a production setting soon!

Learn More!

This is a crowdsale model that has the potential to make a huge difference in how ICOs are run and projects are valued. If you would like to learn how you can get involved or want to use the Interactive ICO for your token sale, please get in contact with us by signing up for the newsletter and filling out our survey. You’ll be able to get updates about the status of the protocol and the work that is being done on creating a great user experience for it.

Link to IICO website:

HUGE thanks to C. Brown, Gustavo (Gus) Guimaraes, JG Carvalho, and Rob Bent for their work on building and testing the implementation, and for reading and making suggestions on this post. And thank you especially to Jason Teutsch and Vitalik Buterin for designing this awesome protocol and collaborating with us on improving the protocol, building the implementation, and writing this post!

You can also find out what else we are doing at Modular by checking out our website at https://modular.network. We have some great projects in the works and can’t wait to share them with everyone!

--

--

Joshua Hannan
Modular-Network

Smart Contract Engineer at Dapper Labs, working with Cadence, the best language for smart contracts!