ICO

A real life ico process

This is the archetype version of the ICO process set up by BCDiploma and described at the following address:

archetype ico
constant symbol : string = "BCDT"
constant decimals : int = 18
(* contribution thresholds *)
variable min_contribution : tez = 1tz
variable max_contribution_silver : tez = 10tz
(* bcd token data *)
variable max_bcd_to_sell : tez = 100000000tz
variable exchange_rate_bcd_tez : int = 13000
(* round caps *)
variable soft_cap : tez = 1800tz
variable presales_cap : tez = 1800tz
variable round1_cap : tez = 3600tz
(* presales_cap + 1600 *)
(* Number tokens sent, eth raised, ... *)
variable nb_bcd_sold : tez = 0tz
variable nb_tez_raised : tez = 0tz
(* Roles *)
variable owner : role = @tz1XZ7s6uStC2hZVpPQhXgcdXPwxifByF3Ao
variable whitelister : role = @tz1Lc2qBKEWCBeDU8npG6zCeCqpmaegRi6Jg
variable reserve : role = @tz1bfVgcJC4ukaQSHUe1EbrUd5SekXeP9CWk
variable community : role = @tz1iawHeddgggn6P5r5jtq2wDRqcJVksGVSa
(* contributor *)
enum whitelist =
| Silver
| Gold
asset contributor identified by id {
id : address;
wlist : whitelist;
contrib : tez = 0tz;
}
enum gstate =
| Init initial (* ICO isn't started yet, initial state *)
| PresaleRunning (* Presale has started *)
| PresaleFinished (* Presale has ended *)
| Round1Running (* Round 1 has started *)
| Round1Finished (* Round 1 has ended *)
| Round2Running (* Round 2 has started *)
| Round2Finished (* Round 2 has ended *)
variable vstate : gstate = Init
function is_running () : bool {
return
match vstate with
| PresaleRunning | Round1Running | Round2Running -> true
| _ -> false
end
}
function get_rate () : rational {
var coeff : rational =
match vstate with
| PresaleRunning -> 1.2
| Round1Running -> 1.1
| _ -> 1
end;
return (coeff * exchange_rate_bcd_tez)
}
function get_remaining_tez_to_raise () : tez {
return
match vstate with
| PresaleRunning | PresaleFinished -> presales_cap - nb_tez_raised
| Round1Running | Round1Finished -> round1_cap - nb_tez_raised
| _ -> (
let remaining_bcd : tez = max_bcd_to_sell - nb_bcd_sold in
(1 / exchange_rate_bcd_tez) * remaining_bcd)
end
}
function transition_to_finished () : gstate {
return
match vstate with
| PresaleRunning -> PresaleFinished
| Round1Running -> Round1Finished
| Round1Finished -> Round2Running
| _ -> Round2Finished
end
}
entry contribute () {
require {
c1 : contributor.contains(caller);
c2 : is_running ();
c3 : transferred >= min_contribution;
c4 : (not (contributor[caller].wlist = Silver and transferred >= max_contribution_silver));
}
effect {
(* cap contribution to max_contrib if necessary *)
var lcontrib = transferred;
if contributor[caller].wlist = Silver
and contributor[caller].contrib + lcontrib >= max_contribution_silver
then lcontrib := max_contribution_silver - contributor[caller].contrib;
(* cap contribution to round cap if necessary *)
var remaining_tez : tez = get_remaining_tez_to_raise ();
if remaining_tez <= lcontrib
then (
lcontrib := remaining_tez;
vstate := transition_to_finished ()
);
(* convert contribution to nb of bcd tokens *)
var rate = get_rate ();
var nb_tokens : tez = rate * lcontrib;
(* update ico stats *)
nb_bcd_sold += nb_tokens;
nb_tez_raised += lcontrib;
(* update caller's contribution *)
contributor[caller].contrib += lcontrib;
(* syntaxic sugar for
contributor.update caller { contrib += contrib } *)
if lcontrib <= transferred
then transfer (transferred - lcontrib) to caller
}
}
(* the onlyonce extension specifies that withdraw action can be
executed only once, that is a contributor can withdraw only once. *)
entry withdraw () {
require {
c5 : vstate = Round2Finished;
c6 : nb_tez_raised <= soft_cap;
c7 : contributor[caller].contrib > 0tz;
}
effect {
transfer contributor[caller].contrib to caller;
(* do not forget this *)
contributor[caller].contrib := 0tz
}
}
Edit on GitHub