The EOS Crowdsale contract explained

in #eos7 years ago (edited)

Since there are a lot of questions about exactly what the EOS ERC20 token is and what it means, here is an overview of the functions in the EOS Crowdsale contract.

EOS has 2 contracts, the crowdsale contract and the ERC-20 contract, since the crowdsale contract uses the ERC-20 token we will look at this first.

Screen Shot 2017-10-03 at 19.58.20.png

I will not give you the address for the contract directly because officially you must get this by going to https://eos.io and clicking on "Tokens" and then "Get EOS".

If you are not used to manually calling functions on a contract, then please do not use this information to do anything, I am not responsible if anything here causes you to lose tokens.

There are only 8 functions on the CrowdSale contract that most people need to know about; initialize, buy, buyWithLimit, claim, claimAll, register, collect, freeze. I will go through each function, show it and then explain what it is doing. I will remove some code from the functions sometimes to make it more readable (I will only remove error checks etc)

initialize

function initialize(DSToken eos) auth {
    EOS = eos;
    EOS.mint(totalSupply);

    // Address 0xb1 is provably non-transferrable
    EOS.push(0xb1, foundersAllocation);
    keys[0xb1] = foundersKey;
}

This is the first function that was ever called on the contract, block.one will have called this function just before starting the crowdsale. All it does is create a link inside the contract to the token contract (EOS) and then mint all of the coins that will be distributed and finally allocate the foundersAllocation to block.one. They would have created the ERC-20 token contract and then supplied the address as the eos variable.

The key 0xb1 is very interesting because it is not a normal looking Ethereum address. 0xb1 is an intentionally invalid address which means that even though block.one have been reserved some tokens, they cannot transfer them ever (They will be given a valid key on the EOS chain).

Notice the word auth at the end of the first line? This is a function modifier which means this function can only be called by an authorised account.

buy

function buy() payable {
   buyWithLimit(today(), 0);
}

This is the function that actually gets called when money is sent directly to the contract (the payable modifier says you can send ETH to this function). All it does is call the buyWithLimit function with the first parameter as todays day number and second parameter of 0. We will see what all that means next.

The anonymous payable function actually receives money sent directly, but that just calls buy()

buyWithLimit

function buyWithLimit(uint day, uint limit) payable {
    userBuys[day][msg.sender] += msg.value;
    dailyTotals[day] += msg.value;

    if (limit != 0) {
        assert(dailyTotals[day] <= limit);
    }
}

This is the meat of buying EOS ERC-20 tokens. There are 2 variables here, userBuys and dailyTotals. They are both arrays which you can think of as lists.

userBuys is incremented by the amount of your payment (msg.value) against the day and your address, you can see how much you have bought on each day by reading this variable.

dailyTotals is incremented by the amount of your payment to keep a total of how much is contributed each day (so that it can work out the price of each token when the day ends).

Finally because this is buyWithLimit it will make sure that the total raised for the day is less than your limit, if it is more than your limit the function will fail and the buy will not be committed.

When buy was called earlier it used the default values of today and 0, you can purchase future days and set a limit using this function. Remember all of this functionality is in the distribution app.

claimAll

function claimAll() {
    for (uint i = 0; i < today(); i++) {
        claim(i);
    }
}

This is the function that most people call when they want to claim their tokens. As we saw before when you buy some tokens it just records how much you bought and how much total contribution there has been for that day. Nothing is actually bought.

To actually get your tokens you must wait until the day is over and the price calculated before they can be sent to you.

The claimAll function is very simple, it just makes a variable called i and then sets it to every value between 0 and the current day number and then calls claim for this day. This is very inefficient, most of the calls to claim will not do anything if you only bought on one day. People often have problems with this function running out of gas because it is doing a lot of work looping through each day.

claim

function claim(uint day) {
        if (claimed[day][msg.sender] || dailyTotals[day] == 0) {
            return;
        }

        // This will have small rounding errors, but the token is
        // going to be truncated to 8 decimal places or less anyway
        // when launched on its own chain.

        var dailyTotal = cast(dailyTotals[day]);
        var userTotal  = cast(userBuys[day][msg.sender]);
        var price      = wdiv(cast(createOnDay(day)), dailyTotal);
        var reward     = wmul(price, userTotal);

        claimed[day][msg.sender] = true;
        EOS.push(msg.sender, reward);
}

This is the meat of the claimAll function, it is called for every day from the start until the current day but you can save gas by calling this function with the day that you contributed on.

The function starts by checking that you have not already claimed for this day and that the contributions for the day are not 0. Either way, continuing would be pointless so it just exits if either of those are true.

Next there is a bunch of mathematics which just calculates the price of the token based on how much ether received (dailyTotals) and then your reward (number of EOS tokens you will get)

Next it marks that day as claimed by you and then sends the EOS tokens to you EOS.push(msg.sender, reward);

register

function register(string key) {
        keys[msg.sender] = key;
}

This is the function that is called when you register your EOS key so that you will have tokens on the main net when it launches. You must do this at some point before the token sale ends.

As you can see it is a very simple function, it just checks that the day is not more that 1 day after the crowdsale ends, the EOS token is roughly valid and then it sets the keys variable alongside your Ethereum address. This data is pulled out of Ethereum by a script so that the genesis file can be generated.

These are all the functions that you as a buyer of tokens will use. There are a couple more which are interesting though.

collect

function collect() auth {
    exec(msg.sender, this.balance);
}

Again, aside from error checking and logging this function is a one liner, but such a good line!

It will take all the money that is in the crowd sale and send it to the address that you supply. Great I hear you say, we can all be rich and take the money!!

Hold on, there's the auth modifier :( Only the owner of the contract can call this function.

Whoever owns the contract regularly calls this function to withdraw the money from the crowdsale.

freeze

function freeze() {
    assert(today() > numberOfDays + 1);
    EOS.stop();
}

Another small but mighty function, EOS is the token contract and this function calls stop on it. We aren't looking at the token contract at the moment but I think you know what it does.

Wait one sec! This function DOESN'T have the auth modifier, this means anyone can call this function to stop the madness...

The first line checks if the crowdsale has ended before proceeding, this means that anyone can be the person to stop the crowdsale contract as long as you wait until after it has ended.

That's it

The crowdsale is actually hardly any code, it is just a tally of all contributions and a calculator to work out how much the price of a token is for a particular day and then send them when you claim.

Homework

If you want to have a look at the real contract, find the address (The first 3 letters are an anagram of DAO). Then enter it into etherscan.io in the search box. This will show you all the information about the contract.

On the tabs you should also see the Read Smart Contract tab. Click on this and you can see all of the variables that I have mentioned here (plus a few more). You can read the values of these variables by entering the day number and / or your address. Play around - reading from the contract is free and you cannot break it.

If you have registered your EOS key then you should be able to enter your Ethereum address and see that the key is actually registered.

P.S. Any time you see an amount of ether (ie in userBuys) the value is expressed in wei, not ether. You didn't just become rich overnight!

** Please upvote this post if it was useful to you **

** I have now done an explanation of the ERC-20 token. Please read that next! **

https://steemit.com/eos/@m-i-k-e/the-eos-erc-20-contract-explained

DISCLAIMER : I am not an expert on EOS nor am I an employee of block.one or associated with them in any way. I do own EOS tokens. Anything here is just my interpretation based on my reading of white papers, the source code for eos, the Solidity token sale contract and conversations in public chat rooms. I have been a developer for >20 years but I do not focus on C++ or blockchain in my day job. Some of the things that I say may not be technically accurate but I think that the general ideas are correct, if there are any material errors please comment and I will fix them

Sort:  

@m-i-k-e great post! a lot of people going into this ICO with little to no idea of what is going on!!

I have just uploaded a post about people buying EOS tokens from exchanges, and would appreciate your thoughts on it. I have tried to make it as helpful as possible from my own experience.

Upvoted and followed.

Alex

Thanks for the upvote, I have had a look at your post and it seems correct. There is a chance that exchanges will register and migrate the tokens for you but personally I would not rely on that. If they do it then it will likely be a few weeks after the mainnet is launched before you could withdraw the tokens.

Using or staking the tokens would probably be very difficult when they are on an exchange and they could rent out your tokens to the highest bidder and then not give you the profits

Oh... I bought like 2/3 of my tokens via exchanges, but then I transfer over to MEW. I better read your article too @alexmavor

Thanks for the upvote on my wolf.social post @m-i-k-e - I hope to attract one or more developers to the project. :)

The @OriginalWorks bot has determined this post by @m-i-k-e to be original material and upvoted it!

ezgif.com-resize.gif

To call @OriginalWorks, simply reply to any post with @originalworks or !originalworks in your message!

To enter this post into the daily RESTEEM contest, upvote this comment! The user with the most upvotes on their @OriginalWorks comment will win!

For more information, Click Here! || Click here to participate in the @OriginalWorks writing contest!
Special thanks to @reggaemuffin for being a supporter! Vote him as a witness to help make Steemit a better place!

Please discuss buying EOS from an exchange.
What are the requirements

The ERC-20 token explanation is up now, please read that (and upvote). You need to understand what an ERC-20 token is before exchange accounts make sense

Great post on EOS @m-i-k-e! Look forward to reading more from you.
HAT TRICK: Upvoted/Resteemed/Followed

Thanks for this. I've actually bookmarked this so that I can come back to it after completing my Udemy training on Smart Contract development.

Upvoted and following, if you happen to take a specific interest in ICO research, feel free to follow us back. Thanks.

You should note that in buyWithLimit, the dailyTotals will be calculated at the moment of contract execution. So if you're contributing to a future period where only 100 ETH have been contributed so far, your transaction specifying a limit of 101 ETH would process normally and you would still buy EOS tokens, even if the day ends up collecting 20000 ETH.

I'm talking about this code in particular:

    userBuys[day][msg.sender] += msg.value;
    dailyTotals[day] += msg.value;

    if (limit != 0) {
        assert(dailyTotals[day] <= limit);
    }

    LogBuy(day, msg.sender, msg.value);

This was the info that I came for. Thanks for pointing that out as it's not explicitly obvious when the limit is determined.

Quality write-up. Well done m8