# 手把手教程 ——Alchemy Week2 **Published by:** [RogerZ](https://paragraph.com/@rogerz-2/) **Published on:** 2022-08-15 **URL:** https://paragraph.com/@rogerz-2/alchemy-week2 ## Content Alchemy 教程目录Week1: How to Develop an NFT Smart Contract (ERC721) with Alchemy 教程Week2: How to Build “Buy Me a Coffee“ Defi dapp with Alchemy 教程官方教程在此,也可以看 youtube 视频链接,不想看我啰嗦的可以移步。 step0 环境配置系统环境: Window10 X64编程环境: nodeJS v16.15.0 NPM v8.5.5 nodeJS 安装回头写一篇教程吧,最简单的方法可以 google nvm,比去官网下载 nodeJS 的安装包好用多了。另外 windows 系统下需要配置环境变量,不明白的可以先百度。编程IDE: VS Code VS code 下载官网,exe 的安装程序,按照提示即可step1 部署 web3 环境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 出现这样的画面,代表全部安装完成了。图中的 WARN 不用在意,JS 特色之一4. 安装 dotenv 库可以看下文件夹内的各文件,如果和以下树形图一致,那就表示环境部署没有问题了。. ├── README.md ├── contracts ├── hardhat.config.js ├── node_modules ├── package-lock.json ├── package.json ├── scripts └── test 接下来的step3, step4两步,使用 VScode 打开文件夹进行编辑。step3 编写智能合约在 ./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; } } step4 部署智能合约在 ./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。跑通没有报错的话,等一会就可以在正式的网络中成功部署了。step5 申请 Alchemy 帐号并配置小狐狸钱包1. 注册 alchemy 帐号详细步骤可以回看 week1 教程的 step3。 https://www.alchemy.com/ 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/goerlistep6 在 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 测试网,还可以在浏览器中查看。step7 网站搭建1. 注册 repl.it 网站的帐号 https://replit.com/ 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 中查看。step8 部署提现合约在 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 的交易,且余额变成了0step9 在官网提交任务并 claim NFT在官方申请网址中提交对应的信息。 https://alchemyapi.typeform.com/roadtoweektwo 在 mintkudos 网址中 claim Alchemy 的 NFT,通常需要一天至一周可以获得 allowlist 资格。 https://mintkudos.xyz/claim/611 ## Publication Information - [RogerZ](https://paragraph.com/@rogerz-2/): Publication homepage - [All Posts](https://paragraph.com/@rogerz-2/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@rogerz-2): Subscribe to updates