本文介绍了如何使用Svelte框架构建一个与以太坊Sepolia网络上的智能合约交互的示例应用。文章详细阐述了技术栈、环境配置、智能合约实现、前端与智能合约的交互逻辑,并提供了从本地测试部署到公共测试网的完整流程。文章结构清晰,包含步骤指导和代码示例,为开发者提供了实用的参考。
今天,我们将构建一个示例应用程序,使用 Svelte 并将其连接到一个我们在 Sepolia 网络上部署的智能合约。Svelte 是一个免费的开源前端项目,正在持续开发中,允许用户轻松构建 Web 应用程序。
我们的前端将能够做到以下几点:
我们的技术栈如下:
请参阅此链接 here 以查看最终代码。
依赖项 | 版本 |
---|---|
node.js | 18.18.1 |
svelte | ^3.0.0 |
hardhat | ^2.18.1 |
ethers | ^5.7.2 |
@rollup/plugin-json | ^6.0.1 |
dayjs | ^1.10.7 |
sirv-cli | ^1.0.0 |
rollup | ^2.3.4 |
通过运行以下命令来克隆 this svelte 启动模板,其中包含所有必需的组件和 CSS 属性。
使用 HTTPS:
git clone https://github.com/quiknode-labs/qn-guide-examples.git
或者,使用 SSH:
git clone git@github.com:quiknode-labs/qn-guide-examples.git
接下来,导航到项目目录(例如,qn-guide-examples/ethereum/wave-portal-svelte/code/wave-portal-starter-boilerplate)并通过运行以下命令安装 node_modules:
cd qn-guide-examples/ethereum/wave-portal-svelte/code/wave-portal-starter-boilerplate/
yarn
在 wave-portal-starter-boilerplate 目录中,安装 @nomicfoundation/hardhat-toolbox(其中包含我们进行智能合约开发所需的所有内容),运行以下命令:
yarn add --dev hardhat @nomicfoundation/hardhat-toolbox @nomicfoundation/hardhat-network-helpers @nomicfoundation/hardhat-chai-matchers @nomicfoundation/hardhat-ethers @nomicfoundation/hardhat-verify chai ethers hardhat-gas-reporter solidity-coverage @typechain/hardhat typechain @typechain/ethers-v6
现在,让我们使用 Hardhat 初始化我们的智能合约开发环境。
要使用 Hardhat 创建一个新的智能合约开发环境,请运行下面的命令。在终端窗口中运行命令之前,请确保你处于 wave-portal-starter-boilerplate 文件夹。
npx hardhat init
在提示时选择以下设置:
现在,你应该在项目文件夹内看到一个 hardhat.config.js 文件。
接下来,打开你的 hardhat.config.js 文件。此文件包含有关 Hardhat 以太坊环境的所有配置。更新该文件的内容,使其如下所示:
require("@nomicfoundation/hardhat-toolbox");
module.exports = {
solidity: "0.8.4",
paths: {
artifacts: "./src/artifacts",
},
networks: {
hardhat: {
chainId: 1337,
},
},
};
有了我们的 Hardhat 配置设置后,让我们继续。
我们现在需要创建所需的文件。首先,我们将创建一个 contracts 并在其中创建一个名为 WavePortal.sol 的文件:
mkdir contracts
echo > contracts/WavePortal.sol
该文件将包含我们的智能合约代码。在此新文件中,粘贴以下智能合约代码。该合约将允许我们存储 wave,reaction type,greeting message 并向随机用户发放奖品。
你的 WavePortal.sol 文件应如下所示:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract WavePortal {
enum Reaction {
Wave,
Cake,
Hype
}
struct Wave {
Reaction reaction;
string message;
address waver;
uint256 timestamp;
}
uint256 totalWaves;
Wave[] public waveList;
event NewWave(
Reaction reaction,
string message,
address indexed from,
uint256 timestamp
);
constructor() {}
function wave(Reaction _reaction, string memory _message) public {
totalWaves += 1;
waveList.push(Wave(_reaction, _message, msg.sender, block.timestamp));
emit NewWave(_reaction, _message, msg.sender, block.timestamp);
}
function getAllWaves() public view returns (Wave[] memory) {
return waveList;
}
function getTotalWaves() public view returns (uint256) {
return waveList.length;
}
}
对上述代码的解释。
WavePortal
。Wave
,Cake
和 Hype
作为预定义值。totalWaves
用于存储总波数。waveList
用于存储所有波。NewWave
,包含有关反应、消息、发送者地址和波的时间戳的信息。此事件在我们的波成功存储到合约时触发。Reaction
,将存储我们的反应信息,_message 为类型 string,将存储用户的消息。将 _message 声明为 memory 意味着,函数执行后将会从内存中销毁。我们可以使用我们的合约的 ABI、ethers.js 库和我们的 contract address 从 Svelte 应用程序与智能合约交互。ABI 代表应用程序二进制接口。我们可以将 ABI 视为一个接口,提供前端与智能合约中的所有可调用函数和变量的访问。
我们可以通过使用 Hardhat 这样的开发框架编译我们的智能合约来获取 ABI。你经常可以在 Etherscan 上找到智能合约的 ABI。
所以,让我们通过运行下面的命令编译我们的智能合约。
npx hardhat compile
确保你在项目的根目录内(例如,wave-portal-starter-boilerplate)
在成功编译后,你应该在 code/wave-portal-starter-boilerplate/src 文件夹下看到一个名为 artifacts 的新文件夹。你可以在 artifacts/contracts/WavePortal.json 文件夹下找到我们合约的 ABI。我们可以通过简单地导入此 .json 文件来使用这个 ABI。
为了方便快速的交互,我们将把合约部署到本地测试节点。为此,我们需要在同一目录中创建另一个终端窗口(Mac 用户按 CMD + T;Windows 用户按 CTRL + SHIFT + T)。然后,运行以下命令启动本地链节点:
npx hardhat node
运行此命令后,将在终端中列出所有帐户及其私钥:
这些是 Hardhat 创建给我们的 20 个测试帐户,我们可以使用这些帐户在本地部署和测试我们的智能合约。每个帐户都有足够数量的测试 Ether。
让我们使用这些帐户之一将智能合约部署到本地主机。但在此之前,在我们的 scripts
文件夹中,我们将创建一个名为 deploy.js 的文件:
mkdir scripts
echo > scripts/deploy.js
此文件将在我们尝试部署合约时执行。在这个 deploy.js 文件中,包含以下代码:
const hre = require("hardhat")
async function main() {
const WavePortal = await hre.ethers.getContractFactory("WavePortal");
const wavePortal = await WavePortal.deploy();
await wavePortal.waitForDeployment();
console.log("WavePortal deployed to:", wavePortal.target);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
让我们回顾一下代码。
.deploy()
,这将部署合约的实例。.waitForDeployment()
,等待合约部署。main()
函数并添加错误处理。现在,让我们运行部署脚本并提供标志 --network localhost
给 CLI,指示我们希望将合约部署到本地网络。
npx hardhat run scripts/deploy.js --network localhost
该脚本将我们的智能合约部署到本地网络,现在我们应该能够与之交互。
在成功部署后,你应该在终端中看到以下输出:
注意: 此合约是在本地节点上部署的,使用了我们启动本地网络时创建的第一个帐户。
请存储此地址以供参考,因为我们将在与 Svelte 客户端的智能合约交互时需要它。
现在,为了向我们在测试节点上部署的智能合约发送交易,我们需要配置 MetaMask 钱包以使用 Hardhat 环境创建的帐户之一。
让我们将其中一个帐户导入我们 MetaMask 钱包并使用其测试 ETH。为此,请按照以下步骤操作:
添加自定义网络后,将你的网络更改为新添加的网络。接下来,我们将从 Hardhat 环境中导入 MetaMask 的其中一个私钥。
在 MetaMask 扩展程序顶部,单击 账户 下拉,然后单击 导入账户。输入在运行 Hardhat 本地区块链的终端窗口中获得的私钥,然后单击 导入。确保在继续进行下一个步骤之前选择此帐户。
由于我们的合约已成功部署并且 MetaMask 钱包已配置,让我们开始从 Svelte 前端进行交互。
在本教程中,我们将不专注于开发用户界面。相反,我们将完全专注于核心功能和集成。在前端中,我们将主要关注两件事:
让我们开始与前端的集成。要测试我们的前端,使用以下命令启动服务器。但在启动服务器之前,请确保当前目录位于 code/wave-portal-starter-boilerplate/
:
yarn dev
现在,你应该在 localhost:5000 上看到如下前端。如果 localhost:5000 无法工作,则尝试 http://127.0.0.1:5000/。
注意:如果你是 M1 用户,你可能需要在 Mac 上禁用 Airplay 设置,以使其正常工作。
你会注意到,通过单击任何一个问候,什么也没有发生。同时,我们无法看到任何以前的问候。因此,让我们添加逻辑,将问候发送到我们的智能合约并获取所有以前的问候。
导航到 code/wave-portal-starter-boilerplate/src
文件夹下的 App.svelte 文件。App.svelte 在服务器启动时呈现到主页,因此包含获取波的所有功能。
更新你在部署时记录到 CLI 中的合约地址,放在第 10 行。
const CONTRACT_ADDRESS = 'YOUR_CONTACT_ADDRESS';
现在,在 App.svelte 的第 12 行的 getAllWaves()
函数中粘贴以下代码。此函数从区块链网络提取所有问候到我们的客户端。
async function getAllWaves() {
if (!window.ethereum) {
return;
}
const provider = new ethers.providers.Web3Provider(window.ethereum);
const wavePortalContract = new ethers.Contract(
CONTRACT_ADDRESS,
WavePortal.abi,
provider
);
const recievedWaves = await wavePortalContract.getAllWaves();
if (!recievedWaves) {
waveList = [];
return;
}
const normalizeWave = (wave) => ({
reaction: wave.reaction,
message: wave.message,
waver: wave.waver,
timestamp: new Date(wave.timestamp * 1000),
});
waveList = recievedWaves
.map(normalizeWave)
.sort((a, b) => b.timestamp - a.timestamp);
console.log('waveList: ', waveList);
return;
}
对上述代码的解释。
我们必须导入 WavePortal 合约的 ABI,以便能够与我们的智能合约交互。因此,将以下导入语句添加到 App.svelte 的第 3 行。
import { ethers } from 'ethers';
import WavePortal from './artifacts/contracts/WavePortal.sol/WavePortal.json'
你应该会看到如下所示的错误。
这是因为我们尝试在 App.svelte 中导入 JSON 文件,而要导入 JSON 文件,我们需要通过运行以下命令添加额外的插件 rollup-plugin。
yarn add @rollup/plugin-json
导航到 code/wave-portal-starter-boilerplate/
目录中的 rollup.config.js 文件。此文件包含所有有关 rollup 的配置。现在,在你的 rollup.config.js 文件中导航到插件数组,如下所示并添加 json(),位于第 60 行。
plugins: [\
commonjs(),\
json(),\
...\
]
在 code/wave-portal-starter-boilerplate/
目录中的 rollup.config.js 文件中,为了使用 json(),我们还需要从我们新添加的插件中导入 json(),因此在 rollup.config.js 文件的第 7 行添加以下导入语句。
import json from "@rollup/plugin-json";
现在,重新启动开发服务器,你应该看到前端服务器成功启动。目前,你不会在前端看到任何问候,因为我们没有问候。所以,让我们添加一个发送问候的功能。
为此,导航到 code/wave-portal-starter-boilerplate/src/components/SendWave.svelte 文件。此文件将包含发送波的逻辑。通过粘贴以下代码来完成第 7 行的 sendWaveReaction()
函数。此功能将发送 wave reaction。
async function sendWaveReaction(reaction, message) {
loading = true;
try {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const wavePortalContract = new ethers.Contract(
CONTRACT_ADDRESS,
WavePortal.abi,
signer
);
const transaction = await wavePortalContract.wave(reaction, message, {
gasLimit: 400000,
});
await transaction.wait();
message = '';
fetchWaves();
loading = false;
} catch (error) {
alert('Error while sending wave', error);
loading = false;
}
}
以上代码的回顾。
我们还需要在 SendWave.svelte 文件中第 2 行添加以下导入语句。
import { ethers } from 'ethers';
import WavePortal from '../artifacts/contracts/WavePortal.sol/WavePortal.json';
为了从我们的前端与智能合约交互,我们需要将 MetaMask 钱包连接到我们的网站。为此,在 code/wave-portal-starter-boilerplate/src/components/Wallet.svelte 文件中,通过粘贴以下代码在第 6 行来完成 connectWallet()
函数。 Wallet.svelte 将包含将 MetaMask 钱包连接到前端所需的所有逻辑。
async function connectWallet() {
walletConnected = false;
const { ethereum } = window;
await ethereum
.request({ method: 'eth_requestAccounts' })
.then((accountList) => {
const [firstAccount] = accountList;
account = firstAccount;
walletConnected = true;
})
.catch((error) => {
walletConnected = false;
connectWalletError = error;
console.log('error connecting wallet');
});
}
以上代码的解释:
({ method: 'eth_requestAccounts' })
,这将提供连接钱包的帐户。现在,重新启动服务器,如果需要,你应该在单击 连接 MetaMask 按钮时看到 MetaMask 弹出窗口。连接后,我们将能够成功发送问候、挥手,并获取所有问候。
现在,我们的智能合约在本地节点上成功运行,因此让我们在实时公共测试网上部署它。你可以随意关闭在 Hardhat 上运行的本地节点,因为不再需要它。
在本教程中,我们将部署到 Sepolia 测试网络。为此,我们需要更新我们的 MetaMask 钱包以连接到 Sepolia 网络,并向自己发送一些测试 ETH(你可以使用 QuickNode Multi-Chain Faucet)。不用担心,我们将向你展示每一步。
导航到 Endpoints 页面上的 QuickNode(不用担心,你可以免费创建一个帐户),然后单击 创建端点。然后,选择 Ethereum 链和 Sepolia 测试网络,然后单击 创建端点。
创建端点后,你将拥有一个 HTTP 和 WSS URL。为了本指南的目的,请保留 HTTP 提供程序 URL,因为你稍后将需要它。
由于我们将在测试网上部署智能合约,因此我们需要测试网 ETH 来支付交易费用。
你可以使用 QuickNode Multi-Chain Faucet 在许多区块链上获取测试 ETH。只需选择区块链和网络,然后粘贴你的钱包地址或连接钱包即可。
注意:你需要在以太坊主网中至少有 0.001 ETH 才能使用水龙头。
要在测试网上部署,我们需要在 hardhat.config.js 中更新额外的网络和钱包信息。我们需要设置从中进行部署的钱包的私钥。你可以通过单击右上角的省略号(三个点),然后点击 账户详细信息 --> 显示私钥 来导出你的私钥。请保留此内容,以便你在下面的文件中使用。
因此,请打开 hardhat.config.js 文件并输入以下代码:
require("@nomicfoundation/hardhat-toolbox");
module.exports = {
solidity: "0.8.4",
paths: {
artifacts: './src/artifacts',
},
networks: {
hardhat: {},
sepolia: {
url: "<YOUR_QUICKNODE_URL_HERE>",
accounts: [`0x${your-private-key}`]
}
},
};
你需要将 YOUR_QUICKNODE_URL_HERE 替换为你在 创建以太坊 Sepolia 端点 中获得的 HTTP 提供程序 URL。此外,用你从 MetaMask 获得的私钥替换 your-private-key 占位符。
现在,运行以下命令,将我们的智能合约部署到 Sepolia 网络,确保你在根目录(例如,code/wave-portal-starter-boilerplate/)内。
npx hardhat run scripts/deploy.js --network sepolia
在成功部署后,你应该在终端中看到以下输出。
WavePortal deployed to: 0x4f5F98f3696e1dDc107fd786d252D6Ff8D351B6b
你需要在 src/App.svelte
中更新你的合约地址,使用你刚刚在 Sepolia 区块链上创建的合约地址。
另外,由于我们的前端应用程序使用与 Hardhat toolbox 不同版本的 Ethers,我们需要将其降级到正确的版本:
yarn remove ethers
yarn add --dev ethers@5.7.2
刷新你的前端应用并连接你的钱包,现在你应该能够在 Sepolia 区块链上与其进行交互。
恭喜!你已使用 Svelte 集成了你的第一个 dApp。请尝试上述挑战,如果你遇到任何错误,随时通过 Twitter 联系我(@0xCrispy)。
请注意,此项目中仍有许多内容可以改进。因此,我们挑战你尝试以下内容:
如果你有任何问题,请随时通过我们专用的 Discord 频道与我们联系或使用下面的表单提供反馈。通过关注我们的 Twitter 和 Telegram 公告频道,随时了解最新动态。
让我们知道 如果你有任何反馈或对新主题的请求。我们非常乐意听取你的意见。
- 原文链接: quicknode.com/guides/eth...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!