关于后端:如何构建Buy-Me-a-CoffeeDeFi-dApp

33次阅读

共计 16738 个字符,预计需要花费 42 分钟才能阅读完成。

🥸 本教程来自官网:https://docs.alchemy.com/docs。对原文局部内容进行了批改。教程中所有实例通过自己实际,代码可见:https://github.com/ChuXiaoYi/web3Study

区块链技术令人惊叹,因为它使咱们可能应用代码和软件编程货币。只需几行代码,就能够构建各种应用程序和协定,为世界各地的人们发明新的机会。

“Buy Me A Coffee”是一个风行的网站,创作者、教育家、娱乐者和各种人们用它来创立一个着陆页,任何人都能够发送一些钱作为他们的服务的感激。然而,为了应用它,你必须有银行账户和信用卡。并非每个人都有!

建设在区块链之上的分散化应用程序的一个益处是,世界各地的任何人都能够应用一个以太坊钱包来拜访该应用程序,任何人都能够在不到 1 分钟的工夫内收费设置一个以太坊钱包。让咱们看看如何利用它来取得劣势!

在本教程中,您将学习如何开发和部署一个分散化的“Buy Me A Coffee”智能合约,容许访问者发送给您(虚伪)以太作为小费并留下好的音讯,应用 Alchemy、Hardhat、Ethers.js 和以太坊 Goerli。

通过本教程,您将学习以下内容:

  • 应用 Hardhat 开发环境构建、测试和部署智能合约。
  • 连贯您的 MetaMask 钱包到 Goerli 测试网络,应用 Alchemy RPC 端点。
  • 从 goerlifaucet.com 获取收费的 Goerli ETH。
  • 应用 Ethers.js 与部署的智能合约进行交互。
  • 应用 Replit 为您的分散化应用程序构建前端网站。

视频教程版本在此处: https://youtu.be/cxxKdJk55Lk

先决条件

为了筹备本教程的其余部分,您须要领有:

  • npm (npx) 版本 8.5.5
  • node 版本 16.13.1
  • 一个 Alchemy 帐户(在此收费注册!)

以下不是必须的,但十分有用:

  • 一些相熟命令行
  • 一些相熟 JavaScript

当初让咱们开始构建咱们的智能合约!

编写 BuyMeACoffee.sol 智能合约

Github 参考链接:https://github.com/alchemyplatform/RTW3-Week2-BuyMeACoffee-Contracts

如果你之前应用过相似于 OpenZeppelin WizardRemix 的工具,那么你曾经筹备好应用 Hardhat 了。

Hardhat 相似于开发环境和编码工具,但它更加可定制,并且从你本人计算机的命令行界面运行,而不是浏览器应用程序。

咱们将应用 Hardhat 来:

  • 生成我的项目模板
  • 测试咱们的智能合约代码
  • 部署到 Goerli 测试网络

让咱们开始吧!

关上你的终端并创立一个新目录。

mkdir BuyMeACoffee-contracts
cd BuyMeACoffee-contracts

在此目录中,咱们想要启动一个新的 npm 我的项目(默认设置即可):

npm init -y

这将为您创立一个名为 package.json 的文件,其内容应如下所示:

(base) ➜  BuyMeACoffee-contracts git:(main) ✗ npm init -y

Wrote to /Users/chuxiaoyi/work/web3/web3Study/BuyMeACoffee-contracts/package.json:

{
  "name": "buymeacoffee-contracts",
  "version": "1.0.0",
  "description": "","main":"index.js","scripts": {"test":"echo \"Error: no test specified\" && exit 1"},"keywords": [],"author":"",
  "license": "ISC"
}

装置 Hardhat:

npm install --save-dev hardhat

当初咱们创立一个示例我的项目:

npx hardhat

您应该会看到一个欢送音讯和能够执行的操作选项。抉择Create a JavaScript project:

888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88"888 888"88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888"Y88888 888  888 "Y888888"Y888

👷 Welcome to Hardhat v2.14.0 👷‍

批准所有默认设置(我的项目根目录、增加 .gitignore 文件、装置所有示例我的项目依赖项):

✔ What do you want to do? · Create a JavaScript project
✔ Hardhat project root: · /Users/chuxiaoyi/work/web3/web3Study/BuyMeACoffee-contracts
✔ Do you want to add a .gitignore? (Y/n) · y
✔ Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)? (Y/n) · y

Hardhat 会为咱们生成一个 hardhat.config.js 文件以及一些蕴含示例代码的文件夹,包含 contractsscriptstest

要查看所有是否失常工作,请运行:

npx hardhat test

咱们当初曾经胜利配置了咱们的 hardhat 开发环境。

你的我的项目目录当初应该看起来像这样(我在应用 tree 进行可视化):

tree -C -L 1
.
├── README.md
├── contracts
├── hardhat.config.js
├── node_modules
├── package-lock.json
├── package.json
├── scripts
└── test

重要的文件夹和文件包含:

  • contracts – 寄存智能合约的文件夹

    • 在这个我的项目中,咱们只会创立一个智能合约,来组织咱们的 BuyMeACoffee 逻辑
  • scripts – 寄存 Hardhat JavaScript 脚本的文件夹

    • 咱们将编写 deploy 逻辑
    • 例如 buy-coffee 脚本
    • 还有一个 withdraw 脚本来兑现咱们的小费
  • hardhat.config.js – 配置文件,蕴含 Solidity 版本和部署设置

当初应用任何代码编辑器关上我的项目文件夹!我喜爱应用 VSCode。

您会留神到通过 Hardhat 示例我的项目工具曾经主动生成了许多文件。咱们将会替换所有这些文件,从 Lock.sol 合约开始。

  1. 将合约文件重命名为BuyMeACoffee.sol
  2. 用以下代码替换合约代码:
//SPDX-License-Identifier: Unlicense

// contracts/BuyMeACoffee.sol
pragma solidity ^0.8.9;

// 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));
    }
}

请花些工夫浏览合同正文,看看能不能理解目前的状况!

以下是重点:

  • 当咱们部署合同时,constructor将负责部署的钱包地址保留在 owner 变量中作为可付款地址。这对于当前咱们想要提取合同收集的任何小费十分有用。
  • buyCoffee函数是合同上最重要的函数。它承受两个字符串 _name_message,同时也承受以太币,因为 payable 修饰符。它应用 _name_message输出创立一个在区块链上存储的 Memo 构造体。

    • 当访客调用 buyCoffee 函数时,他们必须提交一些以太币,因为 require(msg.value > 0) 语句。以太币随后将被保留在合同 balance 中,直到提取。
  • memos数组存储所有从咖啡购买中生成的 Memo 构造体。
  • NewMemo日志事件在每次购买咖啡时收回。这使咱们可能从咱们的前端网站上监听新的咖啡购买。
  • withdrawTips是任何人都能够调用的函数,但只会发送资金到合同的最后部署者。

    • address(this).balance获取存储在合同上的以太币
    • owner.send(...)是创立带有以太币的发送交易的语法
    • 包裹所有内容的 require(...) 语句是为了确保如果呈现任何问题,交易将被回滚,不会失落任何货色
    • 这就是咱们失去 require(owner.send(address(this).balance)) 的形式

领有这个智能合同代码,咱们当初能够编写一个脚本来测试咱们的逻辑!

创立一个 buy-coffee.js 脚本来测试你的合约

scripts 文件夹下,应该曾经有一个曾经填好的示例脚本 deploy.js。让咱们将该文件重命名为buy-coffee.js 并粘贴以下代码:

const hre = require("hardhat");

// Returns the Ether balance of a given address.
async function getBalance(address) {const balanceBigInt = await hre.ethers.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("0.01") };
  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);
  });

此时,您的我的项目目录应该长这样:

请随便花几分钟浏览脚本代码。在顶部定义了一些实用程序函数,以不便执行诸如获取钱包余额并将其打印进去等操作。

脚本的次要逻辑在 main() 函数中。正文的代码显示了脚本的流程:

  1. 获取咱们将应用的示例帐户。
  2. 获取要部署的合约。
  3. 部署合约。
  4. 在购买咖啡之前查看余额。
  5. 给所有者买几杯咖啡。
  6. 在购买咖啡之后查看余额。
  7. 提款。
  8. 撤回后查看余额。
  9. 查看备忘录。

这个脚本测试了咱们在智能合约中实现的所有性能!太棒了。

您还能够留神到咱们正在进行乏味的调用,例如:

  • hre.waffle.provider.getBalance
  • hre.ethers.getContractFactory
  • hre.ethers.utils.parseEther
  • 等等。

这些代码行是咱们利用 Hardhat (hre) 开发环境以及 Ethers 和 Waffle SDK 插件的性能来拜访读取区块链钱包账户余额、部署合约和格式化 Ether 加密货币值等性能。

在本教程中,咱们不会过于深刻地探讨这些代码,但您能够通过查阅 Hardhat 和 Ethers.js 文档来理解更多信息。

说了这么多,当初是时候玩乐了,让咱们运行脚本:

npx hardhat run scripts/buy-coffee.js

你应该在终端看到相似于这样的输入:

(base) ➜  BuyMeACoffee-contracts git:(main) ✗ npx hardhat run scripts/buy-coffee.js
Compiled 1 Solidity file successfully
BuyMeACoffee deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
== start ==
Address 0 balance:  9999.99857170375
Address 1 balance:  10000.0
Address 2 balance:  0.0
== bought coffee ==
Address 0 balance:  9999.99857170375
Address 1 balance:  9999.989752217303447058
Address 2 balance:  0.03
== withdrawTips ==
Address 0 balance:  10000.028525789500231956
Address 1 balance:  9999.989752217303447058
Address 2 balance:  0.0
== memos ==
At 1684043147, Carolina (0x70997970C51812dc3A010C7d01b50e0d17dc79C8) said: "You're the best!"At 1684043148, Vitto (0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC) said:"Amazing teacher"At 1684043149, Kay (0x90F79bf6EB2c4f870365E785982E1f101E93b906) said:"I love my Proof of Knowledge"

在脚本开始之初(即在部署合约后),请留神“0”地址有 9999.99877086625 个 ETH。这是因为它作为一种预填充的 hardhat 地址之一,开始时有 10k 个 ETH,但它必须破费大量 ETH 来部署到本地区块链。

在第二步“==bought coffee==”中,地址 1 购买了一杯咖啡。其余两个未显示的钱包也购买了咖啡。总共购买了 3 杯咖啡,小费总额为 0.03 个 ETH。您能够看到地址 2(代表合约地址)持有 0.03 个 ETH。

在“==withdrawTips==”中调用“withdrawTips()”函数后,合约回到 0 个 ETH,并且原始的部署者,即地址 0,当初曾经赚了一些钱,持有 10000.028525789500231956ETH。

咱们开心吗?!?!你能设想一下你行将赚到的小费吗?我能够。

当初让咱们实现一个孤立的部署脚本,以放弃真正的部署简略,同时筹备好部署到 Goerli 测试网络!

应用 Alchemy 和 MetaMask 将您的 BuyMeACoffe.sol 智能合约部署到以太坊 Goerli 测试网

让咱们创立一个新文件scripts/deploy.js,非常简单,只是为了将咱们的合约部署到咱们稍后抉择的任何网络上(如果您还没有留神到,咱们将抉择 Goerli)。

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);
  });

为了审查我的项目构造,咱们当初有一个智能合约和两个 Hardhat 脚本:

当初有了这个编码并保留的 deploy.js 脚本,如果你运行以下命令:

npx hardhat run scripts/deploy.js

你将会看到一行被打印进去:

BuyMeACoffee deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3

乏味的是,如果您一遍又一遍地运行它,每次都会看到完全相同的部署地址:

(base) ➜  BuyMeACoffee-contracts git:(main) ✗ npx hardhat run scripts/deploy.js
BuyMeACoffee deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
(base) ➜  BuyMeACoffee-contracts git:(main) ✗ npx hardhat run scripts/deploy.js
BuyMeACoffee deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
(base) ➜  BuyMeACoffee-contracts git:(main) ✗ npx hardhat run scripts/deploy.js
BuyMeACoffee deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3

为什么呢?这是因为当你运行脚本时,Hardhat 工具默认应用本地开发网络,间接在你的电脑上运行。它疾速而确定性高,非常适合疾速进行一些根本查看。

然而,为了理论部署到运行在寰球各地节点上的测试网络,咱们须要更改咱们的 Hardhat 配置文件以给咱们提供这个选项。

这就是 hardhat.config.json 文件的作用。

在咱们深入研究之前,疾速揭示一句:

  • 📘配置很难!爱护好你的机密!

    有很多小细节可能出错,而且事件总是在变动。最危险的是机密值,例如你的 MetaMask 私钥和 Alchemy URL。

    如果你遇到问题,请查看 Ethereum StackExchange、Alchemy Discord,或者谷歌你的错误信息。

    而且,永远不要分享你的机密!你的密钥,你的硬币!

当您关上您的 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.18",
  networks: {
    goerli: {
      url: GOERLI_URL,
      accounts: [PRIVATE_KEY],
    },
  },
};

这里产生了一些事件:

  • 在配置文件的顶部导入 hardhat-ethershardhat-waffledotenv,咱们的整个 Hardhat 我的项目都能够拜访这些依赖项。
  • 我晓得咱们还没有介绍dotenv,这是一个重要的工具,咱们稍后谈判到它。
  • process.env.GOERLI_URLprocess.env.PRIVATE_KEY 是咱们能够拜访环境变量以在配置文件中应用而不裸露机密值的办法。
  • modules.exports 外部,咱们应用 Solidity 编译器版本 0.8.18。不同的编译器版本反对不同的性能和语法集,因而匹配此版本与咱们的BuyMeACoffee.sol 智能合约顶部的 pragma 申明十分重要。
  • 如果您返回到该文件,您能够穿插查看语句 pragma solidity ^0.8.9;。在这种状况下,即便数字不齐全匹配,也能够,因为符号^ 示意任何大于或等于 0.8.9 的版本都能够应用。
  • 还在 modules.exports 中,咱们定义了一个蕴含一个名为 goerli 的测试网络配置的 networks 设置。当初在咱们进行部署之前,咱们须要确保装置最初一个工具,即 dotenv 模块。正如它的名字所示,dotenv帮忙咱们将 .env 文件连贯到咱们我的项目的其余部分。让咱们设置它。

装置 dotenv:

npm install dotenv

创立 .env 文件:

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>

你会留神到我没有透露任何我的秘密。是的。平安第一。你能够把它放在那个文件里,只有你也有一个 .gitignore 来确保你不会意外地将该文件推送到版本控制。确保 .env 在你的 .gitignore 中列出。

node_modules
.env
coverage
coverage.json
typechain
typechain-types

# Hardhat files
cache
artifacts

此外,为了获取所需的环境变量,您能够应用以下资源:

  • GOERLI_URL – 在 Alchemy 上注册账户,创立一个 Ethereum -> Goerli 应用程序,并应用 HTTP URL
  • GOERLI_API_KEY – 从您雷同的 Alchemy Ethereum Goerli 应用程序中,您能够获取 URL 的最初一部分,那将是您的 API KEY
  • PRIVATE_KEY – 依照 MetaMask 的这些阐明导出您的私钥。

当初,曾经装置了 dotenv,并在您的 .env 文件中填写了内容,咱们简直筹备好将其部署到 Goerli 测试网!

咱们须要做的最初一件事是确保您领有一些 Goerli ETH。这是一种虚伪以太币,使您可能在 Goerli 测试网络上练习进行各种操作,这是构建以太坊应用程序的实际区域。这样,您就不用在以太坊主网上破费真钱。

请返回 https://www.goerlifaucet.com 并应用您的 Alchemy 账户登录以获取一些收费的测试以太币。

当初咱们能够开始部署!

运行部署脚本,这次增加一个非凡标记来应用 Goerli 网络:

npx hardhat run scripts/deploy.js --network goerli

如果你在这里遇到任何谬误,例如 Error HH8,我强烈建议你在 Google、Stack Overflow 或 Ethereum Stackexchange 上搜寻解决方案。当你的 hardhat.config.js.env 或你的 dotenv 模块设置不正确时,遇到这些问题是很常见的。

如果一切顺利,几秒钟后你应该可能在控制台看到你的合约地址:

BuyMeACoffee deployed to: 0xbDde17D0BEA683db1bDa89f26c5c8C786230525E

🎉 祝贺!🎉

您当初曾经在 Goerli 测试网上部署了合约。您能够通过在此处粘贴您的地址来查看它:https://goerli.etherscan.io/

在咱们持续本教程的前端网站 (dapp) 局部之前,让咱们筹备一个咱们稍后须要应用的脚本,withdraw.js脚本。

实现一个 withdraw 脚本

稍后当咱们公布咱们的网站时,咱们须要一种形式来收集咱们的敌人和粉丝留给咱们的所有精彩小费。咱们能够编写另一个 hardhat 脚本来实现这个目标!

scripts/withdraw.js 中创立一个文件。

// 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 = "0xbDde17D0BEA683db1bDa89f26c5c8C786230525E";
  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);
  });

您的我的项目构造应该如下所示:

这个脚本最重要的局部是当咱们调用 withdrawTips() 函数时,从合约余额中取出资金并发送到所有者的钱包中:

// Withdraw funds if there are funds to withdraw.
  if (contractBalance !== "0.0") {console.log("withdrawing funds..")
    const withdrawTxn = await buyMeACoffee.withdrawTips();
    await withdrawTxn.wait();}

如果合约中没有资金,咱们会防止尝试提取以防止不必要地领取燃气费用。

运行脚本时,您将看到如下输入:

(base) ➜  BuyMeACoffee-contracts git:(main) ✗ npx hardhat run scripts/withdraw.js
current balance of owner:  3.164940181128555531 ETH
current balance of contract:  0.0 ETH
no funds to withdraw!
current balance of owner:  3.164940181128555531 ETH

请留神,这次咱们没有增加 -network goerli 标记,这是因为咱们的脚本间接在逻辑中硬编码网络配置:

const provider = new hre.ethers.providers.AlchemyProvider(
    "goerli",
    process.env.GOERLI_API_KEY
);

太好了,当初咱们有一种办法来提取合同的小费!

让咱们进入这个我的项目的 dapp 局部,这样咱们就能够与所有敌人分享咱们的小费页面 :)

应用 Replit 和 Ethers.js 构建前端的 Buy Me A Coffee 网站 dapp

为了放弃简洁和清晰,咱们将应用一个疾速启动演示我的项目的神奇工具,称为 Replit IDE。

拜访我的示例我的项目,而后分叉它以创立您本人的正本以进行批改:https://replit.com/@thatguyintech/BuyMeACoffee-Solidity-DeFi-Tipping-app

您还能够在此查看残缺的网站代码:https://github.com/alchemyplatform/RTW3-Week2-BuyMeACoffee-Website

在 fork repl 之后,您会被带到一个 IDE 页面,您能够:

  • 查看一个Next.js Web 应用程序的代码
  • 取得拜访控制台、终端和 README.md 文件的预览的权限
  • 查看您 dapp 的热从新加载版本

它应该看起来像这样:

这个教程的这个局部将会疾速而乏味——咱们将更新一些变量,使其连贯到咱们在我的项目晚期部署的智能合约,并且在网站上显示您本人的名字!

首先,让咱们将所有货色连贯并使其失常工作,而后我会向您解释每个局部的内容。

这里是咱们须要进行的更改:

  1. 更新 pages/index.js 中的 contractAddress
  2. 在 pages/index.js 中更新名称字符串为您本人的名称
  3. 确保 utils/BuyMeACoffee.json 中的合约 ABI 与您的合约匹配

在 pages/index.js 中更新 contractAddress

您能够看到 contractAddress 变量曾经填充了一个地址。这是一个我部署的示例合约,您能够应用它,然而如果您这样做 … 所有发送到您的网站的小费都将发送到我的地址 :)

您能够通过从咱们之前部署 BuyMeACoffee.sol 智能合约时粘贴您的地址来解决此问题。

在 pages/index.js 中更新名称字符串为您本人的名字

当初这个网站上到处都是我的名字。查找所有应用“Albert”的中央,并将其替换为您的名字 / 匿名材料 /ENS 域,或者您心愿人们称说您的任何内容。

您能够应用“cmd + F”或“ctrl + F”查找所有实例以进行替换。

确保 utils/BuyMeACoffee.json 中的合同 ABI 匹配

这也是一个要害的检查点,特地是当您稍后对智能合约进行更改(在本教程之后)时。

ABI 是应用程序二进制接口,它只是一种让咱们的前端代码晓得能够在智能合约上调用哪些函数的花哨办法。ABI 在智能合约编译时生成在 json 文件中。您能够在智能合约文件夹中的门路 artifacts/contracts/BuyMeACoffee.sol/BuyMeACoffee.json 找到它。

每当您更改您的智能合约代码并重新部署时,您的 ABI 也会更改。将其复制并粘贴到 Replit 文件中:utils/BuyMeACoffee.json

如果应用程序尚未运行,您能够转到 shell 并应用“npm run dev”启动本地服务器以测试您的更改。该网站应在几秒钟内加载:

Replit 的精妙之处在于,一旦你的网站建好,你能够回到你的个人资料,找到 Replit 我的项目链接,而后将其发送给敌人,让他们拜访你的小费页面。

当初让咱们来逐渐浏览一下网站和代码。你曾经能够从下面的截图看出,当你第一次拜访 Dapp 时,它会查看你是否已装置了 MetaMask,并且你的钱包是否已连贯到该网站。第一次拜访时,你将无奈连贯,所以会呈现一个按钮,要求你“连贯你的钱包”。

当你点击“连贯你的钱包”后,MetaMask 的窗口会弹出,询问你是否要通过签订音讯确认连贯。这个音讯签订不须要任何燃气费或老本。

一旦签名实现,网站将确认你的连贯,你将可能看到咖啡表单,以及其余访问者留下的任何以前的备忘录。

砰!就是这样!这就是整个我的项目。请花一秒钟工夫为本人鼓掌,并回顾一下你走过的旅程。

总结一下:

  • 咱们应用 Hardhat 和 Ethers.js 编写、测试和部署了一个自定义的 Solidity 智能合约。
  • 咱们应用 Alchemy 和 MetaMask 将智能合约部署到 Goerli 测试网络。
  • 咱们实现了一个提款脚本,以便咱们可能享受咱们的劳动成果。
  • 咱们应用 Ethers.js 来加载合约 ABI,将一个应用 Next.js、React 和 Replit 构建的前端网站连贯到智能合约上。

这是很多的工作!

本文由 mdnice 多平台公布

正文完
 0