如果你是区块链开发的新手并且不知道从哪里开始,或者你只是想了解如何部署智能合约并与之交互,那么本指南适合你。 我们将介绍使用虚拟钱包 (Metamask)、Solidity、Hardhat 和 Alchemy 在 Goerli 测试网络上创建和部署一个简单的智能合约(如果你还不明白其中的任何含义,请不要担心,我们将 解释一下!)。
有很多方法可以向以太坊链发出请求。 为简单起见,我们将在 Alchemy 上使用免费帐户(如果你还没有 Alchemy 帐户,请点击在此处免费注册),这是一个区块链开发平台和 API,允许我们与以太坊链进行通信,而无需运行我们自己的节点。 该平台还具有用于监视和分析的开发人员工具,我们将在本教程中利用这些工具来了解我们的智能合约部署中的幕后情况。
创建 Alchemy 帐户后,你可以通过创建应用程序来生成 API 密钥。 这将允许我们向 Goerli 测试网络发出请求。 如果你不熟悉测试网,请查看官方给出的指南。

将鼠标悬停在导航栏中的“应用程序”上并单击“创建应用程序”,导航到 Alchemy 仪表板中的“创建应用程序”页面:

将你的应用命名为“Hello World”,提供简短描述,为环境选择“Staging”(用于你的应用记账),然后为你的网络选择“Goerli”。

仔细检查你是否选择了 Goerli 测试网!
点击“创建应用程序”,就是这样! 你的应用程序应显示在下表中。
我们需要一个以太坊账户来发送和接收交易。 在本教程中,我们将使用 Metamask,这是浏览器中的一个虚拟钱包,用于管理你的以太坊账户地址。 如果你想了解更多关于以太坊交易如何运作的信息,请查看以太坊基金会的这个页面。
你可以在此处免费下载和创建 Metamask 帐户。 当你正在创建帐户时,或者如果你已经有帐户,请确保切换到右上角的“Goerli 测试网络”(这样我们就不会处理真钱)。

为了将我们的智能合约部署到测试网络,我们需要一些虚假的 Eth。 要获取 Eth,你可以前往 Goerli 水龙头并输入你的 Goerli 帐户地址,然后单击“Send Me Eth”。 由于网络流量,可能需要一些时间才能收到你的虚假 Eth。 (在撰写本文时,大约需要 30 分钟。)不久之后,你应该会在你的 Metamask 帐户中看到 Eth!
为了仔细检查我们的余额,让我们使用 Alchemy 的 composer 工具发出一个 eth_getBalance 请求。 这将返回我们钱包中的 Eth 数量。

输入你的 Metamask 帐户地址并单击“发送请求”后,应该会看到如下所示的响应:
{"jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000"}
注意:这个结果是 wei 而不是 eth。 魏被用作以太的最小面额。 wei到eth的换算为:1 eth = 10^18 wei。 因此,如果我们将 0x2B5E3AF16B1880000 转换为十进制,我们会得到 5*10^18,它等于 5 eth。 呸! 我们的假钱就在那里🤑。
mkdir hello-world
cd hello-world
首先,我们需要为我们的项目创建一个文件夹。 导航到你的命令行并键入:
现在我们在项目文件夹中,我们将使用 npm init 来初始化项目。 如果你还没有安装 npm,请按照这些说明进行操作(我们还需要 Node.js,所以也下载它!)。
npm init # (or npm init --yes)
你如何回答安装问题并不重要,以下是我们的做法以供参考:
package name: (hello-world)
version: (1.0.0)
description: hello world smart contract
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/.../.../.../hello-world/package.json:
{
"name": "hello-world",
"version": "1.0.0",
"description": "hello world smart contract",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Hardhat 是一个用于编译、部署、测试和调试以太坊软件的开发环境。 在部署到实时链之前,它可以帮助开发人员在本地构建智能合约和 dApp。
在我们的 hello-world 项目中运行:
npm install --save-dev hardhat
在我们的 hello-world 项目文件夹中,运行:
npx hardhat
然后,你应该会看到一条欢迎消息和用于选择你想要执行的操作的选项。 选择“创建一个空的 hardhat.config.js”:
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.0.11 👷
What do you want to do? …
Create a sample project
❯ Create an empty hardhat.config.js
Quit
这将为我们生成一个 hardhat.config.js 文件,我们将在其中为我们的项目指定所有设置(在第 13 步中)。
为了让我们的项目井井有条,我们将创建两个新文件夹。 在命令行中导航到 hello-world 项目的根目录并键入:
mkdir contracts
mkdir scripts
你可能会问自己,我们到底什么时候要编写代码? 好吧,我们到了,第 10 步😄
在你最喜欢的编辑器中打开 hello-world 项目(我喜欢 VSCode)。 智能合约是用一种称为 Solidity 的语言编写的,我们将使用它来编写我们的 HelloWorld.sol 智能合约。
// 使用语义版本控制指定 Solidity 的版本。
// 了解更多:https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
pragma solidity >=0.7.3;
// 定义一个名为“HelloWorld”的合约。
// 合约是功能和数据(其状态)的集合。 部署后,合约将驻留在以太坊区块链上的特定地址。 了解更多:https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
contract HelloWorld {
// 调用更新函数时发出
// 智能合约事件是你的合约将区块链上发生的事情传达给你的应用程序前端的一种方式,它可以“监听”某些事件并在它们发生时采取行动。
event UpdatedMessages(string oldStr, string newStr);
// 声明一个`string`类型的状态变量`message`。
// 状态变量是其值永久存储在合约存储中的变量。 关键字 `public` 使变量可以从合约外部访问,并创建一个函数,其他合约或客户端可以调用该函数来访问该值。
string public message;
// 与许多基于类的面向对象语言类似,构造函数是一种特殊函数,仅在合约创建时执行。
// 构造函数用于初始化合约的数据。 了解更多:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
constructor(string memory initMessage) {
// 接受字符串参数 `initMessage` 并将值设置到合约的 `message` 存储变量中)。
message = initMessage;
}
// 一个接受字符串参数并更新“消息”存储变量的公共函数。
function update(string memory newMessage) public {
string memory oldMsg = message;
message = newMessage;
emit UpdatedMessages(oldMsg, newMessage);
}
}
这是一个超级简单的智能合约,它在创建时存储一条消息,并且可以通过调用更新函数来更新。
我们已经创建了一个 Metamask 钱包、Alchemy 账户,并编写了我们的智能合约,现在是时候连接这三者了。
从你的虚拟钱包发送的每笔交易都需要使用你唯一的私钥进行签名。 为了向我们的程序提供此权限,我们可以将我们的私钥(和 Alchemy API 密钥)安全地存储在环境文件中。
首先,在你的项目目录中安装 dotenv 包:
npm install dotenv --save
这是一个超级简单的智能合约,它在创建时存储一条消息,并且可以通过调用更新函数来更新。
你的环境文件必须命名为 .env,否则将不会被识别为环境文件。请勿将其命名为 process.env 或 .env-custom 或其他任何名称。
你的 .env 应如下所示:
API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
PRIVATE_KEY = "your-metamask-private-key"
PRIVATE_KEY 私钥的获取方式参考这篇文章。
API_URL 的获取方式可以参考下图:

为了将这些连接到我们的代码,我们将在第 13 步的 hardhat.config.js 文件中引用这些变量。
Ethers.js 是一个库,它通过使用更友好的方法包装标准** JSON-RPC **方法,使交互和向以太坊发出请求变得更加容易。
Hardhat 使的集成插件变得非常容易,以获得额外的工具和扩展功能。 我们将利用 Ethers 插件进行合约部署(Ethers.js 有一些超级干净的合约部署方法)。
在你的项目目录的命令行中输入如下命令:
npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0"
在下一步中,我们还需要在我们的 hardhat.config.js 中使用以太币。
到目前为止,我们已经添加了几个依赖项和插件,现在我们需要更新 hardhat.config.js 以便我们的项目了解所有这些。
将你的 hardhat.config.js 更新为如下所示内容:
/**
* @type import('hardhat/config').HardhatUserConfig
*/
require('dotenv').config();
require("@nomiclabs/hardhat-ethers");
const { API_URL, PRIVATE_KEY } = process.env;
module.exports = {
solidity: "0.7.3",
defaultNetwork: "goerli",
networks: {
hardhat: {},
goerli: {
url: API_URL,
accounts: [`0x${PRIVATE_KEY}`]
}
},
}
为了确保到目前为止一切正常,让我们编译我们的合约。 编译任务是内置安 hardhat 的任务之一。
从命令行运行:
npx hardhat compile
你可能会收到有关源文件中未提供 SPDX 许可证标识符的警告,但无需担心!
现在我们的合约已经写好并且我们的配置文件已经准备好了,是时候编写我们的合约部署脚本了。
导航到 /scripts 文件夹并创建一个名为 deploy.js 的新文件,向其中添加以下内容:
async function main() {
const HelloWorld = await ethers.getContractFactory("HelloWorld");
// 开始部署,返回一个解析为合约对象的 promise
const hello_world = await HelloWorld.deploy("Hello World!");
console.log("Contract deployed to address:", hello_world.address);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
其中:
const HelloWorld = await ethers.getContractFactory("HelloWorld");
ethers.js 中的 ContractFactory 是用于部署新智能合约的抽象,因此 HelloWorld 这里是我们的 hello world 合约实例的工厂。 使用 hardhat-ethers 为 ContractFactory 和 Contract 添加插件时,实例默认连接到第一个签名者(所有者)。
const hello_world = await HelloWorld.deploy();
在 ContractFactory 上调用 deploy() 将启动部署,并返回解析为 Contract 对象的 Promise。 这是为我们的每个智能合约功能提供方法的对象。
我们终于准备好部署我们的智能合约了! 切换到命令行并运行:
npx hardhat run scripts/deploy.js --network goerli
然后,你应该会看到如下内容:
Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570
请复制并粘贴此地址以将其保存在某处,因为我们将在以后的教程中使用此地址,因此你不想丢失它。
如果我们去 Goerli etherscan 并搜索我们的合约地址,我们应该能够看到它已经成功部署。 交易将如下所示:

发件人地址应该与你的 Metamask 帐户地址匹配,收件人地址将显示“Contract Creation”,但如果我们点击交易,我们将在收件人字段中看到我们的合同地址:

至此,恭喜你! 你已经在以太坊链上部署了一个智能合约🎉
要了解幕后发生的事情,让我们导航到 Alchemy 仪表板中的 Explorer 选项卡。 如果你有多个 Alchemy 应用程序,请确保按应用程序过滤并选择“Hello World”。

在这里,你将看到当我们调用 deploy() 函数时,Hardhat/Ethers 在后台为我们进行的一些 JSON-RPC 调用。 这里要提到的两个重要的问题是 eth_sendRawTransaction,它是实际将我们的合约写入 Ropsten 链的请求,以及 eth_getTransactionByHash,它是一个请求读取我们的交易信息给定哈希(发送交易时的典型模式)。
要了解有关发送交易的更多信息,你可以从这里查看有关使用 Web3 发送交易的教程。
出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>
我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?