В этом уроке мы будем использовать React качестве нашего внешнего фреймворка. Поскольку это руководство в первую очередь ориентировано на разработку Web3, мы не будем тратить много времени на изучение основ React. Вместо этого мы сосредоточимся на добавлении функциональности в наш проект.
Обычно эта функция чеканки требует, чтобы вы передали две переменные в качестве параметров, во-первых , который указывает адрес, который будет получать ваш недавно созданный NFT, а во-вторых, NFT, строку recipienttokenURI, которая преобразуется в документ JSON, описывающий метаданные NFT.
Вот ссылка на ERC-721 NFT смарт контракта мы будем называть в этом учебнике. Если вы хотите узнать, как мы сделали это, мы настоятельно рекомендуем вам проверить наш другой учебник, «Как создать NFT»
Круто, теперь, когда мы понимаем, как работает NFT, давайте клонируем наши стартовые файлы!
Затем откройте свою копию в своем любимом редакторе кода (в Alchemy мы большие поклонники VSCode), а затем перейдите в свою папку :src
Весь код, который мы напишем, будет находиться в папке.
Мы будем редактировать компонент Minter.js и писать дополнительные файлы javascript, чтобы дать нашему проекту функциональность Web3.src
Запустите свой проект React
Чтобы запустить проект, перейдите в корневой каталог папки minter-starter-files и запустите npm install в терминале, чтобы установить зависимости проекта:
cd minter-starter-files
npm install
npm start
Компонент Minter.js
Вернемся в папку в нашем редакторе и откроем файл src Minter.js.
Очень важно, чтобы мы понимали все в этом файле, так как это основной компонент React, над которым мы будем работать.
//State variables
const [walletAddress, setWallet] = useState("");
const [status, setStatus] = useState("");
const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [url, setURL] = useState("");
useEffect(async () => { //TODO: implement
}, []);
const connectWalletPressed = async () => { //TODO: implement
};
const onMintPressed = async () => { //TODO: implement
};
//the UI of our component
return (
<div className="Minter">
<button id="walletButton" onClick={connectWalletPressed}>
{walletAddress.length > 0 ? (
"Connected: " +
String(walletAddress).substring(0, 6) +
"..." +
String(walletAddress).substring(38)
) : (
<span>Connect Wallet</span>
)}
</button>
<br></br>
<h1 id="title">????♂️ Alchemy NFT Minter</h1>
<p>
Simply add your asset's link, name, and description, then press "Mint."
</p>
<form>
<h2>???? Link to asset: </h2>
<input
type="text"
placeholder="e.g. https://gateway.pinata.cloud/ipfs/<hash>"
onChange={(event) => setURL(event.target.value)}
/>
<h2>???? Name: </h2>
<input
type="text"
placeholder="e.g. My first NFT!"
onChange={(event) => setName(event.target.value)}
/>
<h2>✍️ Description: </h2>
<input
type="text"
placeholder="e.g. Even cooler than cryptokitties ;)"
onChange={(event) => setDescription(event.target.value)}
/>
</form>
<button id="mintButton" onClick={onMintPressed}>
Mint NFT
</button>
<p id="status">
{status}
</p>
</div>
);
Вы можете бесплатно скачать и создать учетную запись Metamask здесь . Когда вы создаете учетную запись или если у вас уже есть учетная запись, обязательно переключитесь на «Тестовую сеть Ropsten» в правом верхнем углу (чтобы мы не имели дело с реальными деньгами).
Чтобы чеканить наши NFT (или подписать любые транзакции в блокчейне Ethereum), нам понадобится фальшивая Eth. Чтобы получить Eth, вы можете перейти к крану Ropsten и ввести адрес своей учетной записи Ropsten, а затем нажать «Отправить Ropsten Eth». Вскоре после этого вы должны увидеть Eth в своей учетной записи Metamask!
Чтобы дважды проверить наш баланс, давайте сделаем запрос eth_getBalance с помощью инструмента композитора Alchemy . Это вернет количество Eth в нашем кошельке. После того, как вы введете адрес своей учетной записи Metamask и нажмете «Отправить запрос», вы должны увидеть такой ответ:
{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}
In your interact.js file, add the following
export const connectWallet = async () => {
if (window.ethereum) {
try {
const addressArray = await window.ethereum.request({
method: "eth_requestAccounts",
});
const obj = {
status: "???????? Write a message in the text-field above.",
address: addressArray[0],
};
return obj;
} catch (err) {
return {
address: "",
status: "???? " + err.message,
};
}
} else {
return {
address: "",
status: (
<span>
<p>
{" "}
????{" "}
<a target="_blank" href={`https://metamask.io/download.html`}>
You must install Metamask, a virtual Ethereum wallet, in your
browser.
</a>
</p>
</span>
),
};
}
};
import { useEffect, useState } from "react";
import { connectWallet } from "./utils/interact.js";
const Minter = (props) => {
//State variables
const [walletAddress, setWallet] = useState("");
const [status, setStatus] = useState("");
const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [url, setURL] = useState("");
const connectWalletPressed = async () => {
const walletResponse = await connectWallet();
setStatus(walletResponse.status);
setWallet(walletResponse.address);
};
export const getCurrentWalletConnected = async () => {
if (window.ethereum) {
try {
const addressArray = await window.ethereum.request({
method: "eth_accounts",
});
if (addressArray.length > 0) {
return {
address: addressArray[0],
status: "???????? Write a message in the text-field above.",
};
} else {
return {
address: "",
status: "???? Connect to Metamask using the top right button.",
};
}
} catch (err) {
return {
address: "",
status: "???? " + err.message,
};
}
} else {
return {
address: "",
status: (
<span>
<p>
{" "}
????{" "}
<a target="_blank" href={`https://metamask.io/download.html`}>
You must install Metamask, a virtual Ethereum wallet, in your
browser.
</a>
</p>
</span>
),
};
}
};
import { useEffect, useState } from "react";
import {
connectWallet,
getCurrentWalletConnected //import here
} from "./utils/interact.js";
useEffect(async () => {
const {address, status} = await getCurrentWalletConnected();
setWallet(address)
setStatus(status);
}, []);
function addWalletListener() {
if (window.ethereum) {
window.ethereum.on("accountsChanged", (accounts) => {
if (accounts.length > 0) {
setWallet(accounts[0]);
setStatus("???????? Write a message in the text-field above.");
} else {
setWallet("");
setStatus("???? Connect to Metamask using the top right button.");
}
});
} else {
setStatus(
<p>
{" "}
????{" "}
<a target="_blank" href={`https://metamask.io/download.html`}>
You must install Metamask, a virtual Ethereum wallet, in your
browser.
</a>
</p>
);
}
}
useEffect(async () => {
const {address, status} = await getCurrentWalletConnected();
setWallet(address)
setStatus(status);
addWalletListener();
}, []);
npm install dotenv --save
vim .env
REACT_APP_PINATA_KEY = <pinata-api-key>
REACT_APP_PINATA_SECRET = <pinata-api-secret>
require('dotenv').config();
const key = process.env.REACT_APP_PINATA_KEY;
const secret = process.env.REACT_APP_PINATA_SECRET;
require('dotenv').config();
const key = process.env.REACT_APP_PINATA_KEY;
const secret = process.env.REACT_APP_PINATA_SECRET;
const axios = require('axios');
export const pinJSONToIPFS = async(JSONBody) => {
const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`;
//making axios POST request to Pinata ⬇️
return axios
.post(url, JSONBody, {
headers: {
pinata_api_key: key,
pinata_secret_api_key: secret,
}
})
.then(function (response) {
return {
success: true,
pinataUrl: "https://gateway.pinata.cloud/ipfs/" + response.data.IpfsHash
};
})
.catch(function (error) {
console.log(error)
return {
success: false,
message: error.message,
}
});
};
REACT_APP_PINATA_KEY = <pinata-key>
REACT_APP_PINATA_SECRET = <pinata-secret>
REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/<alchemy-key>
cd ..
npm install @alch/alchemy-web3
require('dotenv').config();
const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY;
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(alchemyKey);
require('dotenv').config();
const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY;
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(alchemyKey);
const contractABI = require('../contract-abi.json')
const contractAddress = "0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE";
export const mintNFT = async (url, name, description) => {
}
export const mintNFT = async(url, name, description) => {
//error handling
if (url.trim() == "" || (name.trim() == "" || description.trim() == "")) {
return {
success: false,
status: "❗Please make sure all fields are completed before minting.",
}
}
}
import {pinJSONToIPFS} from './pinata.js'
export const mintNFT = async(url, name, description) => {
//error handling
if (url.trim() == "" || (name.trim() == "" || description.trim() == "")) {
return {
success: false,
status: "❗Please make sure all fields are completed before minting.",
}
}
//make metadata
const metadata = new Object();
metadata.name = name;
metadata.image = url;
metadata.description = description;
//make pinata call
const pinataResponse = await pinJSONToIPFS(metadata);
if (!pinataResponse.success) {
return {
success: false,
status: "???? Something went wrong while uploading your tokenURI.",
}
}
const tokenURI = pinataResponse.pinataUrl;
}
window.contract = await new web3.eth.Contract(contractABI, contractAddress);
//set up your Ethereum transaction
const transactionParameters = {
to: contractAddress, // Required except during contract publications.
from: window.ethereum.selectedAddress, // must match user's active address.
'data': window.contract.methods.mintNFT(window.ethereum.selectedAddress, tokenURI).encodeABI()//make call to NFT smart contract
};
//sign the transaction via Metamask
try {
const txHash = await window.ethereum
.request({
method: 'eth_sendTransaction',
params: [transactionParameters],
});
return {
success: true,
status: "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" + txHash
}
} catch (error) {
return {
success: false,
status: "???? Something went wrong: " + error.message
}
}
export const mintNFT = async(url, name, description) => {
//error handling
if (url.trim() == "" || (name.trim() == "" || description.trim() == "")) {
return {
success: false,
status: "❗Please make sure all fields are completed before minting.",
}
}
//make metadata
const metadata = new Object();
metadata.name = name;
metadata.image = url;
metadata.description = description;
//pinata pin request
const pinataResponse = await pinJSONToIPFS(metadata);
if (!pinataResponse.success) {
return {
success: false,
status: "???? Something went wrong while uploading your tokenURI.",
}
}
const tokenURI = pinataResponse.pinataUrl;
//load smart contract
window.contract = await new web3.eth.Contract(contractABI, contractAddress);//loadContract();
//set up your Ethereum transaction
const transactionParameters = {
to: contractAddress, // Required except during contract publications.
from: window.ethereum.selectedAddress, // must match user's active address.
'data': window.contract.methods.mintNFT(window.ethereum.selectedAddress, tokenURI).encodeABI() //make call to NFT smart contract
};
//sign transaction via Metamask
try {
const txHash = await window.ethereum
.request({
method: 'eth_sendTransaction',
params: [transactionParameters],
});
return {
success: true,
status: "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" + txHash
}
} catch (error) {
return {
success: false,
status: "???? Something went wrong: " + error.message
}
}
}
import { connectWallet, getCurrentWalletConnected, mintNFT } from "./utils/interact.js";
const onMintPressed = async () => {
const { status } = await mintNFT(url, name, description);
setStatus(status);
};