# Alchemy第二周教程 **Published by:** [tuzi](https://paragraph.com/@binancezh-2/) **Published on:** 2022-08-10 **URL:** https://paragraph.com/@binancezh-2/alchemy ## Content 电脑需要安装 ubuntu 和 visual studio code 安装后管理员 ubuntu 运行mkdir BuyMeACoffee-contracts cd BuyMeACoffee-contracts npm init -y npx hardhat 运行成功后选择第一个一直回车创建新项目完成后界面输入Code . 会跳转打开code软件 (code内容修改后都需要手动点击保存)右键重命名为 BuyMeACoffee.sol 把内容修改为://SPDX-License-Identifier: Unlicense // contracts/BuyMeACoffee.sol pragma solidity ^0.8.0; // Switch this to your own contract address once deployed, for bookkeeping! // Example Contract Address on Goerli: 0xDBa03676a2fBb6711CB652beF5B7416A53c1421D 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; } // Address of contract deployer. Marked payable so that // we can withdraw to this address later. address payable owner; // List of all memos received from coffee purchases. Memo[] memos; constructor() { // Store the address of the deployer as a payable address. // When we withdraw funds, we'll withdraw here. owner = payable(msg.sender); } /** * @dev fetches all stored memos */ function getMemos() public view returns (Memo[] memory) { return memos; } /** * @dev buy a coffee for owner (sends an ETH tip and leaves a memo) * @param _name name of the coffee purchaser * @param _message a nice message from the purchaser */ function buyCoffee(string memory _name, string memory _message) public payable { // Must accept more than 0 ETH for a coffee. require(msg.value > 0, "can't buy coffee for free!"); // Add the memo to storage! memos.push(Memo( msg.sender, block.timestamp, _name, _message )); // Emit a NewMemo event with details about the memo. 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)); } } 右键重命名为buy-coffee.js内容修改为: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 purchases. async function printMemos(memos) { for (const memo of memos) { const timestamp = memo.timestamp; const tipper = memo.name; const tipperAddress = memo.from; const message = memo.message; console.log(`At ${timestamp}, ${tipper} (${tipperAddress}) said: "${message}"`); } } async function main() { // Get the example accounts we'll be working with. const [owner, tipper, tipper2, tipper3] = await hre.ethers.getSigners(); // We get the contract to deploy. const BuyMeACoffee = await hre.ethers.getContractFactory("BuyMeACoffee"); const buyMeACoffee = await BuyMeACoffee.deploy(); // Deploy the contract. await buyMeACoffee.deployed(); console.log("BuyMeACoffee deployed to:", buyMeACoffee.address); // Check balances before the coffee purchase. 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("Carolina", "You're the best!", tip); await buyMeACoffee.connect(tipper2).buyCoffee("Vitto", "Amazing teacher", tip); await buyMeACoffee.connect(tipper3).buyCoffee("Kay", "I love my Proof of Knowledge", tip); // Check balances after the coffee purchase. console.log("== bought coffee =="); await printBalances(addresses); // Withdraw. await buyMeACoffee.connect(owner).withdrawTips(); // Check balances after withdrawal. console.log("== withdrawTips =="); await printBalances(addresses); // Check out the memos. 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); }); 终端运行npx hardhat run scripts/buy-coffee.js 查看是否有报错如果没有报错,会出来一个合约如下图。在code内右键新建文件deploy.js内容为:// scripts/deploy.js const hre = require("hardhat"); async function main() { // We get the contract to 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); }); 运行npx hardhat run scripts/deploy.js 查看是否有报错,如果没报错会出现一个合约代码,如下图:点击hardhat.config.js 将内容修改为:// hardhat.config.js require("@nomiclabs/hardhat-ethers"); require("@nomiclabs/hardhat-waffle"); require("dotenv").config() // You need to export an object to set up your config // Go to https://hardhat.org/config/ to learn more 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] } } }; 运行npm install dotenv运行touch .env填充 env内容GOERLI_URL=https://eth-goerli.alchemyapi.io/v2/<your api key> GOERLI_API_KEY=<your api key> PRIVATE_KEY=<your metamask api key> https://www.alchemy.com/ 在这里注册,新建GOERLI_UL为 https链接 GOERLI_API_KEY 为 api-key PRIVATE_KEY为小狐狸私钥(创建新账号)如下图运行npx hardhat run scripts/deploy.js --network goerli 查看是否报错,如果没报错,会出现一个合约地址,如下图如果报错,如下图 选择的2行代码修改为一行: require("@nomicfoundation/hardhat-toolbox");修改后再次运行npx hardhat run scripts/deploy.js --network goerli 创建合约,如果继续报错,看一下报错原因是什么。 https://goerli.etherscan.io/ 运行成之后就可以去里面查询合约 并且复制合约链接备用(填表要用) 新建文件再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="0xDBa03676a2fBb6711CB652beF5B7416A53c1421D"; 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); }); 打开登录自己账号 https://replit.com/@thatguyintech/BuyMeACoffee-Solidity-DeFi-Tipping-app 点击代码修改为自己创建的合约地址ctrl + F 搜索Albert 可以修改自己的昵称 随便改点击发布 内容自己填复制填表用复制填表用在新窗口打开复制的这个链接运行后使用小狐狸交互一下 结束填表 https://alchemyapi.typeform.com/roadtoweektwo 最后一项要填3个添加, 第一个是合约链接(是链接,不是地址) 第二、三是结尾上面让复制的2个链接。 ## Publication Information - [tuzi](https://paragraph.com/@binancezh-2/): Publication homepage - [All Posts](https://paragraph.com/@binancezh-2/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@binancezh-2): Subscribe to updates - [Twitter](https://twitter.com/tututuzi11): Follow on Twitter