My company has decided to crowdsale their PupperCoin token in order to help fund the network development. This network will be used to track dog breeding activity across the globe in a decentralized way, and allow humans to track the genetic trail of their pets. I have already worked with the necessary legal bodies and obtained the green light on creating a crowdsale open to the public. However, I am required to enable refunds if the crowdsale is successful and the goal is met, and I am only allowed to raise a maximum of 300 ether. The crowdsale will run for 24 weeks.
I will need to create an ERC20 token that will be minted through a Crowdsale
contract that I can leverage from the OpenZeppelin Solidity library.
This crowdsale contract will manage the entire process, allowing users to send ETH and get back PUP (PupperCoin). This contract will mint the tokens automatically and distribute them to buyers in one transaction.
It will need to inherit Crowdsale
, CappedCrowdsale
, TimedCrowdsale
, RefundableCrowdsale
, and MintedCrowdsale
.
I will conduct the crowdsale on the Kovan or Ropsten testnet in order to get a real-world pre-production test in.
Using Remix, I created a file called PupperCoin.sol
and create a standard ERC20Mintable
token.
I Created a new contract named PupperCoinCrowdsale.sol
, and prepared it like a standard crowdsale.
A simple standard ERC20Mintable
and ERC20Detailed
contract was used, and hardcoding 18
as the decimals
parameter, leaving the initial_supply
parameter alone. Hardcoding the decimals was not necessary however, since most use-cases match Ethereum's default, I can do so.
I ask that you follow the instructions listed below to ensure you are able to run the steps on your own and add the token to MetaMask. I will also include information such as the token parameters, token name, crowdsale cap, etc. for simplicity.
-
I simply needed to fill in the
PupperCoin.sol
file with this starter code, which contains the complete contract that I will need to work with in the Crowdsale. THIS FILE IS BEING REFERENCED BELOW AS: PUPPERCOIN -
Leverage the Crowdsale starter code, saving the file in Remix as
PupperCoinCrowdsale.sol
. This file will be used later in the process to test a tranaction and make comparions, as we see below. THIS IS THE FILE BEING REFERENCED BELOW AS: PUPPERCOINCROWDSALE -
Leverage the Crowdsale starter code, saving the file in Remix as
Crowdsale.sol
. This file will be used later in the process to test theFakenow
function, as we see below. THIS IS THE FILE BEING REFERENCED BELOW AS: CROWDSALE
You will need to bootstrap the contract by inheriting the following OpenZeppelin contracts:
-
Crowdsale
-
MintedCrowdsale
-
CappedCrowdsale
-
TimedCrowdsale
-
RefundablePostDeliveryCrowdsale
You will need to provide parameters for all of the features of your crowdsale, such as the name
, symbol
, wallet
for fundraising, goal
, etc. Feel free to configure these parameters to your liking.
You can hardcode a rate
of 1, to maintain parity with ether units (1 TKN per Ether, or 1 TKNbit per wei). If you'd like to customize your crowdsale rate, follow the Crowdsale Rate calculator on OpenZeppelin's documentation. Essentially, a token (TKN) can be divided into TKNbits just like ether can be divided into wei. When using a rate
of 1, just like 1000000000000000000 wei is equal to 1 ether, 1000000000000000000 TKNbits is equal to 1 TKN.
Since RefundablePostDeliveryCrowdsale
inherits the RefundableCrowdsale
contract, which requires a goal
parameter, you must call the RefundableCrowdsale
constructor from your PupperCoinCrowdsale
constructor, as well as the others. RefundablePostDeliveryCrowdsale
does not have its own constructor, so just use the RefundableCrowdsale
constructor that it inherits.
If you forget to call the RefundableCrowdsale
constructor, the RefundablePostDeliveryCrowdsale
will fail since it relies on it (it inherits from RefundableCrowdsale
), and does not have its own constructor.
When passing the open
and close
times, use now
and now + 24 weeks
to set the times properly from your PupperCoinCrowdsaleDeployer
contract.
In this contract, you will model the deployment based off of the ArcadeTokenCrowdsaleDeployer
you built previously. Leverage the OpenZeppelin Crowdsale Documentation for an example of a contract deploying another, as well as the starter code provided in Crowdsale.sol.
Test the crowdsale by sending ether to the crowdsale from a different account (not the same account that is raising funds), then once you confirm that the crowdsale works as expected, try to add the token to MetaMask and test a transaction. You can test the time functionality by replacing now
with fakenow
, and creating a setter function to modify fakenow
to whatever time you want to simulate. You can also set the close
time to be now + 5 minutes
, or whatever timeline you'd like to test for a shorter crowdsale.
Replacing now
with Fakenow
and setting the close
time to be now + 5 minutes
.
Compile Contract with the Fakenow
function from Crowdsale.sol file
MetaMask Confirmation with Fakenow
function from the Crowdsale.sol file.
Deployed Contracts testing the Fakenow
function from the Crowdsale.sol file.
Once you confirm that the crowdsale works as expected, try to add the token to MetaMask and test a transaction. You can add custom tokens in MetaMask from the Add custom token
feature. Make sure to purchase higher amounts of tokens in order to see the denomination appear in your wallets as more than a few wei worth.
Remember, the refund feature of RefundablePostDeliveryCrowdsale
only allows for refunds once the crowdsale is closed and the goal is met. See the OpenZeppelin RefundableCrowdsale documentation for details as to why this logic is used to prevent potential attacks on your token's value.
Deploy the PupperCoinCrowdsale to the Kovan or Ropsten testnet, and store the deployed address for later. Switch MetaMask to your desired network, and use the Deploy
tab in Remix to deploy your contracts. Take note of the total gas cost, and compare it to how costly it would be in reality. Since you are deploying to a network that you don't have control over, faucets will not likely give out 300 test ether. You can simply reduce the goal when deploying to a testnet to a much smaller amount, like 10,000 wei.
Compiled PupperCoinCrowdsale Contracts
MetaMask confirmation request.
Deployed PupperCoinCrowdsale Contracts to Ropsten Testnent
TokenAddress Contracts:
TokenSaleAddress Contracts:
PupperCoinCrowdsale Deployer Token address and token sale address.
Take note of the total gas cost, and compare it to how costly it would be in reality.
Test the crowdsale (using the PuppercoinCrowdsale file) by sending ether to the crowdsale from a different account
This is being done to show the functionality of the crontracts which were deployed in Crowdsale.
Deploy the crowdsale (utilizing the coded fakenow
function) to the Kovan or Ropsten testnet, and store the deployed address for later. Switch MetaMask to your desired network, and use the Deploy
tab in Remix to deploy your contracts. Take note of the total gas cost as before, and compare it to how costly it would be in reality. Since you are deploying to a network that you don't have control over, faucets will not likely give out 300 test ether. You can simply reduce the goal when deploying to a testnet to a much smaller amount, like 10,000 wei.
When sending ether to the contract, make sure you hit the goal
that you set, and finalize
the sale using the Crowdsale
's finalize
function. In order to finalize, isOpen
must return false (isOpen
comes from TimedCrowdsale
which checks to see if the close
time has passed yet). Since the goal
is 300 ether, you may need to send from multiple accounts. If you run out of prefunded accounts in Ganache, you can create a new workspace.
isOpen:
Take note of the total gas cost as before, and compare it to how costly it would be in reality.