本节是《Solidity by Example》的中文翻译与深入讲解,专为零基础或刚接触区块链开发的小白朋友打造。我们将通过"示例 + 解说 + 提示"的方式,带你逐步理解每一段 Solidity 代码的实际用途与背后的逻辑。
Solidity 是以太坊等智能合约平台使用的主要编程语言,就像写网页要用 HTML 和 JavaScript,写智能合约就需要会 Solidity。
如果你从没写过区块链代码也没关系,只要你了解一点点编程概念,比如"变量""函数""条件判断",我们就能从最简单的例子开始,一步步建立你的 Solidity 编程思维。
Gas and Gas Price
Gas
执行一笔交易需要支付多少以太(Ether)?
你需要支付的以太 = 消耗的 Gas 数量 * Gas 价格,其中:
- Gas 是计算的单位
- 消耗的 Gas 是交易中使用的 Gas 总量
- Gas 价格是你愿意为每单位 Gas 支付的以太数量
Gas 价格更高的交易有更高的优先级被包含在区块中
未使用的 Gas 将被退还。
Gas Limit
Gas 消耗有两个上限:
- Gas 限制(你愿意为交易使用的最大 Gas 数量,由你设置)
- 区块 Gas 限制(一个区块允许的最大 Gas 数量,由网络设置)
描述解释:
- 什么是 Gas?
- Gas 是以太坊网络中用于衡量计算工作量的单位。每次执行交易(如调用合约函数、转账)都需要消耗一定量的 Gas。
- 类似于"油费",Gas 是运行区块链操作的"燃料"。
- 交易费用计算:
- 交易费用 = 消耗的 Gas 数量(Gas Spent) × Gas 价格(Gas Price)。
- Gas 数量:取决于交易的复杂程度(例如,存储数据比简单计算更耗 Gas)。
- Gas 价格:用户设置的每单位 Gas 愿意支付的以太(以 Wei 或 Gwei 为单位,例如 20 Gwei)。
- 更高的 Gas 价格会激励矿工(或验证者)优先处理你的交易。
- 未使用的 Gas:
- 如果交易设置的 Gas 限制(Gas Limit)高于实际消耗的 Gas,多余的 Gas 会退还给用户。
- 例如:设置 Gas 限制为 100,000,但实际只用了 50,000,则 50,000 Gas 的费用会退还。
- Gas 限制(Gas Limit):
- 交易 Gas 限制:由用户设置,表示愿意为单笔交易支付的最大 Gas 数量。
- 太低可能导致交易失败(Gas 不够完成操作)。
- 太高可能浪费 Ether(虽然未使用的 Gas 会退还)。
- 区块 Gas 限制:由以太坊网络设置,表示一个区块能包含的所有交易的 Gas 总量上限。
- 确保区块不会过大,影响网络性能。
- 交易 Gas 限制:由用户设置,表示愿意为单笔交易支付的最大 Gas 数量。
- 交易失败:
- 如果交易耗尽了 Gas 限制(例如进入无限循环),交易会失败,状态变更会被回滚,但已消耗的 Gas 费用不会退还。
javascript
// SPDX-License-Identifier: MIT
// 声明代码采用 MIT 开源许可证,允许自由使用、修改和分发代码。
pragma solidity ^0.8.26;
// 指定 Solidity 编译器版本必须大于或等于 0.8.26 并且小于 0.9.0。
// `pragma` 指令确保合约使用兼容的编译器版本,`^0.8.26` 表示支持 0.8.26 或更高版本(但不超过 0.9.0)。
contract Gas {
// 定义一个名为 `Gas` 的智能合约。
// 合约是一个运行在以太坊区块链上的程序,包含数据(状态变量)和逻辑(函数)。
// 这个合约的目的是展示 Gas 消耗的行为,特别是当 Gas 用尽时会发生什么。
uint256 public i = 0;
// 声明一个名为 `i` 的状态变量,类型为 `uint256`(256 位无符号整数,范围从 0 到 2^256-1)。
// 初始化值为 0,存储在区块链上。
// `public` 关键字表示该变量可以被外部访问,Solidity 会自动为其生成一个 getter 函数(类似于 `function i() public view returns (uint256)`)。
// Using up all of the gas that you send causes your transaction to fail.
// State changes are undone.
// Gas spent is not refunded.
// 如果用尽了你发送的所有 Gas,交易会失败。
// 状态变更会被撤销。
// 已消耗的 Gas 不会退还。
function forever() public {
// 定义一个名为 `forever` 的公共函数。
// `public` 表示函数可以被外部调用(用户、其他合约或 DApp)。
// 没有 `view` 或 `pure` 修饰符,表示函数会修改区块链状态,需消耗 Gas。
// Here we run a loop until all of the gas are spent
// and the transaction fails
// 这里运行一个循环,直到所有 Gas 耗尽,导致交易失败。
while (true) {
// 无限循环(`true` 表示循环条件永远成立)。
i += 1;
// 每次循环将状态变量 `i` 的值增加 1。
// 修改状态变量需要 Gas(因为更新区块链存储)。
// 由于是无限循环,函数会持续消耗 Gas,直到达到交易的 Gas 限制(Gas Limit)。
// 当 Gas 用尽时,交易失败,`i` 的变更被撤销(回滚),但已消耗的 Gas 不退还。
}
}
}
Gas
是一个简单的智能合约,展示了以太坊中 Gas 的消耗行为,特别是当交易耗尽 Gas 时的后果。
代码做什么?
- 状态变量
i
:- 存储一个数字,初始值为 0,永久保存在区块链上。
- 因为是
public
,可以通过 getter 函数(i()
)读取。
- 函数
forever
:- 运行一个无限循环,每次循环将
i
加 1。 - 每次循环修改
i
会消耗 Gas(因为更新区块链状态)。 - 由于循环永不停止,函数会一直消耗 Gas,直到达到用户设置的 Gas 限制(Gas Limit)。
- 当 Gas 用尽:
- 交易失败。
i
的任何变更(例如增加到某个值)会被撤销(回滚到 0)。- 已消耗的 Gas 费用不会退还。
- 运行一个无限循环,每次循环将
- Gas 消耗演示:
- 部署合约需要 Gas(初始化
i
)。 - 调用
forever
会消耗大量 Gas,直到失败。 - 调用
i
的 getter 函数是view
操作,链下调用免费。
- 部署合约需要 Gas(初始化
关键点:
- Gas 的作用:
- Gas 限制了交易的计算量,防止无限循环或恶意代码耗尽网络资源。
- 每种操作(例如计算、存储、调用)都有固定的 Gas 成本。
- 交易失败:
- 如果 Gas 用尽(例如
forever
的无限循环),交易失败,状态变更回滚。 - 已消耗的 Gas 不会退还,因为矿工已经为计算付出了工作。
- 如果 Gas 用尽(例如
- Gas 限制:
- 交易 Gas 限制 :用户设置,例如 100,000 Gas。如果
forever
耗尽这 100,000 Gas,交易失败。 - 区块 Gas 限制:网络设置,例如 30,000,000 Gas,限制一个区块的总 Gas 消耗。
- 交易 Gas 限制 :用户设置,例如 100,000 Gas。如果
- Gas 价格:
- 用户设置 Gas 价格(例如 20 Gwei),决定交易优先级。
- 总费用 = Gas 消耗 × Gas 价格(以 Wei 计算)。
- 用途:
- 理解 Gas 对于编写高效、安全的智能合约至关重要。
- 开发者需要优化代码,减少 Gas 消耗(例如避免无限循环)。
Gas 的注意事项
- Gas 耗尽:
- 如果交易耗尽 Gas(例如
forever
的无限循环),交易失败,状态回滚,已用 Gas 不退还。 - 确保设置合理的 Gas 限制,覆盖预期操作。
- 如果交易耗尽 Gas(例如
- Gas 优化:
- 避免无限循环或复杂计算。
- 使用
immutable
或constant
变量减少存储成本。 - 合并状态更新,减少写入次数。
- Gas 价格:
- Gas 价格过低可能导致交易长时间未被打包(特别是在网络拥堵时)。
- 使用 Gas 价格推荐工具(如 Etherscan Gas Tracker)设置合理价格。
- 安全性:
- 在处理 Ether 时,检查
msg.value
和 Gas 限制,防止意外失败。 - 避免在循环中调用外部合约(可能导致 Gas 攻击)。
- 在处理 Ether 时,检查
- 实际应用:
- 转账:确保 Gas 足够覆盖转账操作。
- 合约调用:估算复杂函数的 Gas 消耗,设置适当的 Gas 限制。
- 前端交互:在 DApp 中显示 Gas 费用(以 Ether 或 Gwei),提示用户。