跳转到主要内容
Change page

智能合约剖析

智能合约是在以太坊上的一个地址运行的程序。它们由数据和函数组成,可以在收到交易后执行。以下是智能合约的组成概述。

前提条件

请确保你已经阅读过关于智能合约的内容。本文档假设你已经熟悉 JavaScript 或 Python 等编程语言。

数据

任何合约数据都必须分配到一个位置:storagememory。在智能合约中修改存储的成本很高,因此你需要考虑数据应该存放在哪里。

存储

持久性数据被称为存储,由状态变量表示。这些值永久存储在区块链上。你需要声明类型,以便合约在编译时能够跟踪它在区块链上需要多少存储空间。

// Solidity 示例
contract SimpleStorage {
    uint storedData; // 状态变量
    // ...
}
# Vyper 示例
storedData: int128

如果你已经使用过面向对象语言进行编程,你可能会对大多数类型很熟悉。然而,如果你刚接触以太坊开发,address 对你来说应该是个新概念。

address 类型可以保存一个以太坊地址,相当于 20 字节或 160 位。它以十六进制表示法返回,并带有前导 0x。

其他类型包括:

  • 布尔值
  • 整数
  • 定点数
  • 固定大小的字节数组
  • 动态大小的字节数组
  • 有理数和整数常量
  • 字符串常量
  • 十六进制常量
  • 枚举

更多解释,请查看文档:

内存

仅在合约函数执行期间存储的值称为内存变量。由于这些变量不会永久存储在区块链上,因此使用它们的成本要低得多。

Solidity 文档 (opens in a new tab)中了解更多关于以太坊虚拟机 (EVM) 如何存储数据(存储、内存和栈)的信息。

环境变量

除了你在合约中定义的变量外,还有一些特殊的全局变量。它们主要用于提供有关区块链或当前交易的信息。

示例:

属性状态变量描述
block.timestampuint256当前区块时段的时间戳
msg.senderaddress消息的发送者(当前调用)

函数

简单来说,函数可以获取信息或设置信息,以响应传入的交易。

函数调用有两种类型:

  • internal —— 这些不会创建 EVM 调用
    • 内部函数和状态变量只能在内部访问(即从当前合约或派生自它的合约内部)
  • external —— 这些会创建 EVM 调用
    • 外部函数是合约接口的一部分,这意味着它们可以从其他合约和通过交易被调用。外部函数 f 不能在内部调用(即 f() 不起作用,但 this.f() 起作用)。

它们也可以是 publicprivate

  • public 函数可以在合约内部调用,也可以通过消息在外部调用
  • private 函数仅在定义它们的合约中可见,在派生合约中不可见

函数和状态变量都可以设为公开 (public) 或私有 (private)

以下是用于更新合约上状态变量的函数:

// Solidity 示例
function update_name(string value) public {
    dapp_name = value;
}
  • 类型为 string 的参数 value 被传递到函数中:update_name
  • 它被声明为 public,意味着任何人都可以访问它
  • 它没有被声明为 view,因此它可以修改合约状态

视图函数

这些函数承诺不修改合约数据的状态。常见的例子是“getter”函数——例如,你可能会使用它来获取用户的余额。

// Solidity 示例
function balanceOf(address _owner) public view returns (uint256 _balance) {
    return ownerPizzaCount[_owner];
}
dappName: public(string)

@view
@public
def readName() -> string:
  return dappName

哪些操作被视为修改状态:

  1. 写入状态变量。
  2. 触发事件 (opens in a new tab)
  3. 创建其他合约 (opens in a new tab)
  4. 使用 selfdestruct
  5. 通过调用发送以太币。
  6. 调用任何未标记为 viewpure 的函数。
  7. 使用底层调用。
  8. 使用包含某些操作码的内联汇编。

构造函数

constructor 函数仅在合约首次部署时执行一次。与许多基于类的编程语言中的 constructor 一样,这些函数通常将状态变量初始化为其指定的值。

# Vyper 示例

@external
def __init__(_beneficiary: address, _bidding_time: uint256):
    self.beneficiary = _beneficiary
    self.auctionStart = block.timestamp
    self.auctionEnd = self.auctionStart + _bidding_time

内置函数

除了你在合约中定义的变量和函数外,还有一些特殊的内置函数。最明显的例子是:

  • address.send() —— Solidity
  • send(address) —— Vyper

这些函数允许合约向其他账户发送 ETH。

编写函数

你的函数需要:

  • 参数变量和类型(如果它接受参数)
  • 内部/外部 (internal/external) 声明
  • pure/view/payable 声明
  • 返回类型(如果它返回值)

一个完整的合约可能看起来像这样。在这里,constructor 函数为 dapp_name 变量提供了一个初始值。

事件和日志

事件使你的智能合约能够与前端或其他订阅应用程序进行通信。一旦交易被验证并添加到区块中,智能合约就可以触发事件并记录日志信息,然后前端可以处理和利用这些信息。

带注释的示例

这些是一些用 Solidity 编写的示例。如果你想尝试这些代码,可以在 Remix (opens in a new tab) 中与它们进行交互。

Hello world

代币

独特的数字资产

延伸阅读

查看 Solidity 和 Vyper 的文档,以获取更完整的智能合约概述: