π€ Smart contracts are kind of like "always on" vending machines that anyone can access. Let's make a decentralized, digital currency. Then, let's build an unstoppable vending machine that will buy and sell the currency. We'll learn about the "approve" pattern for ERC20s and how contract to contract interactions work.
π΅ Create
YourToken.sol
smart contract that inherits the ERC20 token standard from OpenZeppelin. Set your token to_mint()
1000 (* 10 ** 18) tokens to themsg.sender
. Then create aVendor.sol
contract that sells your token using a payablebuyTokens()
function.
π Edit the frontend that invites the user to
<input\>
an amount of tokens they want to buy. We'll display a preview of the amount of ETH (or USD) it will cost with a confirm button.
π It will be important to verify your token's source code in the block explorer after you deploy. Supporters will want to be sure that it has a fixed supply and you can't just mint more.
π The final deliverable is an app that lets users purchase and transfer your token. Deploy your contracts on your public chain of choice and then
yarn build
andyarn surge
your app to a public web server. Share the url in the Challenge 2 telegram channel.πΎ
𧫠Everything starts by βοΈ Editing YourToken.sol
in packages/hardhat/contracts
git clone https://github.com/scaffold-eth/scaffold-eth-challenges challenge-2-token-vendor
cd challenge-2-token-vendor
git checkout challenge-2-token-vendor
yarn install
π Edit your smart contract YourToken.sol
in packages/hardhat/contracts
You'll have three terminals up for:
yarn chain
(hardhat backend)
yarn start
(react app frontend)
yarn deploy
(to compile, deploy, and publish your contracts to the frontend)
π Visit your frontend at http://localhost:3000
π©βπ» Rerun
yarn deploy --reset
whenever you want to deploy new contracts to the frontend.
ignore any warnings, we'll get to that...
π©βπ» Edit
YourToken.sol
to inherit the ERC20 token standard from OpenZeppelin
Mint 1000 (* 10 ** 18) to your frontend address using the
constructor()
.
(Your frontend address is the address in the top right of http://localhost:3000)
You can
yarn deploy --reset
to deploy your contract until you get it right.
- Can you check the
balanceOf()
your frontend address in the YourToken of theDebug Contracts
tab? - Can you
transfer()
your token to another account and check that account'sbalanceOf
?
(Use an incognito window to create a new address and try sending to that new address. Use the transfer()
function in the Debug Contracts
tab.)
π©βπ» Edit the
Vendor.sol
contract with a payablebuyTokens()
function
Use a price variable named tokensPerEth
set to 100:
uint256 public constant tokensPerEth = 100;
π The
buyTokens()
function inVendor.sol
should usemsg.value
andtokensPerEth
to calculate an amount of tokens toyourToken.transfer()
tomsg.sender
.
π Emit event
BuyTokens(address buyer, uint256 amountOfETH, uint256 amountOfTokens)
when tokens are purchased.
Edit deploy/01_deploy_vendor.js
to deploy the Vendor
(uncomment Vendor deploy lines).
- When you try to buy tokens from the vendor, you should get an error: 'ERC20: transfer amount exceeds balance'
βοΈ Side Quest: send tokens from your frontend address to the Vendor contract address and then try to buy them.
βοΈ We can't hard code the vendor address like we did above when deploying to the network because we won't know the vender address at the time we create the token contract.
βοΈ So instead, edit
YourToken.sol
to transfer the tokens to themsg.sender
(deployer) in the constructor().
βοΈ Then, edit
deploy/01_deploy_vendor.js
to transfer 1000 tokens tovendor.address
.
await yourToken.transfer( vendor.address, ethers.utils.parseEther("1000") );
You can
yarn deploy --reset
to deploy your contract until you get it right.
(You will use the YourToken
UI tab and the frontend for most of your testing. Most of the UI is already built for you for this challenge.)
- Does the
Vendor
address start with abalanceOf
1000 inYourToken
on theDebug Contracts
tab? - Can you buy 10 tokens for 0.1 ETH?
- Can you transfer tokens to a different account?
π Edit
Vendor.sol
to inherit Ownable.
In deploy/01_deploy_vendor.js
you will need to call transferOwnership()
on the Vendor
to make your frontend address the owner
:
await vendor.transferOwnership("**YOUR FRONTEND ADDRESS**");
- Is your frontend address the
owner
of theVendor
?
π Finally, add a
withdraw()
function inVendor.sol
that lets the owner withdraw ETH from the vendor.
- Can only the
owner
withdraw the ETH from theVendor
?
- Can anyone withdraw? Test everything!
- What if you minted 2000 and only sent 1000 to the
Vendor
?
π©βπ« The hardest part of this challenge is to build your Vendor
to buy the tokens back.
π§ The reason why this is hard is the approve()
pattern in ERC20s. You can read more about the approve()
pattern here.
π First, the user has to call approve()
on the YourToken
contract, approving the Vendor
contract address to take some amount of tokens.
π€¨ Then, the user makes a second transaction to the Vendor
contract to sellTokens(uint256 amount)
.
π€ The Vendor
should call yourToken.transferFrom(msg.sender, address(this), theAmount)
and if the user has approved the Vendor
correctly, tokens should transfer to the Vendor
and ETH should be sent to the user.
π Edit
Vendor.sol
and add asellTokens()
function!
approve()
before calling sellTokens(uint256 amount)
.
π¨ Use the Debug Contracts
tab to call the approve and sellTokens() at first but then...
π Look in the App.jsx
for the extra approve/sell UI to uncomment!
- Can you sell tokens back to the vendor?
- Do you receive the right amount of ETH for the tokens?
- Should we disable the
owner
withdraw to keep liquidity in theVendor
? - It would be a good idea to display Sell Token Events. Create the
event
andemit
it in yourVendor.sol
and look atbuyTokensEvents
in yourApp.jsx
for an example of how to update your frontend.
- Now is a good time to run
yarn test
to run the automated testing function. It will test that you hit the core checkpoints. You are looking for all green checkmarks and passing tests!
π‘ Edit the defaultNetwork
in packages/hardhat/hardhat.config.js
, as well as targetNetwork
in packages/react-app/src/App.jsx
, to your choice of public EVM networks
π©βπ You will want to run yarn account
to see if you have a deployer address.
π If you don't have one, run yarn generate
to create a mnemonic and save it locally for deploying.
π° Use a faucet like faucet.paradigm.xyz to fund your deployer address (run yarn account
again to view balances)
π Run
yarn deploy
to deploy to your public network of choice (π wherever you can get β½οΈ gas)
π¬ Inspect the block explorer for the network you deployed to... make sure your contract is there.
π¦ Run yarn build
to package up your frontend.
π½ Upload your app to surge with yarn surge
(you could also yarn s3
or maybe even yarn ipfs
?)
π¬ Windows users beware! You may have to change the surge code in
packages/react-app/package.json
to just"surge": "surge ./build",
β If you get a permissions error yarn surge
again until you get a unique URL, or customize it in the command line.
π Traffic to your url might break the Infura rate limit, edit your key: constants.js
in packages/ract-app/src
.
π Show off your app by pasting the url in the Challenge 2 telegram channel π
Update the api-key
in packages/hardhat/package.json
. You can get your key here.
Now you are ready to run the
yarn verify --network your_network
command to verify your contracts on etherscan π°
π You may see an address for both YouToken and Vendor. You will want the Vendor address.
π This will be the URL you submit to πββοΈSpeedRunEthereum.com.
π¬ Problems, questions, comments on the stack? Post them to the Challenge 2 telegram channel