QuickNode: Create a Token dApp using QuickNode
This guide was originally published on https://docs.filebase.com. Check out the Filebase documentation site for the latest Web3 tutorials using IPFS through Filebase.
Learn how to create a Token dApp using QuickNode.
What is QuickNode?
QuickNode is a platform that allows you to deploy dApps and other applications onto over 12 different blockchain networks with immediate provisioning and availability. QuickNode offers a variety of powerful tools and analytics, all presented through a single control panel dashboard.
Prerequisites:
- A cryptowallet, such as MetaMask.
- Install Node.js and npm.
- Download and install an IDE of your choice, such as VSCode.
- Sign up for a free Filebase account.
- Have your Filebase Access and Secret Keys. Learn how to view your access keys here.
- Create a Filebase Bucket. Learn how to create a bucket here.
1. First, download and install S3FS-FUSE on your local environment.
This tool is available on a Linux or macOS system.
2. Set up an Access Key file for use with S3FS-FUSE.
Set up a credentials file for S3FS at ${HOME}/.passwd-s3fs
. You will need to save your Filebase Access and Secret keys to this file and give it owner permissions. You can do so with the following commands:
echo ACCESS_KEY_ID:SECRET_ACCESS_KEY > ${HOME}/.passwd-s3fs
chmod 600 ${HOME}/.passwd-s3fs
ACCESS_KEY_ID is your Filebase Access key, and SECRET_ACCESS_KEY is your Filebase Secret key. For more information on Filebase access keys, see here.
3. Mount your bucket.
You can mount a Filebase IPFS bucket with the command:
s3fs mybucket /path/to/mountpoint -o passwd_file=${HOME}/.passwd-s3fs -o url=https://s3.filebase.com
- mybucket: name of your Filebase bucket
- /path/to/mountpoint
4. Now, navigate into the mounted Filebase bucket.
Create a react projet for your app:
npx create-react-app my-dapp
This will create a new project directory called my-dapp
. Navigate into this directory with the command:
cd my-dapp
Before we can install Hardhat, we’ll need to remove the default ‘README.md’ file, since it’ll cause a conflict during the Hardhat initialization. Use the following command to remove this file:
rm README.md
5. Install Hardhat with the following command:
npm install --save-dev "hardhat@^2.9.3" "@nomiclabs/hardhat-waffle@^2.0.0" "ethereum-waffle@^3.0.0" "chai@^4.2.0" "@nomiclabs/hardhat-ethers@^2.0.0" "ethers@^5.0.0"
6. Initialize the Hardhat setup prompt with the command:
npx hardhat
Select ‘Create a Basic Sample Project’ > ‘Create a Basic Project’, then select the current directory as the project root, and select ‘Y’ to include a .gitignore.
This command created some project files, such as the contracts and scripts directories.
7. Now, let’s head over to the QuickNode dashboard. Select ‘Create an endpoint’:
8. Select the Ethereum blockchain, then select the Rinkeby network.
9. Select any add-ons if desired.
This tutorial does not use any add-ons. Then select ‘Continue’.
10. Select your payment plan tier and enter your payment information. Then select ‘Pay & create’.
11. Once created, copy the HTTP endpoint API URL:
12. Next, take note of your cryptowallet’s private key.
If you are using Metamask, follow the instructions found here.
13. Navigate into your project’s contracts folder.
Remove the default file called Greeter.sol
, then create a new file called dApp.sol
.
Enter the following content in this new file:
//dApp.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
contract Token {
string public name = "dApp token";
string public symbol = "DAT";
// The fixed amount of tokens stored in an unsigned integer type variable.
uint256 public totalSupply = 1000000;
// An address type variable is used to store ethereum accounts.
address public owner;
// A mapping is a key/value map. Here we store each account balance.
mapping(address => uint256) balances;
/**
* Contract initialization.
*
* The `constructor` is executed only once when the contract is created.
* The `public` modifier makes a function callable from outside the contract.
*/
constructor() {
// The totalSupply is assigned to transaction sender, which is the account
// that is deploying the contract.
balances[msg.sender] = totalSupply;
owner = msg.sender;
}
/**
* A function to transfer tokens.
*
* The `external` modifier makes a function *only* callable from outside
* the contract.
*/
function transfer(address to, uint256 amount) external {
// Check if the transaction sender has enough tokens.
// If `require`'s first argument evaluates to `false` then the
// transaction will revert.
require(balances[msg.sender] >= amount, "Not enough tokens");
// Transfer the amount.
balances[msg.sender] -= amount;
balances[to] += amount;
}
/**
* Read only function to retrieve the token balance of a given account.
*
* The `view` modifier indicates that it doesn't modify the contract's
* state, which allows us to call it without executing a transaction.
*/
function balanceOf(address account) external view returns (uint256) {
return balances[account];
}
}
This contract creates a custom token that we’ll use for your dApp later. Replace the token’s public name and public symbol with whatever you’d like.
14. Now we want to deploy our contract.
To do this, we’ll deploy it on a local HardHat node. To get a list of the local, fake accounts created by HardHat, run the command:
npx hardhat node
Your console output will look like this:
From here, we can see we have 20 test accounts to use.
15. Then, open the hardhat.config.js
file in your project’s directory. Replace the content with the following code:
require("@nomiclabs/hardhat-ethers");
module.exports = {
defaultNetwork: "rinkeby",
networks: {
rinkeby: {
url: "QUICKNODE_API_URL",
accounts: "WALLET_PRIVATE_KEY"
}
},
solidity: {
version: "0.7.0",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
paths: {
sources: "./contracts",
tests: "./test",
cache: "./cache",
artifacts: "./artifacts"
},
mocha: {
timeout: 20000
}
}
Replace the following values:
- QUICKNODE_API_URL: Your QuickNode endpoint URL
- WALLET_PRIVATE_KEY: Your crypto wallet private key
16. Create a new file in your scripts directory called deploy.js
. Enter the following code inside that file:
//deploy.js
const hre = require("hardhat");
async function main() {
// ethers is avaialble in the global scope
const [deployer] = await hre.ethers.getSigners();
console.log(
"Deploying the contracts with the account:",
await deployer.getAddress()
);
console.log("Account balance:", (await deployer.getBalance()).toString());
const Token = await hre.ethers.getContractFactory("Token");
const token = await Token.deploy();
await token.deployed();
console.log("Token address:", token.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
17. Before we deploy this contract, we need some Rinkeby funds.
First, make sure your cryptowallet is configured to use the Rinkeby network. If you’re using Metamask, follow the instructions found here.
18. Then, head over to the Rinkeby faucet and request some test funds:
Once the transaction is complete, you should have 0.1ETH to use:
19. Next, it’s time to deploy our contract. To do this, use the following command:
npx hardhat run –network rinkeby scripts/deploy.js
Take note of this token address.
20. Click on your Metamask extension. Select ‘Import Tokens’:
21. Enter the Token Contract Address.
The Token Symbol should auto-populate after adding the contract address. Scroll down to ‘Token Decimal’. In this tutorial, our contract uses a decimal of 0.
22. Confirm that you’d like to import your token to Metamask:
23. Once imported, you’ll see your token’s balance in your wallet:
24. Next, start your react app with the command:
npx start
25. Navigate to the react app running at localhost:3000
:
26. Edit the src/App.js
file. Replace the existing content with the following code:
import './App.css';
import {ethers} from 'ethers'
import { useState } from 'react';
import TokenArtifact from "./artifacts/contracts/dApp.sol/Token.json"
const tokenAddress = "CONTRACT_ADDRESS"
function App() {
const [tokenData, setTokenData] = useState({})
const [amount, setAmount] = useState()
const [userAccountId, setUserAccountId] = useState()
async function requestAccount() {
await window.ethereum.request({ method: 'eth_requestAccounts' });
}
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
async function _intializeContract(init) {
// We first initialize ethers by creating a provider using window.ethereum
// When, we initialize the contract using that provider and the token's
// artifact. You can do this same thing with your contracts.
const contract = new ethers.Contract(
tokenAddress,
TokenArtifact.abi,
init
);
return contract
}
async function _getTokenData() {
const contract = await _intializeContract(signer)
const name = await contract.name();
const symbol = await contract.symbol();
const tokenData = {name, symbol}
setTokenData(tokenData);
}
async function senddAppToken() {
if (typeof window.ethereum !== 'undefined') {
await requestAccount()
const contract = await _intializeContract(signer)
const transaction = await contract.transfer(userAccountId, amount);
await transaction.wait();
console.log(`${amount} dAppToken has been sent to ${userAccountId}`);
}
}
async function getBalance() {
if (typeof window.ethereum !== 'undefined') {
const contract = await _intializeContract(signer)
const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' })
const balance = await contract.balanceOf(account);
console.log("Account Balance: ", balance.toString());
}
}
return (
<div className="App">
<header className="App-header">
<button onClick={_getTokenData}>get token data</button>
<h1>{tokenData.name}</h1>
<h1>{tokenData.symbol}</h1>
<button onClick={getBalance}>Get Balance</button>
<button onClick={senddAppToken}>Send dAppToken</button>
<input onChange={e => setUserAccountId(e.target.value)} placeholder="Account ID" />
<input onChange={e => setAmount(e.target.value)} placeholder="Amount" />
</header>
</div>
);
}
export default App;
Replace ‘CONTRACT_ADDRESS’ with your Token’s contract address from step 19. Once saved, will look like this:
Using this app, you’ll be able to get the current wallet balance of dApp token in our wallet, send dApp Tokens to other wallets such as the test wallets we got the addresses of earlier, and get dApp token data.
This react front-end of our app can be customized to your liking and can include other scripts, functions, or even images and animations.
Want to learn more about Filebase? You can sign up for a free account today to get started with IPFS.