crowdsales.adoc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. = Crowdsales
  2. Crowdsales are a popular use for Ethereum; they let you allocate tokens to network participants in various ways, mostly in exchange for Ether. They come in a variety of shapes and flavors, so let's go over the various types available in OpenZeppelin and how to use them.
  3. Crowdsales have a bunch of different properties, but here are some important ones:
  4. * Price & Rate Configuration
  5. * Does your crowdsale sell tokens at a fixed price?
  6. * Does the price change over time or as a function of demand?
  7. * Emission
  8. * How is this token actually sent to participants?
  9. * Validation — Who is allowed to purchase tokens?
  10. * Are there KYC / AML checks?
  11. * Is there a max cap on tokens?
  12. * What if that cap is per-participant?
  13. * Is there a starting and ending time frame?
  14. * Distribution
  15. * Does distribution of funds happen in real-time or after the crowdsale?
  16. * Can participants receive a refund if the goal is not met?
  17. To manage all of the different combinations and flavors of crowdsales, OpenZeppelin provides a highly configurable xref:api:crowdsale.adoc#Crowdsale[`Crowdsale`] base contract that can be combined with various other functionalities to construct a bespoke crowdsale.
  18. [[crowdsale-rate]]
  19. == Crowdsale Rate
  20. Understanding the rate of a crowdsale is super important, and mistakes here are a common source of bugs.
  21. ✨ *HOLD UP FAM THIS IS IMPORTANT* ✨
  22. Firstly, *all currency math is done in the smallest unit of that currency and converted to the correct decimal places when _displaying_ the currency*.
  23. This means that when you do math in your smart contracts, you need to understand that you're adding, dividing, and multiplying the smallest amount of a currency (like wei), _not_ the commonly-used displayed value of the currency (Ether).
  24. In Ether, the smallest unit of the currency is wei, and `1 ETH === 10^18 wei`. In tokens, the process is _very similar_: `1 TKN === 10^(decimals) TKNbits`.
  25. * The smallest unit of a token is "bits" or `TKNbits`.
  26. * The display value of a token is `TKN`, which is `TKNbits * 10^(decimals)`
  27. What people usually call "one token" is actually a bunch of TKNbits, displayed to look like `1 TKN`. This is the same relationship that Ether and wei have. And what you're _always_ doing calculations in is *TKNbits and wei*.
  28. So, if you want to issue someone "one token for every 2 wei" and your decimals are 18, your rate is `0.5e18`. Then, when I send you `2 wei`, your crowdsale issues me `2 * 0.5e18 TKNbits`, which is exactly equal to `10^18 TKNbits` and is displayed as `1 TKN`.
  29. If you want to issue someone "`1 TKN` for every `1 ETH`", and your decimals are 18, your rate is `1`. This is because what's actually happening with the math is that the contract sees a user send `10^18 wei`, not `1 ETH`. Then it uses your rate of 1 to calculate `TKNbits = rate * wei`, or `1 * 10^18`, which is still `10^18`. And because your decimals are 18, this is displayed as `1 TKN`.
  30. One more for practice: if I want to issue "1 TKN for every dollar (USD) in Ether", we would calculate it as follows:
  31. * assume 1 ETH == $400
  32. * therefore, 10^18 wei = $400
  33. * therefore, 1 USD is `10^18 / 400`, or `2.5 * 10^15 wei`
  34. * we have a decimals of 18, so we'll use `10 ^ 18 TKNbits` instead of `1 TKN`
  35. * therefore, if the participant sends the crowdsale `2.5 * 10^15 wei` we should give them `10 ^ 18 TKNbits`
  36. * therefore the rate is `2.5 * 10^15 wei === 10^18 TKNbits`, or `1 wei = 400 TKNbits`
  37. * therefore, our rate is `400`
  38. (this process is pretty straightforward when you keep 18 decimals, the same as Ether/wei)
  39. [[token-emission]]
  40. == Token Emission
  41. One of the first decisions you have to make is "how do I get these tokens to users?". This is usually done in one of three ways:
  42. * (default) — The `Crowdsale` contract owns tokens and simply transfers tokens from its own ownership to users that purchase them.
  43. * xref:api:crowdsale.adoc#MintedCrowdsale[`MintedCrowdsale`] — The `Crowdsale` mints tokens when a purchase is made.
  44. * xref:api:crowdsale.adoc#AllowanceCrowdsale[`AllowanceCrowdsale`] — The `Crowdsale` is granted an allowance to another wallet (like a Multisig) that already owns the tokens to be sold in the crowdsale.
  45. [[default-emission]]
  46. === Default Emission
  47. In the default scenario, your crowdsale must own the tokens that are sold. You can send the crowdsale tokens through a variety of methods, but here's what it looks like in Solidity:
  48. [source,solidity]
  49. ----
  50. IERC20(tokenAddress).transfer(CROWDSALE_ADDRESS, SOME_TOKEN_AMOUNT);
  51. ----
  52. Then when you deploy your crowdsale, simply tell it about the token
  53. [source,solidity]
  54. ----
  55. new Crowdsale(
  56. 1, // rate in TKNbits
  57. MY_WALLET, // address where Ether is sent
  58. TOKEN_ADDRESS // the token contract address
  59. );
  60. ----
  61. [[minted-crowdsale]]
  62. === Minted Crowdsale
  63. To use a xref:api:crowdsale.adoc#MintedCrowdsale[`MintedCrowdsale`], your token must also be a xref:api:token/ERC20.adoc#ERC20Mintable[`ERC20Mintable`] token that the crowdsale has permission to mint from. This can look like:
  64. [source,solidity]
  65. ----
  66. contract MyToken is ERC20, ERC20Mintable {
  67. // ... see "Tokens" for more info
  68. }
  69. contract MyCrowdsale is Crowdsale, MintedCrowdsale {
  70. constructor(
  71. uint256 rate, // rate in TKNbits
  72. address payable wallet,
  73. IERC20 token
  74. )
  75. MintedCrowdsale()
  76. Crowdsale(rate, wallet, token)
  77. public
  78. {
  79. }
  80. }
  81. contract MyCrowdsaleDeployer {
  82. constructor()
  83. public
  84. {
  85. // create a mintable token
  86. ERC20Mintable token = new MyToken();
  87. // create the crowdsale and tell it about the token
  88. Crowdsale crowdsale = new MyCrowdsale(
  89. 1, // rate, still in TKNbits
  90. msg.sender, // send Ether to the deployer
  91. address(token) // the token
  92. );
  93. // transfer the minter role from this contract (the default)
  94. // to the crowdsale, so it can mint tokens
  95. token.addMinter(address(crowdsale));
  96. token.renounceMinter();
  97. }
  98. }
  99. ----
  100. [[allowancecrowdsale]]
  101. === AllowanceCrowdsale
  102. Use an xref:api:crowdsale.adoc#AllowanceCrowdsale[`AllowanceCrowdsale`] to send tokens from another wallet to the participants of the crowdsale. In order for this to work, the source wallet must give the crowdsale an allowance via the ERC20 xref:api:token/ERC20.adoc#IERC20-approve-address-uint256-[`approve`] method.
  103. [source,solidity]
  104. ----
  105. contract MyCrowdsale is Crowdsale, AllowanceCrowdsale {
  106. constructor(
  107. uint256 rate,
  108. address payable wallet,
  109. IERC20 token,
  110. address tokenWallet // <- new argument
  111. )
  112. AllowanceCrowdsale(tokenWallet) // <- used here
  113. Crowdsale(rate, wallet, token)
  114. public
  115. {
  116. }
  117. }
  118. ----
  119. Then after the crowdsale is created, don't forget to approve it to use your tokens!
  120. [source,solidity]
  121. ----
  122. IERC20(tokenAddress).approve(CROWDSALE_ADDRESS, SOME_TOKEN_AMOUNT);
  123. ----
  124. [[validation]]
  125. == Validation
  126. There are a bunch of different validation requirements that your crowdsale might be a part of:
  127. * xref:api:crowdsale.adoc#CappedCrowdsale[`CappedCrowdsale`] — adds a cap to your crowdsale, invalidating any purchases that would exceed that cap
  128. * xref:api:crowdsale.adoc#IndividuallyCappedCrowdsale[`IndividuallyCappedCrowdsale`] — caps an individual's contributions.
  129. * xref:api:crowdsale.adoc#WhitelistCrowdsale[`WhitelistCrowdsale`] — only allow whitelisted participants to purchase tokens. this is useful for putting your KYC / AML whitelist on-chain!
  130. * xref:api:crowdsale.adoc#TimedCrowdsale[`TimedCrowdsale`] — adds an xref:api:crowdsale.adoc#TimedCrowdsale-openingTime--[`openingTime`] and xref:api:Crowdsale.adoc#TimedCrowdsale-closingTime--[`closingTime`] to your crowdsale
  131. Simply mix and match these crowdsale flavors to your heart's content:
  132. [source,solidity]
  133. ----
  134. contract MyCrowdsale is Crowdsale, CappedCrowdsale, TimedCrowdsale {
  135. constructor(
  136. uint256 rate, // rate, in TKNbits
  137. address payable wallet, // wallet to send Ether
  138. IERC20 token, // the token
  139. uint256 cap, // total cap, in wei
  140. uint256 openingTime, // opening time in unix epoch seconds
  141. uint256 closingTime // closing time in unix epoch seconds
  142. )
  143. CappedCrowdsale(cap)
  144. TimedCrowdsale(openingTime, closingTime)
  145. Crowdsale(rate, wallet, token)
  146. public
  147. {
  148. // nice, we just created a crowdsale that's only open
  149. // for a certain amount of time
  150. // and stops accepting contributions once it reaches `cap`
  151. }
  152. }
  153. ----
  154. [[distribution]]
  155. == Distribution
  156. There comes a time in every crowdsale's life where it must relinquish the tokens it's been entrusted with. It's your decision as to when that happens!
  157. The default behavior is to release tokens as participants purchase them, but sometimes that may not be desirable. For example, what if we want to give users a refund if we don't hit a minimum raised in the sale? Or, maybe we want to wait until after the sale is over before users can claim their tokens and start trading them, perhaps for compliance reasons?
  158. OpenZeppelin is here to make that easy!
  159. [[postdeliverycrowdsale]]
  160. === PostDeliveryCrowdsale
  161. The xref:api:crowdsale.adoc#PostDeliveryCrowdsale[`PostDeliveryCrowdsale`], as its name implies, distributes tokens after the crowdsale has finished, letting users call xref:api:crowdsale.adoc#PostDeliveryCrowdsale-withdrawTokens_address-[`withdrawTokens`] in order to claim the tokens they've purchased.
  162. [source,solidity]
  163. ----
  164. contract MyCrowdsale is Crowdsale, TimedCrowdsale, PostDeliveryCrowdsale {
  165. constructor(
  166. uint256 rate, // rate, in TKNbits
  167. address payable wallet, // wallet to send Ether
  168. IERC20 token, // the token
  169. uint256 openingTime, // opening time in unix epoch seconds
  170. uint256 closingTime // closing time in unix epoch seconds
  171. )
  172. PostDeliveryCrowdsale()
  173. TimedCrowdsale(openingTime, closingTime)
  174. Crowdsale(rate, wallet, token)
  175. public
  176. {
  177. // nice! this Crowdsale will keep all of the tokens until the end of the crowdsale
  178. // and then users can `withdrawTokens()` to get the tokens they're owed
  179. }
  180. }
  181. ----
  182. [[refundablecrowdsale]]
  183. === RefundableCrowdsale
  184. The xref:api:crowdsale.adoc#RefundableCrowdsale[`RefundableCrowdsale`] offers to refund users if a minimum goal is not reached. If the goal is not reached, the users can xref:api:crowdsale.adoc#RefundableCrowdsale-claimRefund-address-payable-[`claimRefund`] to get their Ether back.
  185. [source,solidity]
  186. ----
  187. contract MyCrowdsale is Crowdsale, RefundableCrowdsale {
  188. constructor(
  189. uint256 rate, // rate, in TKNbits
  190. address payable wallet, // wallet to send Ether
  191. IERC20 token, // the token
  192. uint256 goal // the minimum goal, in wei
  193. )
  194. RefundableCrowdsale(goal)
  195. Crowdsale(rate, wallet, token)
  196. public
  197. {
  198. // nice! this crowdsale will, if it doesn't hit `goal`, allow everyone to get their money back
  199. // by calling claimRefund(...)
  200. }
  201. }
  202. ----