Now we want to allow crypto user to buy our musical NFTs with MockTokens. For this to happen we will need to do few steps: 1) re-deploy our MusicNFT contract with updated max number of NFTs; 2) set-up desired price per MusicNFT
; 3) send tokens from newly deployed MockUSDC
to user address; 4) create buyNFT
JavaScript function; 5) update our base.html
and lastly 6) update our crypt_user.html
.
1) Currently we have max NFT variable inside nft_package.sol
set to 5. What you need to do is to re-deploy both contracts by running deploy.py
script in our smart-contract/brownie_music_nfts/scripts
folder (or add setter function for this var inside smart contract and then re-deploy). You should pass let's say 1000 NFTs as second argument to MusciNFT.deploy()
function inside Brownie ./scripts/deploy.py
(and now this call need to look something like this musciNFTdeployed = MusicNFT.deploy(mockDeployed,1000,{'from':accountOne})
). And then from command line
brownie run deploy.py --network polygon-test
.
Ones we re-deploy our contracts with new argument we need to pass new contract addresses to all JavaScript files where we use them (in our case call_js.js
and node_interaction.js
) as well as to import new MockTokens
and MusciaNFTs
in MetaMask. Then we need to transfer new MockTokens
to buyer address before he perform new test NFT purches (all code for this we have in node_interaction_sc.js
. Or simply you can make transfers directly in MetaMask from deployer to NFT buyers address ones you import our contracts there).
Reason why we do all this is fact that if we keep max NFTs on 5 (what is current situation defined during our fist deploy), very soon we will hit max limit during testing phase and our calls to smart contract buyNFT
function will start to be reverted. To avoide this situation we need to re-deploy contracts and to assigne this new address to all relevenat places as we said earlier (for how to do all this you can go and check part no. 6 in this blog seria, smart contract deployment
).
Now, step two: setup desired MusicNFT price
1) CDN Ethers at the end (just bellow ending body tag) of base.html
template.
<script src="https://cdn.ethers.io/scripts/ethers-v4.min.js"
Then install with npm inside our root directory because we want to use it with Node (we will call some smart contract functions also from Node)
$npm install ethers@5.7.2
Here is important to install this specific version of Ethers because newer versions, after 6, tend to have problems with setting up InfuraProvider
Now is time to set new price of MusicalNFT and to transfer some MockUSDC to the user
Add to existing .env
1) infura API key; 2) signer privat keys and 3) network on which we would like to connect.
Also add .env
to .gitignore
(if you didn't already)
To get Infura API key you need to open profile on https://www.infura.io/
. Ones you are in just create new project and copy API keys. They should look something like this https://polygon-mumbai.infura.io/v3/52153a550fd8498c9041752356789010
. Be cerfule to pick up Mumbai
testnet from Polygon
, becuse we deploy our contracts there. Pass API key to INFURA_API_KEY
inside newly created .env
file.
Then for wallet private key go to MetaMask and select account from where you made contracts deployment. Then go to more
=> account details
=> show private keys
. Copy private key to your local enviroment varibale under SIGNER_PRIVATE_KEY
Last thing we need here is ETHEREUM_NETWORK=maticmum
(mainnet Polygon is matic
and testnet Mumabi is maticmum
)
Now let's load .env
file. For this we need to install packege dotenv
with command npm install dotenv
inside our project root directory. Ones we start to work with contracts we will require this package inside Node
to load .env
varibales.
Now open Node
$node
Welcome to Node.js v19.1.0.
Type ".help" for more information.
> require("dotenv").config()
> const network = process.env.ETHEREUM_NETWORK
> const infura = process.env.INFURA_API_KEY
> const private_key = process.env.SIGNER_PRIVATE_KEY
At this point we should have all varibales from our .env
loaded into Node and assigned to network, infura and private_key. What we need in this moment is to defin provider, signer (the one who will pay for transaction) and instancies of our contracts.
To make instance of provider, signer and contracts we always need to have follwoing ingredients: 1) ethereum network we intend to use; 2) in our case Infura API keys; 3) private keys of the signer account; 4) contract ABI and 5) address of deployed contract. With this elements you can first instatite: provider (gateway to Ethereum network); then signer (creator and signer of tx) and finally contract itself. And with ethers.js
this can be done very easly
Open separate terminal from the one where your Node is running. From project root directory copy:
$cp smart-contracts/brownie_musical_nfts/build/contracts/MockUSDC.json ./static
Then MusicNFT.json
(if you already didn't)
$cp smart-contracts/brownie_musical_nfts/build/contracts/MusicNFT.json ./static
Let's go back to Node and import contracts ABIs (we will need them in last step when we instatie contracts) from our Node
:
// Assigning ABI path to const
>const mockJsonFile = "./static/MockUSDC.json";
>const nftJsonFile = "./static/MusicNFT.json";
// Loading contract ABIs
>const mockAbi=JSON.parse(fs.readFileSync(mockJsonFile));
>const nftAbi=JSON.parse(fs.readFileSync(nftJsonFile));
At this point we have our ABI
and .env
varibles loaded in node
and we can move to define provider, signer and contracts instancie (ones we come to the last point we will be able to work with our deplyed contracts from within Node
).
step 1: Lets define provider
and contract.
Node:
> const { ethers } = require("ethers");
> const provider = new ethers.providers.InfuraProvider(
network,
infura);
step 2: define signer
>const signer = new ethers.Wallet(private_key, provider);
step 3: define MusicNFT
contract as well as MockToken
// contract address from Mumbai Polygon testnet (last deployment)
const nft_contract_address = "0x1D33a553541606E98c74a61D1B8d9fff9E0fa138"
const mock_usdc_address = "0xCeD9Fe6C4851acea6833DB0B7A0d2b892E4BBD5f"
// Let's instatiate both contracts
const contract_usdc = new ethers.Contract(mock_usdc_address, mockAbi.abi, signer);
const contract_nft = new ethers.Contract(nft_contract_address, nftAbi.abi, signer);
At this point we have our contract in Node
ready to be used. We can test that. Let's define one async function to check for name of our NFT.
// This function will return name of our NFT contract
getNftName = async () {
let result = await contract_nft.name();
console.log(result);
}
>getNftName()
Promise {
<pending>,
[Symbol(async_id_symbol)]: 1779,
[Symbol(trigger_async_id_symbol)]: 5
}
> MuscialNFT
Now we have fully functional contract in our node
and we can update NFT price and send to potential user some MockTokens
. Here we should notice that we are sending MockTokens
to user because only deployer of contract have all issued tokens. In real life situation user should have his own USDC or some other stabilcoins to pay for his NFTs. Last two steps: update NFT price, send MockTokens tokens to the buyer.
setNftPrice = async (price) => {
let result = await contract_nft.setNFTPrice(price);
}
// check for new price
var assert = require('assert');
// When you call this function please pass as argument price you set previously
checkNFTPrice = async (price) => {
let nft_price = await contract_nft.NFTPriceInUSDC();
console.log(Number(nft_price));
assert(Number(nft_price) == price, "Price was not upddted!");
}
// sending MockTokens to the customer
// const buyer = here you should add address of acocunt from which you will purches NFTs
const buyer = "0xB4A5D329e35F83a2e6AB5a74d56f5bD67EaB8d83"
const amount = BigInt(10e18)
sendTokenToBuyer = async (buyer, amount) => {
await mock_usdc_contract.transfer(buyer, amount)
}
*this JS code you can find in static/node_interaction_sc.js
Now when our customer have MockTokens to buy NFT, we will move to front-end and add functinoality to allow user to make this NFT purches.
For this to work we will have few step process. In first step user approve NFT contract to spend his MockTokens
in amount calulcated by simple multiplication of number of NFTs he wants to buy time current price of MusicNFT
. But because we are calling MockUSDC
transfeFrom
from within MusicNFT, MockUSDC
contract will se MusicNFT
as caller and it will expect MusicNFT
to have allowance on buyer USDC mock tokens. That is why first step is to call approve function of our MockToken
and then in second step to call buyNFT
function from MusicNFT
contract. If first step was ok, then MockUSDC
will se that caller (MusicNFT
contract) have rights to use buyer MockTokens
and he will then transfer in last step from buyer to MusicNFT
precise amount of tokens. Only then MusicNFT
contract will mint new NFTs to buyer addrees and emit event of confirmation. And with this we have all steps done!
And this function can look something like this (inside static/call_sc.js
file):
const nft_contract_address = "0x1D33a553541606E98c74a61D1B8d9fff9E0fa138"
const mock_usdc_address = "0xCeD9Fe6C4851acea6833DB0B7A0d2b892E4BBD5f"
const main_account = "0x273f4FCa831A7e154f8f979e1B06F4491Eb508B6"
const test_account = "0xB4A5D329e35F83a2e6AB5a74d56f5bD67EaB8d83"
// set provider
const provider = new ethers.providers.Web3Provider(window.ethereum);
// get signer this need to be test_account
const signer = provider.getSigner(0);
// see for original ABI file in github repo. you can copy from there
const ABI_NFT = [...]
// see for original ABI file in github repo. you can copy from there
const ABI_USDC = [..]
buyNFT = async () => {
// 1. get value user pass to use from front-end about number of NFTs he would like to buy
const numberOfNFTS = document.getElementById('NFT').value;
// 2. get current price of NFT from our smart contract and caluclate total price to be payed
const price = await nft_contract.NFTPriceInUSDC();
console.log(Number(price)*numberOfNFTS);
const total_price = Number(price)*numberOfNFTS
// 3. approve MusicNFT to transfer user MockTokens in total amount on contract
await usdc_contract.approve(nft_contract_address, total_price)
// 4. finally call buyNFT function from MusicNFT contract to buy and mint new NFTs to user account
await nft_contract.buyNFT("uri://music_nft_token", numberOfNFTS)
}
This newly created call_js.js
script should be added to our base.html
{% load static %}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Muscial NFT</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<script type="text/javascript" src="{% static 'connect_wallet.js' %}"></script>
<link rel="shortcut icon" type="image" href="{% static 'favicon.ico' %}" >
<!-- <script type="module" src="{% static 'call_sc.js' %}"></script> -->
</script>
</head>
<body onload="checkMetaMaskState()">
<body>
{% include "navbar.html"%}
<div class="container ">
<br/>
<br/>
{% if messages %}
{% for message in messages%}
<div class="alert alert-warning alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor%}
{% endif %}
{% block content %}
{% endblock content %}
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
<script src="https://cdn.ethers.io/scripts/ethers-v4.min.js"
charset="utf-8"
type="text/javascript"></script>
<script type="text/javascript" src="{% static 'call_sc.js' %}"></script>
</html>
And then last step is to connect onclick
inside our crypto_user.html
to newly created JS funciton buyNFT()
. This will allow user to pass number of NFTs he would like to buy, then in backgorund we will calculate total price, approve tokens to MusicNFT
contract, transfer MockTokens to MusciNFT
contract and in last step mint new NFT (or NFTs, depends from number user give as input to front-end) and assigne to buyer address. With this we have buy NFT with crypto fully in place.
<table class="table table-hover">
<thead class="table-dark">
<tr>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">Total no. NFTs</th>
<th scope="col">Means of payment</th>
<th scope="col">Buy NFT</th>
</tr>
</thead>
<tbody>
<tr>
<td> {{ customer.first_name }} {{ customer.last_name }} </td>
<td> {{ customer.email }} </td>
<td> {{ customer.total_no_of_nfts }} </td>
<td>
<form action="" method="post">
{%csrf_token%}
{{orderForm.as_p}}
<input type="submit" value="Save" class="btn btn-secondary">
</form>
</td>
<td>
<form action="" method="post">
{%csrf_token%}
<label for="NFT">Number of NFTs</label>
<input type="text" id="NFT" ><br>
</form>
<input onclick="buyNFT()" type="submit" value="Buy" class="btn btn-secondary">
</td>
</tr>
</tbody>
</table>
{% for card in metadata%}
{% if forloop.counter0|divisibleby:3 %} <div class="row">{% endif %}
<div class="card m-5 p-2" style="width: 18rem;">
{% load static %}
<img src="{% static 'nft/'%}{{card.cover_file_name}}" class="card-img-top" alt="..." width="50" height="200"/>
<div class="card-body">
<h5 class="card-title">{{card.name}}</h5>
<br>
<p class="card-text">{{card.description}}</p>
</div>
</div>
{% if forloop.counter|divisibleby:3 or forloop.last %}</div> {% endif %}
<br>
{% endfor %}
If everything went well, ones user login into our platform he will be able to give number of NFTs he wants to buy and then press buy
button. MetaMask will pop-up aksking him first for approval for MockTokens
to MusicNFT
contract, and then in second step ask to confirm start of buyNFT
transaction. Something like this:
In next blog, we will listen for emited minting event on our backend and updated our database and front-end accordingly.
In this repo you can find latest code
Top comments (0)