
Week1: How to Develop an NFT Smart Contract (ERC721) with Alchemy 教程
官方教程在此,也可以看 youtube 视频链接,不想看我啰嗦的可以移步。
系统环境:
Window10 X64
编程环境:
nodeJS v16.15.0
NPM v8.5.5
nodeJS 安装回头写一篇教程吧,最简单的方法可以 google nvm,比去官网下载 nodeJS 的安装包好用多了。另外 windows 系统下需要配置环境变量,不明白的可以先百度。
编程IDE:
VS Code
VS code 下载官网,exe 的安装程序,按照提示即可
1. 新建文件夹 BuyMeACoffee-contracts,在文件夹内启动 cmd,npm init -y初始化项目,获得文件 package.json。
2. 安装 hardhat,在 cmd 中输入

根据提示依次
选择Create a basic sample project
选择默认路径
3. 复制下列代码,安装需要的库
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

4. 安装 dotenv 库
可以看下文件夹内的各文件,如果和以下树形图一致,那就表示环境部署没有问题了。
.
├── README.md
├── contracts
├── hardhat.config.js
├── node_modules
├── package-lock.json
├── package.json
├── scripts
└── test
接下来的step3, step4两步,使用 VScode 打开文件夹进行编辑。
在 ./contracts/ 文件夹下新建 BuyMeACoffee.sol,复制以下代码,然后保存。
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
contract BuyMeACoffee {
// Event to emit when a Memo is created
event NewMemo(
address indexed from,
uint256 timestamp,
string name,
string message
);
// Memo struct
struct Memo {
address from;
uint256 timestamp;
string name;
string message;
}
// List of all memos received from friends.
Memo[] memos;
// Address of contract deployer
address payable owner;
// Deploy logic
constructor() {
owner = payable(msg.sender);
}
/**
* @dev buy a coffee for contract owner
* @param _name name of the coffee buyer
* @param _message a message from the coffee buyer
*/
function buyCoffee(string memory _name, string memory _message) public payable {
require(msg.value > 0, "can't buy coffee with 0 eth.");
// add the memo to storage
memos.push(Memo(
msg.sender,
block.timestamp,
_name,
_message
));
// emit a log event when a new memo is created
emit NewMemo(
msg.sender,
block.timestamp,
_name,
_message
);
}
/**
* @dev send the entire balance stored in this contract to the owner
*/
function withdrawTips() public {
require(owner.send(address(this).balance));
}
/**
* @dev retrieve all the memos received and stored on the blockchain
*/
function getMemos() public view returns(Memo[] memory) {
return memos;
}
}
在 ./scripts/ 文件夹下新建 buy-coffee.js,复制以下代码,然后保存。
// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// When running the script with `npx hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.
const hre = require("hardhat");
// Returns the Ether balance of a given address
async function getBalance(address) {
const balanceBigInt = await hre.waffle.provider.getBalance(address);
return hre.ethers.utils.formatEther(balanceBigInt);
}
// Logs the Ether balances for a list of addresses
async function printBalances(addresses) {
let idx = 0;
for (const address of addresses) {
console.log(`Address ${idx} balance: `, await getBalance(address));
idx++;
}
}
// Logs the memos stored on-chain from coffee puchases.
async function printMemos(memos) {
for (const memo of memos) {
const timestamp = memo.timestamp;
const tipper = memo.name;
const tipperAddress = memo.address;
const message = memo.message;
console.log(`At ${timestamp}, ${tipper} (${tipperAddress}) said: "${message}"`);
}
}
async function main() {
// Get example accounts
const [owner, tipper, tipper2, tipper3] = await hre.ethers.getSigners();
// Get the contract to deploy & deploy.
const BuyMeACoffee = await hre.ethers.getContractFactory("BuyMeACoffee");
const buyMeACoffee = await BuyMeACoffee.deploy();
await buyMeACoffee.deployed();
console.log("BuyMeACoffee deployed to ", buyMeACoffee.address);
// Check balances before the coffee purchases.
const addresses = [owner.address, tipper.address, buyMeACoffee.address];
console.log("== start ==");
await printBalances(addresses);
// Buy the owner a few coffees.
const tip = {value: hre.ethers.utils.parseEther("1")};
await buyMeACoffee.connect(tipper).buyCoffee("Angela", "Hello from Angela!", tip);
await buyMeACoffee.connect(tipper2).buyCoffee("Bernard", "Greeting from Bernard!", tip);
await buyMeACoffee.connect(tipper3).buyCoffee("Charlie", "Hi from Charlie!", tip);
// Check balances after the coffee purchases.
console.log("== bought coffee ==");
await printBalances(addresses);
// Withdraw funds
await buyMeACoffee.connect(owner).withdrawTips();
// Check balances after withdraw.
console.log("== withdrawTips ==");
await printBalances(addresses);
// Read all the memos left for the owner.
console.log("== memos ==");
const memos = await buyMeACoffee.getMemos();
printMemos(memos);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
在 cmd 中运行 npx hardhat run scripts/buy-coffee.js,返回
BuyMeACoffee deployed to 0x5FbDB2315678afecb367f032d93F642f64180aa3
== start ==
Address 0 balance: 9999.998770013125
Address 1 balance: 10000.0
Address 2 balance: 0.0
== bought coffee ==
Address 0 balance: 9999.998770013125
Address 1 balance: 9998.999752902345533407
Address 2 balance: 3.0
== withdrawTips ==
Address 0 balance: 10002.998724110143833925
Address 1 balance: 9998.999752902345533407
Address 2 balance: 0.0
== memos ==
At 1660237686, Angela (undefined) said: "Hello from Angela!"
At 1660237687, Bernard (undefined) said: "Greeting from Bernard!"
At 1660237688, Charlie (undefined) said: "Hi from Charlie!"
这个是运行在本地网络的,简单理解就是没有上链,不消耗任何 gas。跑通没有报错的话,等一会就可以在正式的网络中成功部署了。
1. 注册 alchemy 帐号详细步骤可以回看 week1 教程的 step3。
2. 进入 Dashboard,点击 create app。

3. 依次填入 Name, Description,Chain 选择 Ethereum,Network 选择 Goerli,然后点击 CREATE APP。

4. 点击 VIEW KEY,复制 HTTPS 和 API KEY 的内容

5. 查看小狐狸钱包中是否有 Goerli 测试网,如果没有可以去 chainlink.org 上添加一个,并保存私钥,第六步要用到。
6. 因为交互需要消耗 gas,所以还需通过 chainlink faucet 获取测试代币 ETH。通过 faucet 的链接,进入申请代币即可。
https://faucets.chain.link/goerli
1. 在 ./scripts/ 文件夹下新建 deploy.js,复制以下代码,然后保存。
const hre = require("hardhat");
async function main() {
// Get the contract to deploy & deploy.
const BuyMeACoffee = await hre.ethers.getContractFactory("BuyMeACoffee");
const buyMeACoffee = await BuyMeACoffee.deploy();
await buyMeACoffee.deployed();
console.log("BuyMeACoffee deployed to ", buyMeACoffee.address);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
2. 编辑 ./hardhat.config.js,复制以下代码,然后保存。
require("@nomiclabs/hardhat-waffle");
require("@nomiclabs/hardhat-ethers");
require("dotenv").config();
const GOERLI_URL = process.env.GOERLI_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.4",
networks: {
goerli: {
url: GOERLI_URL,
accounts: [PRIVATE_KEY]
}
}
};
3. 新建 ./.env 文件,复制以下代码,然后保存。
GOERLI_URL=输入goerli的rpc url
GOERLI_API_KEY=输入goerli的api key
PRIVATE_KEY=输入钱包私钥
4. 在 cmd 中运行 npx hardhat run scripts/deploy.js --network goerli,返回
BuyMeACoffee deployed to 合约地址
至此,合约已经部署到了 goerli 测试网,还可以在浏览器中查看。

1. 注册 repl.it 网站的帐号
2. 教程做好了简单的前端,直接 fork 即可,教程的 repo 链接,点击 Fork Repl 即可

3. 更换 ./pages/index.jsx 文件第 10 行的 contractAddress,改成刚刚在 goerli 网络部署的合约地址。

4. 替换 ./pages/index.jsx 文件中的 Albert,可以改为自己的名字,一共有3处。

5. 从 VS Code 中复制 ./artifacts/contracts/BuyMeACoffee.sol/BuyMeACoffee.json 的所有内容到 ./utils/BuyMeACoffee.json
6. 点击右上角 publish,按提示填写

7. 点击右上角 Open in a new tab,会在浏览器新的标签页中打开

8. 点击 connect your wallet 按钮后,链接小狐狸后,即出现了前端页面

9. 体验交互一次,用另一个钱包,点击 Send 1 Coffee for 0.001ETH 按钮,小狐狸中点击确认

10. 网页自动更新了 memo。

同时可以在 etherscan 中查看。

在 VS Code 中,新建 ./scripts/withdraw.js 文件,复制以下代码并保存。
const hre = require("hardhat");
const abi = require("../artifacts/contracts/BuyMeACoffee.sol/BuyMeACoffee.json");
async function getBalance(provider, address) {
const balanceBigInt = await provider.getBalance(address);
return hre.ethers.utils.formatEther(balanceBigInt);
}
async function main() {
// Get the contract that has been deployed to Goerli.
const contractAddress="部署的合约地址";
const contractABI = abi.abi;
// Get the node connection and wallet connection.
const provider = new hre.ethers.providers.AlchemyProvider("goerli", process.env.GOERLI_API_KEY);
// Ensure that signer is the SAME address as the original contract deployer,
// or else this script will fail with an error.
const signer = new hre.ethers.Wallet(process.env.PRIVATE_KEY, provider);
// Instantiate connected contract.
const buyMeACoffee = new hre.ethers.Contract(contractAddress, contractABI, signer);
// Check starting balances.
console.log("current balance of owner: ", await getBalance(provider, signer.address), "ETH");
const contractBalance = await getBalance(provider, buyMeACoffee.address);
console.log("current balance of contract: ", await getBalance(provider, buyMeACoffee.address), "ETH");
// Withdraw funds if there are funds to withdraw.
if (contractBalance !== "0.0") {
console.log("withdrawing funds..")
const withdrawTxn = await buyMeACoffee.withdrawTips();
await withdrawTxn.wait();
} else {
console.log("no funds to withdraw!");
}
// Check ending balance.
console.log("current balance of owner: ", await getBalance(provider, signer.address), "ETH");
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
见下图,现在合约中有 0.001ETH 余额

运行 npx hardhat run scripts\withdraw.js --network goerli,返回
current balance of owner: 0.999016010494752056 ETH
current balance of contract: 0.001 ETH
withdrawing funds..
current balance of owner: 0.999970651994298471 ETH
再到 etherscan 浏览器中查看,多了一笔 Withdraw Tips 的交易,且余额变成了0

在官方申请网址中提交对应的信息。
https://alchemyapi.typeform.com/roadtoweektwo
在 mintkudos 网址中 claim Alchemy 的 NFT,通常需要一天至一周可以获得 allowlist 资格。
https://mintkudos.xyz/claim/611


Week1: How to Develop an NFT Smart Contract (ERC721) with Alchemy 教程
官方教程在此,也可以看 youtube 视频链接,不想看我啰嗦的可以移步。
系统环境:
Window10 X64
编程环境:
nodeJS v16.15.0
NPM v8.5.5
nodeJS 安装回头写一篇教程吧,最简单的方法可以 google nvm,比去官网下载 nodeJS 的安装包好用多了。另外 windows 系统下需要配置环境变量,不明白的可以先百度。
编程IDE:
VS Code
VS code 下载官网,exe 的安装程序,按照提示即可
1. 新建文件夹 BuyMeACoffee-contracts,在文件夹内启动 cmd,npm init -y初始化项目,获得文件 package.json。
2. 安装 hardhat,在 cmd 中输入

根据提示依次
选择Create a basic sample project
选择默认路径
3. 复制下列代码,安装需要的库
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

4. 安装 dotenv 库
可以看下文件夹内的各文件,如果和以下树形图一致,那就表示环境部署没有问题了。
.
├── README.md
├── contracts
├── hardhat.config.js
├── node_modules
├── package-lock.json
├── package.json
├── scripts
└── test
接下来的step3, step4两步,使用 VScode 打开文件夹进行编辑。
在 ./contracts/ 文件夹下新建 BuyMeACoffee.sol,复制以下代码,然后保存。
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
contract BuyMeACoffee {
// Event to emit when a Memo is created
event NewMemo(
address indexed from,
uint256 timestamp,
string name,
string message
);
// Memo struct
struct Memo {
address from;
uint256 timestamp;
string name;
string message;
}
// List of all memos received from friends.
Memo[] memos;
// Address of contract deployer
address payable owner;
// Deploy logic
constructor() {
owner = payable(msg.sender);
}
/**
* @dev buy a coffee for contract owner
* @param _name name of the coffee buyer
* @param _message a message from the coffee buyer
*/
function buyCoffee(string memory _name, string memory _message) public payable {
require(msg.value > 0, "can't buy coffee with 0 eth.");
// add the memo to storage
memos.push(Memo(
msg.sender,
block.timestamp,
_name,
_message
));
// emit a log event when a new memo is created
emit NewMemo(
msg.sender,
block.timestamp,
_name,
_message
);
}
/**
* @dev send the entire balance stored in this contract to the owner
*/
function withdrawTips() public {
require(owner.send(address(this).balance));
}
/**
* @dev retrieve all the memos received and stored on the blockchain
*/
function getMemos() public view returns(Memo[] memory) {
return memos;
}
}
在 ./scripts/ 文件夹下新建 buy-coffee.js,复制以下代码,然后保存。
// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// When running the script with `npx hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.
const hre = require("hardhat");
// Returns the Ether balance of a given address
async function getBalance(address) {
const balanceBigInt = await hre.waffle.provider.getBalance(address);
return hre.ethers.utils.formatEther(balanceBigInt);
}
// Logs the Ether balances for a list of addresses
async function printBalances(addresses) {
let idx = 0;
for (const address of addresses) {
console.log(`Address ${idx} balance: `, await getBalance(address));
idx++;
}
}
// Logs the memos stored on-chain from coffee puchases.
async function printMemos(memos) {
for (const memo of memos) {
const timestamp = memo.timestamp;
const tipper = memo.name;
const tipperAddress = memo.address;
const message = memo.message;
console.log(`At ${timestamp}, ${tipper} (${tipperAddress}) said: "${message}"`);
}
}
async function main() {
// Get example accounts
const [owner, tipper, tipper2, tipper3] = await hre.ethers.getSigners();
// Get the contract to deploy & deploy.
const BuyMeACoffee = await hre.ethers.getContractFactory("BuyMeACoffee");
const buyMeACoffee = await BuyMeACoffee.deploy();
await buyMeACoffee.deployed();
console.log("BuyMeACoffee deployed to ", buyMeACoffee.address);
// Check balances before the coffee purchases.
const addresses = [owner.address, tipper.address, buyMeACoffee.address];
console.log("== start ==");
await printBalances(addresses);
// Buy the owner a few coffees.
const tip = {value: hre.ethers.utils.parseEther("1")};
await buyMeACoffee.connect(tipper).buyCoffee("Angela", "Hello from Angela!", tip);
await buyMeACoffee.connect(tipper2).buyCoffee("Bernard", "Greeting from Bernard!", tip);
await buyMeACoffee.connect(tipper3).buyCoffee("Charlie", "Hi from Charlie!", tip);
// Check balances after the coffee purchases.
console.log("== bought coffee ==");
await printBalances(addresses);
// Withdraw funds
await buyMeACoffee.connect(owner).withdrawTips();
// Check balances after withdraw.
console.log("== withdrawTips ==");
await printBalances(addresses);
// Read all the memos left for the owner.
console.log("== memos ==");
const memos = await buyMeACoffee.getMemos();
printMemos(memos);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
在 cmd 中运行 npx hardhat run scripts/buy-coffee.js,返回
BuyMeACoffee deployed to 0x5FbDB2315678afecb367f032d93F642f64180aa3
== start ==
Address 0 balance: 9999.998770013125
Address 1 balance: 10000.0
Address 2 balance: 0.0
== bought coffee ==
Address 0 balance: 9999.998770013125
Address 1 balance: 9998.999752902345533407
Address 2 balance: 3.0
== withdrawTips ==
Address 0 balance: 10002.998724110143833925
Address 1 balance: 9998.999752902345533407
Address 2 balance: 0.0
== memos ==
At 1660237686, Angela (undefined) said: "Hello from Angela!"
At 1660237687, Bernard (undefined) said: "Greeting from Bernard!"
At 1660237688, Charlie (undefined) said: "Hi from Charlie!"
这个是运行在本地网络的,简单理解就是没有上链,不消耗任何 gas。跑通没有报错的话,等一会就可以在正式的网络中成功部署了。
1. 注册 alchemy 帐号详细步骤可以回看 week1 教程的 step3。
2. 进入 Dashboard,点击 create app。

3. 依次填入 Name, Description,Chain 选择 Ethereum,Network 选择 Goerli,然后点击 CREATE APP。

4. 点击 VIEW KEY,复制 HTTPS 和 API KEY 的内容

5. 查看小狐狸钱包中是否有 Goerli 测试网,如果没有可以去 chainlink.org 上添加一个,并保存私钥,第六步要用到。
6. 因为交互需要消耗 gas,所以还需通过 chainlink faucet 获取测试代币 ETH。通过 faucet 的链接,进入申请代币即可。
https://faucets.chain.link/goerli
1. 在 ./scripts/ 文件夹下新建 deploy.js,复制以下代码,然后保存。
const hre = require("hardhat");
async function main() {
// Get the contract to deploy & deploy.
const BuyMeACoffee = await hre.ethers.getContractFactory("BuyMeACoffee");
const buyMeACoffee = await BuyMeACoffee.deploy();
await buyMeACoffee.deployed();
console.log("BuyMeACoffee deployed to ", buyMeACoffee.address);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
2. 编辑 ./hardhat.config.js,复制以下代码,然后保存。
require("@nomiclabs/hardhat-waffle");
require("@nomiclabs/hardhat-ethers");
require("dotenv").config();
const GOERLI_URL = process.env.GOERLI_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.4",
networks: {
goerli: {
url: GOERLI_URL,
accounts: [PRIVATE_KEY]
}
}
};
3. 新建 ./.env 文件,复制以下代码,然后保存。
GOERLI_URL=输入goerli的rpc url
GOERLI_API_KEY=输入goerli的api key
PRIVATE_KEY=输入钱包私钥
4. 在 cmd 中运行 npx hardhat run scripts/deploy.js --network goerli,返回
BuyMeACoffee deployed to 合约地址
至此,合约已经部署到了 goerli 测试网,还可以在浏览器中查看。

1. 注册 repl.it 网站的帐号
2. 教程做好了简单的前端,直接 fork 即可,教程的 repo 链接,点击 Fork Repl 即可

3. 更换 ./pages/index.jsx 文件第 10 行的 contractAddress,改成刚刚在 goerli 网络部署的合约地址。

4. 替换 ./pages/index.jsx 文件中的 Albert,可以改为自己的名字,一共有3处。

5. 从 VS Code 中复制 ./artifacts/contracts/BuyMeACoffee.sol/BuyMeACoffee.json 的所有内容到 ./utils/BuyMeACoffee.json
6. 点击右上角 publish,按提示填写

7. 点击右上角 Open in a new tab,会在浏览器新的标签页中打开

8. 点击 connect your wallet 按钮后,链接小狐狸后,即出现了前端页面

9. 体验交互一次,用另一个钱包,点击 Send 1 Coffee for 0.001ETH 按钮,小狐狸中点击确认

10. 网页自动更新了 memo。

同时可以在 etherscan 中查看。

在 VS Code 中,新建 ./scripts/withdraw.js 文件,复制以下代码并保存。
const hre = require("hardhat");
const abi = require("../artifacts/contracts/BuyMeACoffee.sol/BuyMeACoffee.json");
async function getBalance(provider, address) {
const balanceBigInt = await provider.getBalance(address);
return hre.ethers.utils.formatEther(balanceBigInt);
}
async function main() {
// Get the contract that has been deployed to Goerli.
const contractAddress="部署的合约地址";
const contractABI = abi.abi;
// Get the node connection and wallet connection.
const provider = new hre.ethers.providers.AlchemyProvider("goerli", process.env.GOERLI_API_KEY);
// Ensure that signer is the SAME address as the original contract deployer,
// or else this script will fail with an error.
const signer = new hre.ethers.Wallet(process.env.PRIVATE_KEY, provider);
// Instantiate connected contract.
const buyMeACoffee = new hre.ethers.Contract(contractAddress, contractABI, signer);
// Check starting balances.
console.log("current balance of owner: ", await getBalance(provider, signer.address), "ETH");
const contractBalance = await getBalance(provider, buyMeACoffee.address);
console.log("current balance of contract: ", await getBalance(provider, buyMeACoffee.address), "ETH");
// Withdraw funds if there are funds to withdraw.
if (contractBalance !== "0.0") {
console.log("withdrawing funds..")
const withdrawTxn = await buyMeACoffee.withdrawTips();
await withdrawTxn.wait();
} else {
console.log("no funds to withdraw!");
}
// Check ending balance.
console.log("current balance of owner: ", await getBalance(provider, signer.address), "ETH");
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
见下图,现在合约中有 0.001ETH 余额

运行 npx hardhat run scripts\withdraw.js --network goerli,返回
current balance of owner: 0.999016010494752056 ETH
current balance of contract: 0.001 ETH
withdrawing funds..
current balance of owner: 0.999970651994298471 ETH
再到 etherscan 浏览器中查看,多了一笔 Withdraw Tips 的交易,且余额变成了0

在官方申请网址中提交对应的信息。
https://alchemyapi.typeform.com/roadtoweektwo
在 mintkudos 网址中 claim Alchemy 的 NFT,通常需要一天至一周可以获得 allowlist 资格。
https://mintkudos.xyz/claim/611

Share Dialog
Share Dialog

Subscribe to RogerZ

Subscribe to RogerZ
<100 subscribers
<100 subscribers
No activity yet