- Backend
- Set Up Development Environment
- Create Base Project
- Create Infura Account
- Create Pinata Account
- Copy Mnemonic from MetaMask
- Install Project Dependencies
- Create Contract and Migration
- Create NFT Image
- Create Sample Metadata File
- Compile Contract
- Migrate to Rinkeby Network
- Test Deployed Contract in Console
- Verify the Shoutout on the Blockchain
- Add New Token to MetaMask
- Frontend
- Deploy
- Resources & Credits
- Install Truffle:
npm install -g truffle
- Install Ganache:
npm install -g ganache-cli
- Install Fleek:
npm install -g @fleekhq/fleek-cli
- Download and install the IPFS CLI by following these instructions.
- Install the Blockchain Development Kit for Ethereum extension in VSCode.
mkdir shoutouts.eth
cd shoutouts.eth
truffle init
npm init
cp .env.sample .env
- <infura.io>: Sign up and create a new project. Give it the same name as your codebase.
- Under the Keys heading, copy
Project ID
to your clipboard. - Open your
.env
file and paste the key to the right ofINFURA_KEY=
.
- Visit <pinata.cloud> and create a new account.
- Click API Keys on left sidebar.
- Click
New Key
- Turn on
Admin
level access. - The name of the key should match the project name.
- Click
Create Key
. A modal dialog will appear. ClickCopy All
and store the values somewhere safe, like in a password manager. - Copy
JWT
to the clipboard.
- Open Chrome and log in to MetaMask.
- Click Account Icon on Top Right > Choose
Settings
>Security & Privacy
- Click
Reveal Secret Recovery Phrase
and enter your MetaMask password. - Copy the phrase to your clipboard.
- Open your
.env
file and paste the mnemonic to the right ofMNEMONIC=
.
npm install @openzeppelin/contracts dotenv @truffle/[email protected]
touch contracts/ShoutoutContract.sol
touch migrations/2_deploy_contracts.js
-
Visit https://badge.design to generate an image for your new token.
-
Save a transparent PNG of the image in the root of your project.
-
Open the IPFS Desktop application.
-
Add the image to IPFS:
$ ipfs init generating ED25519 keypair...done peer identity: 12D3KooWK9J7NfuE9wGKWpERRLWDhcpUwhfduu4osbhV2TamQeRZ initializing IPFS node at /Users/droxey/.ipfs $ ipfs daemon Initializing daemon... go-ipfs version: 0.9.0 Repo version: 11 System version: arm64/darwin Golang version: go1.16.5 Swarm listening on /ip4/127.0.0.1/tcp/4001 Swarm listening on /ip4/127.0.0.1/udp/4001/quic Swarm listening on /ip4/192.168.0.118/tcp/4001 Swarm listening on /ip4/192.168.0.118/udp/4001/quic Swarm listening on /ip4/192.168.0.173/tcp/4001 Swarm listening on /ip4/192.168.0.173/udp/4001/quic Swarm listening on /ip6/::1/tcp/4001 Swarm listening on /ip6/::1/udp/4001/quic Swarm listening on /ip6/fdc4:67f5:8ffa:3:144b:dbb3:b143:89a0/tcp/4001 Swarm listening on /ip6/fdc4:67f5:8ffa:3:144b:dbb3:b143:89a0/udp/4001/quic Swarm listening on /ip6/fdc4:67f5:8ffa:3:cf5:b55b:8447:a756/tcp/4001 Swarm listening on /ip6/fdc4:67f5:8ffa:3:cf5:b55b:8447:a756/udp/4001/quic Swarm listening on /p2p-circuit Swarm announcing /ip4/127.0.0.1/tcp/4001 Swarm announcing /ip4/127.0.0.1/udp/4001/quic Swarm announcing /ip4/192.168.0.173/tcp/4001 Swarm announcing /ip4/192.168.0.173/udp/4001/quic Swarm announcing /ip4/71.204.188.97/udp/4001/quic Swarm announcing /ip6/::1/tcp/4001 Swarm announcing /ip6/::1/udp/4001/quic API server listening on /ip4/127.0.0.1/tcp/5001 WebUI: http://127.0.0.1:5001/webui Gateway (readonly) server listening on /ip4/127.0.0.1/tcp/8080 Daemon is ready
-
In a new terminal tab, run
ipfs add shoutout.png
to add the token's image to IPFS:$ ipfs add shoutout.png added QmRGhvqTPvx8kgMSLFdPaCysKvhtP5GV5MsKDmTx3v2QxT shoutout.png 10.79 KiB / 10.79 KiB [=======================================================================================================================================================] 100.00%
Copy the hash you receive when adding the image. This is the address of your image in IPFS.
-
Pin the image in Pinata:
$ ipfs pin remote service add pinata https://api.pinata.cloud/psa PASTE_JWT_KEY $ ipfs pin remote add --service=pinata --name=shoutout.png QmRGhvqTPvx8kgMSLFdPaCysKvhtP5GV5MsKDmTx3v2QxT
This sample metadata can be used prior to developing a metadata API, and exists to prove that we can mint our token successfully.
-
Add the file to IPFS:
$ ipfs add sample-metadata.json added QmVGmwHFxzcrdygWMKPdqp3Q37BBNsGc4M1f6KVRitJ49j sample-metadata.json 196 B / 196 B [===============================================================================================================================================================] 100.00%
-
Pin the file in Pinata:
$ ipfs pin remote add --service=pinata --name=sample-metadata.json QmVGmwHFxzcrdygWMKPdqp3Q37BBNsGc4M1f6KVRitJ49j CID: QmVGmwHFxzcrdygWMKPdqp3Q37BBNsGc4M1f6KVRitJ49j Name: sample-metadata.json Status: pinned
$ truffle compile
Compiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/ShoutoutContract.sol
> Compiling @openzeppelin/contracts/token/ERC721/ERC721.sol
> Compiling @openzeppelin/contracts/token/ERC721/IERC721.sol
> Compiling @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol
> Compiling @openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol
> Compiling @openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol
> Compiling @openzeppelin/contracts/utils/Address.sol
> Compiling @openzeppelin/contracts/utils/Context.sol
> Compiling @openzeppelin/contracts/utils/Counters.sol
> Compiling @openzeppelin/contracts/utils/Strings.sol
> Compiling @openzeppelin/contracts/utils/introspection/ERC165.sol
> Compiling @openzeppelin/contracts/utils/introspection/IERC165.sol
> Artifacts written to /Users/droxey/dev/repos/shoutouts.eth/build/contracts
> Compiled successfully using:
- solc: 0.8.0+commit.c7dfd78e.Linux.g++
$ truffle migrate --network rinkeby
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Starting migrations...
======================
> Network name: 'rinkeby'
> Network id: 4
> Block gas limit: 10000000 (0x989680)
1_initial_migration.js
======================
Deploying 'Migrations'
----------------------
> transaction hash: 0x3d7e525301cb45c617ddfc1977397e9f5ee74b1f4679851ada897994bf08e9e4
> Blocks: 1 Seconds: 12
> contract address: 0xAf47d95c283b8d0333ADEDAF0bB22916ceFFA7FB
> block number: 8841341
> block timestamp: 1624856784
> account: 0xe4233b38fEa3B8c27ea9F54d5A90ec27cEe7F42C
> balance: 1.556150107
> gas used: 251534 (0x3d68e)
> gas price: 1 gwei
> value sent: 0 ETH
> total cost: 0.000251534 ETH
Pausing for 2 confirmations...
------------------------------
> confirmation number: 1 (block: 8841342)
> confirmation number: 2 (block: 8841343)
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.000251534 ETH
2_deploy_contracts.js
=====================
Deploying 'ShoutoutContract'
----------------------------
> transaction hash: 0xea92115a2eb4d8d6164bcbb52714e7c42fccb9de09a060457b128fcf1877ffc6
> Blocks: 1 Seconds: 12
> contract address: 0x001Fd4dd63455F327BE312C6bf7c77c5A63BEe9e
> block number: 8841345
> block timestamp: 1624856844
> account: 0xe4233b38fEa3B8c27ea9F54d5A90ec27cEe7F42C
> balance: 1.553639137
> gas used: 2465052 (0x259d1c)
> gas price: 1 gwei
> value sent: 0 ETH
> total cost: 0.002465052 ETH
Pausing for 2 confirmations...
------------------------------
> confirmation number: 1 (block: 8841346)
> confirmation number: 2 (block: 8841347)
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.002465052 ETH
Summary
=======
> Total deployments: 2
> Final cost: 0.002716586 ETH
$ truffle console --network rinkeby
truffle(rinkeby)> let instance = await ShoutoutContract.deployed()
truffle(rinkeby)> let result = await instance.awardItem("0xe4233b38fEa3B8c27ea9F54d5A90ec27cEe7F42C", "https://gateway.pinata.cloud/ipfs/QmVGmwHFxzcrdygWMKPdqp3Q37BBNsGc4M1f6KVRitJ49j")
- Open https://rinkeby.etherscan.io
- Paste the deployed contract address in the search bar or visit https://rinkeby.etherscan.io/address/DEPLOYED_CONTRACT_ADDRESS (https://rinkeby.etherscan.io/address/0x001Fd4dd63455F327BE312C6bf7c77c5A63BEe9e)
- Should see two transactions --- one to create the contract, and one to award an item.
- Open MetaMask and click Add Token
- Paste the deployed contract address in the
Token Contract Address
field. - Set Token Decimal to 0.
- Click
Next
- Click
Add Token
- Sign up for an account on fleek.co (one per team)
- Add team members by going to the Members link on the sidebar.
- Go to the Storage tab on the sidebar and click Create Folder.
- Name the folder
metadata
. - Generate a Storage API Key by clicking your name at the bottom of the page, then choosing Settings. Scroll to the bottom to generate a new API key.
- Use these keys in your JS to upload files on Fleek!
- Add
app
folder to project. Name is important! - Add
app/js/index.js
to the project. - Add
app/index.html
to the project.
Located in app/js/index.js
.
-
Open your
package.json
file and paste thescript
object found below (example:"scripts": { "lint": "eslint ./", "build": "webpack", "dev": "webpack-dev-server" },
-
At the bottom of
package.json
, paste the followingdevDependencies
for webpack (example:"devDependencies": { "copy-webpack-plugin": "^5.0.5", "webpack": "^4.41.2", "webpack-cli": "^3.3.10", "webpack-dev-server": "^3.9.0" }
-
Run
npm install
to install the newdevDependencies
. -
Copy
webpack.config.js
from this repo and customize it based on your needs.
In the app
directory, we build and run our frontend.
Smart contract changes must be manually recompiled and migrated.
Open another terminal and run the following commands to build the frontend:
cd app
npm run dev
Open a fresh terminal and run the following commands to build the frontend:
cd app
npm run build
The production build will be in the dist
folder after running npm run build
in the app
folder.
This build can be deployed anywhere static sites can be hosted!
Visit http://localhost:8080 in your browser to test your new full stack DApp!
-
Visit <fleek.co> in your browser and create an account.
-
Run
fleek login
to sign in from your terminal before initializing or deploying. -
Initialize fleek for your project:
$ fleek site:init 'This command will walk you through initializing a new or existing fleek site config in the current directory.' 'Successfully initializing a site would create a .fleek.json file in your directory.' 'You can then use `fleek site:deploy` to deploy changes made to your publish directory.' ? Which team you wanna use? username-team ...fetching list of sites for team: username-team ? Which site you wanna use? Create new site ? Select the public directory for deployment ('.' for current directory): . publishing files in /Users/droxey/dev/repos/shoutouts.eth to IPFS packaging site contents... uploading... Site cid is QmULzpjfa3CbBRktd2744jXYqWbYjkHTYd9sbvthzkTkzx creating new site Fleek site late-dust-7939 was successfully initialized.
-
Deploy the entire project to IPFS:
$ fleek site:deploy publishing files in /Users/droxey/dev/repos/shoutouts.eth to IPFS packaging site contents... uploading... Site cid is QmTRnEN27nJLpXFiXEZ8NTr4wE4q5ssg9F9hqztnHrjYso New deployment has been triggered. View deployment here: https://app.fleek.co/#/sites/late-dust-7939/deploys/2021-06-28T04:34:52.361Z?accountId=username-team