启动您自己的初始代币发行
现在是时候为 Crypto Devs 推出代币了。 我们将令牌称为 Crypto Dev Token。 我们将把这个代币免费分发给我们所有的 NFT 持有者,并让其他人用 ETH 购买它们。
喜欢视频?
如果您想从视频中学习,我们的 YouTube 上有本教程的录音。单击下面的屏幕截图观看视频,或继续阅读教程!
建造
要求
- 最多应该有 10,000 个 CD 令牌。
- 每个 Crypto Dev NFT 持有者都应该免费获得 10 个代币,但他们必须支付汽油费。
- ICO 时一张 CD 的价格应该是 0.001 以太币。
- 应该有一个用户可以访问 ICO 的网站。
让我们开始建造吧 🚀
先决条件
你必须已经完成了之前的 NFT Collection 教程。
理论
- 什么是 ERC20?
- ERC-20 是一个技术标准; 它用于以太坊区块链上的所有智能合约以实现代币,并提供所有基于以太坊的代币必须遵循的规则列表。
- 请在继续之前查看所有 ERC20 功能。
建造
智能合约
为了构建智能合约,我们将使用 Hardhat。 Hardhat 是一个以太坊开发环境和框架,专为 Solidity 中的全栈开发而设计。 简单来说,您可以编写智能合约、部署它们、运行测试和调试代码。
要设置安全帽项目,请打开终端并执行以下命令:
mkdir ICO
cd ICO
mkdir hardhat-tutorial
cd hardhat-tutorial
npm init --yes
npm install --save-dev hardhat
在安装 Hardhat 的同一目录中运行:
npx hardhat
确保选择创建 Javascript 项目
,然后按照终端中的步骤完成安全帽设置。
现在在同一个终端中安装 @openzeppelin/contracts
,因为我们将在我们的 CryptoDevs
合约中导入 Openzeppelin 的 ERC721Enumerable 合约。
npm install @openzeppelin/contracts
我们需要调用上一个教程中部署的 CryptoDevs 合约,以检查 CryptoDev NFT 的所有者。 由于我们只需要调用 tokenOfOwnerByIndex 和 balanceOf 方法,我们可以只用这两个函数为 CryptoDevs 合约创建一个接口。 这样我们节省了 gas,因为我们不需要继承和部署整个 CryptoDevs 合约,而只需要其中的一部分。
在 contracts 目录中创建一个新文件并将其命名为 ICryptoDevs.sol。 添加以下行:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ICryptoDevs {
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index)
external
view
returns (uint256 tokenId);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
}
在 contracts 目录中创建另一个文件并将其命名为 CryptoDevToken.sol。 添加以下行:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./ICryptoDevs.sol";
contract CryptoDevToken is ERC20, Ownable {
// Price of one Crypto Dev token
uint256 public constant tokenPrice = 0.001 ether;
// Each NFT would give the user 10 tokens
// It needs to be represented as 10 * (10 ** 18) as ERC20 tokens are represented by the smallest denomination possible for the token
// By default, ERC20 tokens have the smallest denomination of 10^(-18). This means, having a balance of (1)
// is actually equal to (10 ^ -18) tokens.
// Owning 1 full token is equivalent to owning (10^18) tokens when you account for the decimal places.
// More information on this can be found in the Freshman Track Cryptocurrency tutorial.
uint256 public constant tokensPerNFT = 10 * 10**18;
// the max total supply is 10000 for Crypto Dev Tokens
uint256 public constant maxTotalSupply = 10000 * 10**18;
// CryptoDevsNFT contract instance
ICryptoDevs CryptoDevsNFT;
// Mapping to keep track of which tokenIds have been claimed
mapping(uint256 => bool) public tokenIdsClaimed;
constructor(address _cryptoDevsContract) ERC20("Crypto Dev Token", "CD") {
CryptoDevsNFT = ICryptoDevs(_cryptoDevsContract);
}
/**
* @dev Mints `amount` number of CryptoDevTokens
* Requirements:
* - `msg.value` should be equal or greater than the tokenPrice * amount
*/
function mint(uint256 amount) public payable {
// the value of ether that should be equal or greater than tokenPrice * amount;
uint256 _requiredAmount = tokenPrice * amount;
require(msg.value >= _requiredAmount, "Ether sent is incorrect");
// total tokens + amount <= 10000, otherwise revert the transaction
uint256 amountWithDecimals = amount * 10**18;
require(
(totalSupply() + amountWithDecimals) <= maxTotalSupply,
"Exceeds the max total supply available."
);
// call the internal function from Openzeppelin's ERC20 contract
_mint(msg.sender, amountWithDecimals);
}
/**
* @dev Mints tokens based on the number of NFT's held by the sender
* Requirements:
* balance of Crypto Dev NFT's owned by the sender should be greater than 0
* Tokens should have not been claimed for all the NFTs owned by the sender
*/
function claim() public {
address sender = msg.sender;
// Get the number of CryptoDev NFT's held by a given sender address
uint256 balance = CryptoDevsNFT.balanceOf(sender);
// If the balance is zero, revert the transaction
require(balance > 0, "You dont own any Crypto Dev NFT's");
// amount keeps track of number of unclaimed tokenIds
uint256 amount = 0;
// loop over the balance and get the token ID owned by `sender` at a given `index` of its token list.
for (uint256 i = 0; i < balance; i++) {
uint256 tokenId = CryptoDevsNFT.tokenOfOwnerByIndex(sender, i);
// if the tokenId has not been claimed, increase the amount
if (!tokenIdsClaimed[tokenId]) {
amount += 1;
tokenIdsClaimed[tokenId] = true;
}
}
// If all the token Ids have been claimed, revert the transaction;
require(amount > 0, "You have already claimed all the tokens");
// call the internal function from Openzeppelin's ERC20 contract
// Mint (amount * 10) tokens for each NFT
_mint(msg.sender, amount * tokensPerNFT);
}
/**
* @dev withdraws all ETH and tokens sent to the contract
* Requirements:
* wallet connected must be owner's address
*/
function withdraw() public onlyOwner {
address _owner = owner();
uint256 amount = address(this).balance;
(bool sent, ) = _owner.call{value: amount}("");
require(sent, "Failed to send Ether");
}
// Function to receive Ether. msg.data must be empty
receive() external payable {}
// Fallback function is called when msg.data is not empty
fallback() external payable {}
}
现在让我们安装 dotenv 包,以便能够导入 env 文件并在我们的配置中使用它。打开指向 hardhat-tutorial 目录的终端并执行此命令
npm install dotenv
现在在 hardhat-tutorial 文件夹中创建一个 .env 文件并添加以下行。请按照以下说明进行操作。
转到 Quicknode 并注册一个帐户。如果您已经有一个帐户,请登录。Quicknode 是一个节点提供商,可让您连接到各种不同的区块链。我们将使用它通过 Hardhat 部署我们的合约。创建账户后,在 Quicknode 上创建一个 endpoint,选择 Ethereum,然后选择 Goerli 网络。单击右下角的继续,然后单击创建端点。复制 HTTP Provider 中提供给您的链接,并将其添加到 QUICKNODE_HTTP_URL 下面的 .env 文件中。
注意:如果您之前在新生跟踪期间在 Quicknode 上设置了 Goerli Endpoint,则可以使用与之前相同的 URL。无需删除它并设置一个新的。
要获取您的私钥,您需要从 Metamask 中导出它。打开 Metamask,点击三个点,点击 Account Details,然后点击 Export Private Key。确保您使用的是没有主网资金的测试帐户。在您的 .env 文件中为 PRIVATE_KEY 变量添加此私钥。
QUICKNODE_HTTP_URL="添加-quicknode-http-provider-url-这里"
PRIVATE_KEY="在此处添加私钥"
让我们将合约部署到 goerli 网络。在脚本文件夹下创建一个新文件,或替换默认文件,名为 deploy.js
让我们编写一些代码来在 deploy.js 文件中部署合约。
const { ethers } = require("hardhat");
require("dotenv").config({ path: ".env" });
const { CRYPTO_DEVS_NFT_CONTRACT_ADDRESS } = require("../constants");
async function main() {
// Address of the Crypto Devs NFT contract that you deployed in the previous module
const cryptoDevsNFTContract = CRYPTO_DEVS_NFT_CONTRACT_ADDRESS;
/*
A ContractFactory in ethers.js is an abstraction used to deploy new smart contracts,
so cryptoDevsTokenContract here is a factory for instances of our CryptoDevToken contract.
*/
const cryptoDevsTokenContract = await ethers.getContractFactory(
"CryptoDevToken"
);
// deploy the contract
const deployedCryptoDevsTokenContract = await cryptoDevsTokenContract.deploy(
cryptoDevsNFTContract
);
await deployedCryptoDevsTokenContract.deployed();
// print the address of the deployed contract
console.log(
"Crypto Devs Token Contract Address:",
deployedCryptoDevsTokenContract.address
);
}
// Call the main function and catch if there is any error
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});