News & Blog
Stay informed with the latest insights, trends, and updates from SolanaLink.
Stay informed with the latest insights, trends, and updates from SolanaLink.

This report provides an exhaustive, expert-level technical guide for developers on the end-to-end process of creating, deploying, and minting a BEP-721 Non-Fungible Token (NFT) on the Binance Smart Chain (BSC). The methodology detailed herein exclusively utilizes TypeScript for scripting and command-line operations, reflecting professional software development standards for building robust and maintainable blockchain applications.
Before engaging in the practical aspects of development, a deep understanding of the underlying token standard is paramount. The BEP-721 standard is the cornerstone protocol for unique digital assets within the BSC ecosystem, and its architecture dictates the capabilities and constraints of the NFTs created.
The BEP-721 token standard is the protocol that supports the creation and management of non-fungible tokens on the BNB Chain.1 The term "non-fungible" signifies that each token is unique and not interchangeable with any other token, a stark contrast to fungible tokens like BEP-20, where any token is identical to another.3 This uniqueness is the core architectural principle of BEP-721.
The mechanism for achieving this uniqueness is the assignment of a unique identifier, the tokenId, to each token minted under a given smart contract.1 While a BEP-20 contract can issue billions of identical tokens, a BEP-721 contract manages a collection of distinct items, each tracked by its
tokenId. This architecture enables the tokenization of ownership for unique digital or physical assets, such as digital art, collectibles, in-game items, or even tokenized real estate.3
A critical aspect of the BEP-721 standard is that it is a direct extension of Ethereum's ERC-721 standard.1 This is not a superficial similarity; it is a fundamental design choice with profound implications for the developer ecosystem. The Binance Smart Chain is designed to be fully compatible with the Ethereum Virtual Machine (EVM), the runtime environment for Ethereum smart contracts.3
This EVM compatibility means that smart contracts written in Solidity for Ethereum's ERC-721 standard can be deployed on the Binance Smart Chain as BEP-721 tokens with minimal to no modification.6 The same development tools, programming languages (like Solidity), and client-side libraries (like Ethers.js) are applicable to both ecosystems. This strategic decision was instrumental in BSC's rapid growth, as it lowered the barrier to entry for the vast pool of existing Ethereum developers who were seeking alternatives due to Ethereum's high transaction fees and network congestion.5
The primary practical differences for a developer are environmental rather than logical:
The knowledge and skills acquired in developing BEP-721 tokens are, therefore, highly transferable to Ethereum and other EVM-compatible chains like Polygon, Avalanche, and Base, making this a valuable and broadly applicable area of expertise.
The BEP-721 standard, inheriting from EIP-721, mandates a specific set of functions and events that a compliant smart contract must implement. This standardization ensures that NFTs are interoperable with wallets, marketplaces, and other applications within the ecosystem.4
Key functions include:
The separation of on-chain ownership data (the tokenId and its owner) from the potentially large and complex off-chain metadata (the asset's details) is a deliberate architectural pattern. Storing extensive data like high-resolution images or detailed attributes directly on the blockchain is prohibitively expensive due to gas costs.10 The
tokenURI function serves as an immutable, on-chain link between the token and its off-chain descriptive data, providing a cost-effective and scalable solution.10
Choosing the right development environment and tools is a strategic decision that significantly impacts productivity, code quality, and project maintainability. For professional blockchain development, Hardhat combined with TypeScript offers a superior and robust stack.
Hardhat is a state-of-the-art development environment for Ethereum and EVM-compatible chains.12 It facilitates every stage of the smart contract lifecycle, including compiling, deploying, testing, and debugging. Its key advantages include:
While Truffle is another established development framework, Hardhat is often preferred in modern projects for its flexibility, performance, and stronger alignment with contemporary JavaScript and TypeScript development practices.12
TypeScript, a superset of JavaScript, introduces static typing to the development process. For writing deployment scripts, automated tests, and backend interactions with smart contracts, using TypeScript offers significant benefits:
The process of creating a BEP-721 NFT can be visualized as a sequential workflow:
| Feature | Hardhat | Truffle | Expert Recommendation |
|---|---|---|---|
| TypeScript Support | Native, first-class integration with type-safe configurations and plugins.12 | Supported, but often requires more manual configuration and can be less seamless.18 | Hardhat is the superior choice for projects prioritizing TypeScript. |
| Local Test Network | Built-in Hardhat Network with advanced debugging features like console.log in Solidity.12 | Typically relies on an external tool, Ganache, for a local blockchain GUI and CLI.20 | Hardhat provides a more integrated and powerful debugging experience out of the box. |
| Debugging | Provides detailed Solidity stack traces for reverted transactions, simplifying error diagnosis.12 | Debugging capabilities are present but are often considered less intuitive than Hardhat's.18 | Hardhat offers a more developer-friendly and informative debugging workflow. |
| Plugin Ecosystem | Modern, flexible, and growing ecosystem of community-developed plugins.15 | Mature ecosystem, but can sometimes feel more monolithic compared to Hardhat's composable approach.18 | Both are strong, but Hardhat's plugin architecture aligns better with modern development. |
| Community Momentum | Has seen a significant rise in adoption and is often the default choice for new projects.12 | A long-standing and respected framework, but has seen some of its market share shift to Hardhat.17 | Hardhat currently has stronger momentum and is more aligned with the evolving toolchain. |
This section provides a meticulous, step-by-step guide to configuring a professional-grade development environment, which is a prerequisite for building and deploying the BEP-721 smart contract.
A correctly configured workstation and crypto wallet are the foundational elements of the development setup.
Before proceeding, ensure the development machine has Node.js installed. It is recommended to use a Long-Term Support (LTS) version, preferably v22 or later, to ensure compatibility with modern development tools.21 Node.js can be downloaded from its official website. Its installation includes the Node Package Manager (npm), which will be used to manage project dependencies.
A non-custodial crypto wallet is required to interact with the Binance Smart Chain. MetaMask is the industry-standard browser extension wallet for EVM-compatible chains.22
By default, MetaMask is configured for the Ethereum network. To develop on BSC, the BSC Testnet must be added as a custom network.22
Deploying contracts and sending transactions on any blockchain requires payment of gas fees. On the BSC Testnet, these fees are paid with test BNB (tBNB), a token with no real-world value used exclusively for development and testing.24
To obtain tBNB, use a BSC Testnet Faucet. A popular one is available at https://testnet.bnbchain.org/faucet-smart. The process typically involves pasting the wallet address from MetaMask into the faucet's input field and completing a captcha to receive a small amount of tBNB.
With the foundational tools in place, the next step is to create and structure the project using Hardhat.
The Hardhat initialization process installs the hardhat-toolbox, which bundles many essential plugins. However, two additional packages are required for a professional workflow:
Install these dependencies using npm:
Bash
npm install @openzeppelin/contracts dotenv
After initialization, Hardhat creates a standardized project structure 13:
The hardhat.config.ts file is the control center of the project. It must be configured to connect to the BSC Testnet and manage the credentials required for deployment.
The configuration involves defining the Solidity compiler version, setting up the BSC Testnet network details, and adding configuration for BscScan to enable automated contract verification.
To deploy contracts, the deployment script needs access to a wallet's private key to sign transactions. To verify contracts on BscScan, an API key is required. Hard-coding this sensitive information directly into the hardhat.config.ts file is a severe security risk, as it could be accidentally committed to a public version control system like GitHub.
The best practice is to use a .env file 16:
Replace the entire content of hardhat.config.ts with the following complete and robust configuration. This configuration imports the necessary types, loads the environment variables using dotenv, and defines the bsc_testnet network and etherscan verification settings.
TypeScript
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import "dotenv/config";
// Ensure that the environment variables are set.
const privateKey = process.env.BSC_TESTNET_PRIVATE_KEY;
if (!privateKey) {
throw new Error("Please set your BSC_TESTNET_PRIVATE_KEY in a.env file");
}
const bscscanApiKey = process.env.BSCSCAN_API_KEY;
if (!bscscanApiKey) {
throw new Error("Please set your BSCSCAN_API_KEY in a.env file");
}
const config: HardhatUserConfig = {
solidity: "0.8.24",
networks: {
hardhat: {
// Configuration for the local Hardhat Network
},
bsc_testnet: {
url: "https://data-seed-prebsc-1-s1.binance.org:8545/",
chainId: 97,
accounts: [privateKey],
gasPrice: 20000000000, // 20 Gwei
},
},
etherscan: {
apiKey: {
bscTestnet: bscscanApiKey,
},
},
};
export default config;
This configuration establishes a professional, multi-environment workflow. The networks object decouples the application logic from the deployment target. By simply changing the --network flag in a command (e.g., --network bsc_testnet), the same deployment script can be targeted to different environments without any code changes, embodying a powerful abstraction that is a hallmark of the Hardhat framework.13
| Parameter | BSC Testnet Value | BSC Mainnet Value |
|---|---|---|
| Network Name | Binance Smart Chain Testnet | Binance Smart Chain Mainnet |
| RPC URL | https://data-seed-prebsc-1-s1.binance.org:8545/ | https://bsc-dataseed.binance.org/ |
| Chain ID | 97 | 56 |
| Currency Symbol | tBNB | BNB |
| Block Explorer URL | https://testnet.bscscan.com | https://bscscan.com |
This part transitions from environment setup to the core development tasks: authoring the on-chain smart contract and preparing the off-chain metadata that defines the NFT.
Writing a smart contract from scratch is a complex and high-risk endeavor. Security vulnerabilities can lead to irreversible financial loss. Therefore, it is a non-negotiable best practice to build upon a foundation of secure, audited, and community-vetted code.
OpenZeppelin provides a library of modular, reusable, and secure smart contract implementations for the most common token standards, including ERC-721.10 By inheriting from OpenZeppelin's base contracts, developers can significantly reduce security risks and ensure compliance with the BEP-721 standard. The
@openzeppelin/contracts package was installed in the previous section for this purpose.
A new Solidity file named MyNFT.sol should be created inside the contracts/ directory. The default Lock.sol file created by Hardhat can be deleted. The following code provides a robust and production-ready template for a BEP-721 contract.
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract MyNFT is ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor(address initialOwner)
ERC721("My Awesome NFT", "MANFT")
Ownable(initialOwner)
{}
function safeMint(address to, string memory uri) public onlyOwner {
uint256 tokenId \= \_tokenIds.current();
\_tokenIds.increment();
\_safeMint(to, tokenId);
\_setTokenURI(tokenId, uri);
}
}
Code Analysis:
The value and richness of an NFT are defined by its metadata. This metadata links the on-chain token to its off-chain representation, such as a piece of art, a collectible card, or a game item.
The tokenURI function in the contract is expected to point to a JSON file that conforms to a specific schema.9 This standardization allows marketplaces like OpenSea and wallets to correctly display the NFT's properties.
A typical metadata JSON file looks like this:
JSON
{
"name": "My First NFT",
"description": "An awesome digital creation stored decentrally.",
"image": "ipfs://QmQEVVLJUR1WLN15S49rzDJsSP7za9DxeqpUzWuG4aondg",
"attributes":
}
Storing NFT assets on a centralized server (e.g., AWS S3) is considered an anti-pattern. If the server goes down or the company hosting it ceases to exist, the link breaks, and the NFT effectively points to nothing. This undermines the principle of permanence that blockchain technology offers.
The InterPlanetary File System (IPFS) is the industry-standard solution for decentralized storage.9 IPFS is a peer-to-peer network for storing and sharing data in a distributed file system. Files on IPFS are content-addressed, meaning the URI of a file is a cryptographic hash of its content. This ensures that if the file is ever changed, its URI will also change, providing a form of verifiability.
To ensure data persistence on IPFS, files must be "pinned" by one or more nodes. Services like Pinata, NFT.Storage, or QuickNode's IPFS service provide an easy way to upload and pin files.32
The process for preparing the metadata is as follows:
This final part brings all the preceding elements together, executing the scripts to deploy the smart contract, mint the NFT on the BSC Testnet, and verify the results on the blockchain.
With the smart contract authored and the environment configured, the next step is to compile and deploy it.
Hardhat simplifies the compilation process. From the project's root directory, run the following command in the terminal 12:
Bash
npx hardhat compile
This command instructs Hardhat to:
In the scripts/ directory, delete the sample script and create a new file named deploy.ts. This script will programmatically deploy the MyNFT contract.
TypeScript
import { ethers } from "hardhat";
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploying contracts with the account:", deployer.address);
const MyNFT = await ethers.getContractFactory("MyNFT");
// Pass the deployer's address to the Ownable constructor
const myNFT = await MyNFT.deploy(deployer.address);
await myNFT.waitForDeployment();
const contractAddress = await myNFT.getAddress();
console.log("MyNFT contract deployed to:", contractAddress);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Script Analysis:
To deploy the contract to the BSC Testnet, execute the script using the Hardhat runner, specifying the target network with the --network flag 13:
Bash
npx hardhat run scripts/deploy.ts --network bsc_testnet
The terminal will output the deployer's address and, after a short wait, the address of the newly deployed MyNFT contract. This address should be saved for the next steps.
With the contract live on the testnet, an NFT can now be minted by calling the safeMint function.
Create a new file in the scripts/ directory named mint-nft.ts. This script will interact with the already-deployed contract.
TypeScript
import { ethers } from "hardhat";
// --- CONFIGURATION ---
const CONTRACT_ADDRESS = "YOUR_DEPLOYED_CONTRACT_ADDRESS"; // Replace with your contract's address
const RECIPIENT_ADDRESS = "RECIPIENT_WALLET_ADDRESS"; // Replace with the address to receive the NFT
const TOKEN_URI = "ipfs://YOUR_METADATA_JSON_IPFS_HASH"; // Replace with your metadata JSON's IPFS URI
// -------------------
async function main() {
const [signer] = await ethers.getSigners();
console.log("Minting NFT with the account:", signer.address);
// Get the contract instance
const myNFT = await ethers.getContractAt("MyNFT", CONTRACT_ADDRESS);
console.log(`Minting NFT for ${RECIPIENT_ADDRESS} with token URI ${TOKEN_URI}`);
// Call the safeMint function
const tx = await myNFT.safeMint(RECIPIENT_ADDRESS, TOKEN_URI);
console.log("Transaction sent. Waiting for confirmation…");
// Wait for the transaction to be mined
const receipt = await tx.wait();
console.log(`Transaction confirmed. Hash: ${receipt?.hash}`);
// Extract the tokenId from the Transfer event
const transferEvent = receipt?.logs.find(
(log: any) => log.eventName === 'Transfer'
);
if (transferEvent) {
const tokenId = transferEvent.args.tokenId;
console.log(`Successfully minted NFT with tokenId: ${tokenId.toString()}`);
} else {
console.error("Could not find Transfer event in transaction receipt.");
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Script Analysis:
Run the minting script using the same Hardhat command structure:
Bash
npx hardhat run scripts/mint-nft.ts --network bsc_testnet
The script will output the transaction hash and, upon success, the tokenId of the newly minted NFT.
The final step is to verify the contract's source code and inspect the results of the deployment and minting operations on a public block explorer.
Verifying the smart contract's source code on BscScan makes it publicly readable and auditable, which builds trust with users. The hardhat-verify plugin (included in the hardhat-toolbox) automates this process.
Run the following command, replacing DEPLOYED_CONTRACT_ADDRESS with the actual address from the deployment step and DEPLOYER_ADDRESS with the argument passed to the constructor:
Bash
npx hardhat verify --network bsc_testnet DEPLOYED_CONTRACT_ADDRESS "DEPLOYER_ADDRESS"
Hardhat will use the BSCSCAN_API_KEY from the .env file to communicate with BscScan and upload the source code for verification.29
Navigate to the BSC Testnet explorer (testnet.bscscan.com) and search for the deployed contract address. After successful verification, a new "Contract" tab with a green checkmark will appear.
Using BscScan, one can:
This final verification on a public block explorer provides definitive, immutable proof that the BEP-721 NFT has been successfully created, deployed, and minted on the Binance Smart Chain.
This report has detailed the comprehensive, end-to-end process for creating a BEP-721 NFT on the Binance Smart Chain using a professional, code-centric approach with Hardhat and TypeScript. By following the structured methodology presented—from understanding the foundational BEP-721 standard and architecting a robust development stack to authoring a secure smart contract, managing decentralized metadata, and executing programmatic deployment and minting—developers are equipped with the knowledge to build and manage unique digital assets on the BSC ecosystem. The emphasis on security best practices, such as leveraging OpenZeppelin contracts and securely managing credentials, alongside the use of industry-standard tools, ensures that the resulting NFTs are not only functional but also secure and maintainable. The skills and architectural patterns outlined are highly transferable across the broader EVM-compatible landscape, positioning this knowledge as a valuable asset for any developer working in the Web3 space.