*We have retargeted our start block to 3933451 due to time drift.*
Hi. I’m Mike Goldin. I’m a software developer at ConsenSys and I wrote the adToken launch contracts.
It is very stressful to do a token launch! Millions of dollars get pushed at your application in a very short period of time, and every Wei needs to be handled or rejected correctly. On a blockchain you cannot simply “reboot the server” if something goes wrong; if you lose somebody’s money it is gone forever and they will not be happy with you.
The adToken launch is extremely simple. We are selling a finite number of tokens at a fixed price. MetaX aims to realize revenue of $10 million from the sale, and the $10 million figure is a hard cap. We will price the tokens one day in advance of the launch relative to the exchange rate at that time such that if all 500 million tokens are sold MetaX will realize $10 million in revenue at that exchange rate.
There is no dynamic pricing or exotic auctioning mechanism. The adToken launch is a fixed-price, finite-supply sale. Nothing fancy happens here. MetaX will realize revenues very close to $10 million depending on fluctuations in the price of Ether in the day between our setting the sale price and the sale’s start block.
Let’s look at the contracts!
Don’t Trust Me, Trust The Machine!
Here is the sale contract deployed on the mainnet, and here is the source code. First take a look at the storage variables called startBlock and freezeBlock. The startBlock is used in the saleStarted modifier to reject purchases attempted before the sale begins. The freezeBlock is used in the isFrozen modifier to prevent the contract owner (me!) from making changes to the price and startBlock less than startBlock-freezeBlock blocks before the startBlock. If you look on Etherscan you can see our start block is currently set to 3939181, which is a prime number and therefore a very good block on which to launch a token. Block 3939181 should come around noon EST on June 26th. Our freezeBlock is set to 3937741, which should come about six hours before the startBlock.
We are planning on setting the final sale price about a day in advance and probably will not adjust it at the last minute unless there is a crazy swing in the price of Ether. But for you, as a buyer, you can rest assured that we cannot change the price less than six hours in advance of the sale start, so you will know the price you’re paying.
If we do decide to change the startBlock, notice in the changeStartBlock function that the freezeBlock will automatically shadow the new startBlock. So if we do change the startBlock, you can still rest assured that six hours before the sale the final parameters will be locked in and not even we will be able to change them.
One important owner-only function which is not modified by the isFrozen modifier is the emergencyToggle. If at any point we discover some problem in the launch contracts we can activate the emergencyToggle to prevent the sale from proceeding.
The Critical Path
The Sale contract is 242 lines long, but there are 31 lines (16 minus comments and whitespace) which are truly in the critical path. This is the purchaseTokens function. Notice first of all that this is the only function in the contract specified as payable, meaning raw sends to the contract will revert. Indeed, messages which include an Ether balance sent to any function other than purchaseTokens will revert. Aside from the payable modifier, the purchaseTokens function’s modifiers require that the sale has started, that the emergency flag is not toggled and that the contract’s setup is complete (setup is complete, look for yourself on Etherscan!).
Okay, the modifiers seem pretty simple. What happens in the purchaseTokens function?
On lines 178–180 we are going to determine whether the sender sent in any excess Ether. If adToken are priced at 2 Wei and the user sends us 3 Wei we sadly cannot give the user half an adToken. We have to give them some of their money back! That’s what we do in these lines: determine whether any excess Ether was sent so we can send it back to them.
Once we’ve done that, we know exactly how many adToken the sender can purchase given the amount of Ether they sent us. On line 183 we make sure that there are actually enough adToken still available for sale and revert the transaction if not. If there are enough adToken available for the purchaser to buy the amount they specified, we then at line 187 send any excess Ether they sent to us back to them before proceeding.
Okay, we’re almost done! On line 191 we transfer the Ether the sender sent us to our wallet. Right after that we transfer the tokens the sender just purchased to the account they sent with.
Note that we use transfer instead of send on line 191. Both transfer and send will only forward 2300 gas to the receiving address which prevents reentrancy the likes of which brought down the DAO, but transfer will actually revert the entire transaction if an Out-of-Gas error occurs in the receivers fallback function.
On line 194 we wrap our token transfer (different than an Ether transfer!) in an assert statement. The ERC-20 token contracts we’re using return false on failure, and we will want to revert the whole transaction if the token transfer fails for any reason.
Finally, on line 196 we emit an event to let the world know the purchase was successful!
Things to note: once the sale is sold out, all transactions to purchaseTokens should throw due to the check at line 183. This means there should be no way to give the Sale contract Ether once the sale is over. This same logic also means that even before the sale is fully sold out, purchases for more tokens than are available will fail — small purchases may give you a better chance of getting tokens before they sell out! It is also possible to write a wrapper contract that will buy exactly the right amount of tokens and close the sale out while returning any excess balance to the sender, but we leave that as an exercise to the reader (make sure you write a function to let yourself withdraw the tokens! Tokens are attributed to the msg.sender, which will be your contract! Unless you specify a withdraw function you will lock your tokens doing this!).
Testing Mission Critical Code
The final step in making me feel great about the safety of this token launch is… A public bug bounty!
The AdToken Launch Contracts Bug Bounty!
Our contracts have been deployed on Kovan as well as the mainnet, and we are offering large bounties (500 thousand adToken!) for bugs which can:
- Lock/burn Ether OR adToken
- Enable the theft of Ether OR adToken
We will offer smaller bounties for bugs which produce unexpected behaviors that do not rise to the level of asset theft. If you need Kovan ETH or want to communicate with other bounty hunters, join the AdChain slack!
Please send all bugs to firstname.lastname@example.org