---
title: "Solidity 智能合约简介"
description: "从高层次理解 Ethereum 智能合约"
---

# Solidity 智能合约简介

Solidity 智能合约有多种用途，从多签钱包到去中心化交易所都有涉及。Solidity 是专为编写智能合约而构建的主要 web3 编程语言之一，因此其受欢迎程度一直在稳步上升。

本文将向你介绍 Solidity 智能合约、其工作原理以及相关特性。我们还会讲解 Solidity 智能合约的语法和数据类型，帮助你入门，以便在下一个项目中与 Solidity 智能合约进行交互。要进一步拓展你的 Solidity 知识，可以完成 Alchemy University 的[免费 Solidity 语法课程](https://www.alchemy.com/university/courses/solidity)。

## **什么是 Solidity 智能合约？**

Solidity 智能合约是[用 Solidity 编写](https://www.alchemy.com/overviews/solidity)、部署到 Ethereum 区块链、并在 [Ethereum 虚拟机](https://www.alchemy.com/overviews/what-is-the-ethereum-virtual-machine-evm)（EVM）中执行的程序。Ethereum 会在指定地址存储智能合约的代码及其数据（即状态）。

当预先设定的条件满足时，存储在区块链上的程序（即智能合约）便会执行，无需中介参与。换句话说，智能合约是控制 Ethereum 状态中账户行为的程序。

你可以使用 Solidity 或任何其他兼容 EVM 的编程语言编写智能合约。要使其兼容 EVM，必须先将其编译为字节码。

Solidity 是一种面向对象的高级语言，用于编写智能合约。用 Solidity 编写的智能合约可用于投票、众筹、盲拍、多签钱包等多种用途。

## 智能合约是如何工作的？

智能合约是存储在区块链上的程序。智能合约以代码的形式巩固各方之间的协议，并在满足预设条件时自动执行规则。这意味着智能合约所涉及的各方都能立即确定结果，无需耗费时间，也无需第三方参与。智能合约还可以自动化工作流程，在执行后触发下一个事件。

遵循 [ERC20 标准](https://www.alchemy.com/overviews/erc20-solidity)的智能合约被称为 ERC20 代币。ERC20 代币允许持有者之间转移代币。

## **Solidity 智能合约有哪些特性？**

智能合约天生具有不可变性和确定性。这意味着，一旦智能合约部署或构建到 Ethereum 上，除非其中编程了触发自毁的条件，否则它将永远存在。代码始终会按照编程时设定的条件执行。

此外，智能合约是确定性的，因为每个网络节点在给定方法相同输入的情况下，都能得出相同的结果。如果不同节点在执行智能合约时得出不同的输出，就违反了共识协议，该智能合约也将无法使用。

另外，智能合约是[无需许可的](https://www.alchemy.com/overviews/permissionless-vs-permissioned-blockchains)，这意味着任何拥有 ETH 并能访问互联网的人，都可以在 Ethereum 上部署智能合约。

最后，智能合约是可组合的。这意味着你可以将其他项目的智能合约作为构建模块用于你自己的项目。智能合约可以被视为开放的 API，用户无需自己编写智能合约就能成为 dApp 开发者，只需要知道[如何与它们交互](https://docs.soliditylang.org/en/v0.8.13/abi-spec.html)即可。

## **Solidity 智能合约语法**

虽然有许多资源可以帮助你[入门学习 Solidity](https://www.alchemy.com/overviews/learn-solidity)，但本节将概述编写 Solidity 智能合约所使用的语法。

### **1. 合约（Contracts）**

Solidity 中的合约类似于面向对象语言中的类。每个合约都可以包含状态变量、函数、函数修饰符、事件、错误、结构体类型和枚举类型的声明。本文将详细讨论构造函数、状态变量和整数变量。此外，合约还可以从其他合约继承。

还有一些特殊的合约，称为库（libraries）和[接口](https://www.alchemy.com/overviews/solidity-interface)（interfaces）。

### **2. 语义化版本控制**

Solidity 和大多数软件一样，使用语义化版本控制。这意味着除非升级主版本号，否则不会有重大变化。你可以在文档中查阅每个 Solidity 版本引入的主要破坏性变更。截至 2022 年 8 月，Solidity 最新版本为 **v0.8.16**。

### **3. 构造函数（Constructor）**

构造函数在部署（即合约创建）期间只会被调用一次。构造函数是用 constructor 关键字声明的[可选函数](https://www.alchemy.com/overviews/solidity-functions)，允许你运行合约初始化代码。

在构造函数代码执行之前，如果你在内联时初始化了状态变量，它们会被初始化为指定的值；如果没有初始化，则会被设为默认值。

构造函数运行结束后，合约的最终代码会被部署到区块链上。

如果没有构造函数，合约将使用默认构造函数。

### **4. 状态变量（State variables）**

状态变量是其值存储在合约存储中的变量。每个函数都有自己的作用域，状态变量应始终定义在该作用域之外。

状态变量遵循可见性规则。你可以使用 [_public_ 可见性关键字](https://www.alchemy.com/overviews/solidity-function-visibility)将状态变量设为公开，这会提供一个 getter 函数，供其他智能合约访问。

### **5. 修饰符（Modification）**

在 Solidity 中，[修饰符以声明的方式修改函数的语义](https://www.alchemy.com/overviews/solidity-modifier)。换句话说，修饰符会改变其所附加的函数的行为。修饰符有助于消除代码冗余，可以在一个智能合约的多个函数中重复使用，用于检查相同的条件。

### **6. 整数变量（Integer variables）**

Solidity 中有两种整数变量类型：[无符号整数（uint）](https://www.alchemy.com/overviews/solidity-uint)和有符号整数（int）。有符号整数是一种可以存储正值和负值的值数据类型。而无符号整数没有符号，因此是必须为非负数的值数据类型。

## **Solidity 数据类型**

与其他编程语言一样，Solidity 也有多种数据类型分类。不过，Solidity 的独特之处在于，多种基本数据类型可以组合形成更复杂的数据类型。本节概述 Solidity 中的主要数据类型。

### **1. 布尔值（Booleans）**

Solidity 中用 _bool_ 表示布尔值，其可能的取值为常量 true 和 false。

### **2. 字符串（Strings）**

字符串字面量可以用双引号或单引号书写。字符串还可以拆分成多个连续的部分，这在处理长字符串时会很有用。

### **3. 数字（Numbers）**

_Fixed_ 和 _ufixed_ 分别指不同大小的有符号和无符号定点数。

### **4. 字节（Bytes）**

在 Solidity 中，byte 指的是 8 位有符号整数。位以 0 或 1 的二进制值存储在内存中。在 Solidity 中，数据类型 byte 表示一个字节序列。

Solidity 中有两种字节类型：**固定大小的字节数组**和**动态大小的字节数组**。关键字 bytesX 用于定义变量，其中 X 表示字节序列的长度，范围从 1 到 32。

Solidity 中的 bytes 表示一个动态的字节数组。不过，它并不是值类型。

### **5. 地址（Address）**

地址（address）存储一个 20 字节的值，这与 Ethereum 地址的大小相同。通过地址校验和测试的十六进制字面量属于 address 类型。长度在 39 到 41 位之间、且未通过校验和测试的十六进制字面量会产生错误。你可以在整数类型前面补零，或在 bytesNN 类型后面补零，以消除该错误。

### **6. 可支付地址（Payable address）**

[可支付地址](https://www.alchemy.com/docs/solidity-payable-functions)（用 address payable 表示）与普通地址类似，但额外具有 transfer 和 send 成员。之所以需要区分，是因为普通地址可能是一个未构建为接受 Ether（ETH）的智能合约。address payable 是一个你可以向其发送 Ether 的地址。

### **7. 枚举（Enums）**

枚举，在 Solidity 中也称为枚举值，是创建用户自定义类型的一种方式，可以用来创建常量值，比如一组整型常量的名称。与基本值类型相比，枚举能使智能合约更易读、更易维护。

枚举可以与所有整数类型进行显式相互转换，但不允许隐式转换。从整数进行显式转换时，会在运行时检查该值是否落在枚举的取值范围内，否则会引发 panic 错误。因此，枚举有助于减少代码中出现的 bug。

枚举至少需要一个成员，声明时的默认值是第一个成员。枚举的成员数量不能超过 256 个。

### **8. 数组（Arrays）**

数组是[一组相同数据类型的变量](https://www.alchemy.com/overviews/solidity-arrays)，每个变量都有一个唯一的索引。数组可以具有编译期确定的固定大小，此时称为**固定数组**；也可以具有动态大小，此时称为**动态数组**。

固定大小数组在声明时就已确定预定的大小。不能使用 new 关键字来添加额外的数组成员，而必须以内联方式初始化数据变量。

动态大小数组在声明时没有预定的大小，其大小在运行时确定。

数组元素可以是任意类型，包括映射（mapping）或结构体（struct，一种用户自定义的数据结构）。

### **9. 映射（Mappings）**

在 Solidity 中，[映射的工作方式类似于其他编程语言中的哈希表](https://www.alchemy.com/overviews/solidity-mapping)或字典。映射作为一种引用类型，以键值对的形式存储数据，其中键可以是除引用类型以外的任意内置数据类型，值则可以是任意类型。

如果映射类型的状态变量是公开的，Solidity 会为你创建一个 getter 函数。

### **10. 结构体（用户自定义类型）**

结构体（struct）允许你[定义新的数据类型](https://www.alchemy.com/overviews/solidity-struct)。结构体类型可以在映射和数组内部使用，其本身也可以包含嵌套的映射和数组。不过，结构体不能包含自身类型的成员，因为结构体的大小必须是有限的。

## **什么是智能合约上下文？**

**智能合约上下文提供了关于交易运行环境的信息。**当通过一笔交易调用智能合约函数时，被调用的函数会收到一些传入的额外信息。在智能合约函数内部，你可以访问这些上下文变量。

你可以访问消息发送者（message sender）、gas 价格，以及交易中进行的各项检测。因此，你可以在代码逻辑中灵活地使用这一系列上下文变量。

## **报名参加 Ethereum 开发者训练营，了解更多内容**

在本文中，我们介绍了 Solidity 智能合约的入门基础知识，并概述了关键的数据类型和语法，以加深你的理解。报名参加 Alchemy 的[免费在线 Ethereum 开发者训练营](https://www.alchemy.com/university/courses/ethereum)，了解更多关于使用 Solidity 创建智能合约的内容。如果你是开发领域的新手，Alchemy University 的 [3 周 Ethereum 开发 JavaScript 速成课程](https://www.alchemy.com/university/courses/js)是一个很好的先修课程。
