diff --git a/README.md b/README.md index 2cf57d1..469b365 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,14 @@ # Solidity 高级程序设计 -**前置说明**: +❌❌❌ +注意: -这个教程的目标读者是那些已经熟悉 Solidity 语言基本用法的人,他们可能已经编写过一些简单的智能合约,并对 Solidity 的核心概念和语法有一定的了解。如果您已经掌握了 Solidity 的基本用法,这个教程可以帮助您填补一些知识的空白,并深入学习更高级的概念和技术。 +这这个教程的目标读者是已经熟悉 Solidity 语言基本用法的人。如果您已经掌握了 Solidity 的基本用法,这个教程可以帮助您填补知识的空白,并深入学习更高级的概念和技术。 -对于更好地阅读和理解这个教程,确实需要具备一定的 Solidity 基础知识,并且最好具备其他语言的生产级项目编码水平。 - -对于完全零基础的 Solidity 小白来说,这个教程可能并不适合作为他们初次接触。 - -如果您是一个初学者,我建议您首先学习 Solidity 的基础知识。您可以通过阅读 Solidity 的官方文档和教程来入门,掌握 Solidity 的基本语法、数据类型、控制流程等。一旦您对 Solidity 有了一定的了解,您可以尝试编写简单的智能合约,以加深对 Solidity 的理解。 +对于完全零基础的 Solidity 小白来说,这个教程可能并不适合作为他们初次接触。 如果您是一个初学者,我建议您首先学习 Solidity 的基础知识。您可以通过阅读 Solidity 的官方文档和教程来入门,掌握 Solidity 的基本语法、数据类型、控制流程等。 在学习 Solidity 的过程中,如果您遇到任何问题,都可以随时向我提问。我将尽力帮助您理解和解决问题。 - - -❌❌❌ 注意: - -这个前置条件非常重要,对于没有编程基础的人来说,阅读这个教程可能会非常困难。如果您没有掌握 Solidity 的基本用法或其他编程语言的经验,确实建议您先学习编程的基础知识和其他语言,然后再尝试深入学习 Solidity。 - -如果您在学习 Solidity 的过程中遇到困难,可以随时向我提问。我会尽力为您提供帮助和解答。同时,使用搜索引擎和参考其他教程和资源也是一个很好的学习策略,因为这样可以获取更多的知识和解决问题的方法。 +同时,使用搜索引擎和参考其他教程和资源也是一个很好的学习策略,因为这样可以获取更多的知识和解决问题的方法。 另外,关于Solidity学习资源的地址,一下是一些常用的学习资源,供大家参考: - [Solidity 官方文档](https://blog.soliditylang.org/) @@ -60,14 +51,8 @@ - 实体书籍:方便有读书习惯的人阅读。 -如果你喜欢这个项目,请给它点一个[![GitHub stars](https://img.shields.io/github/stars/anbang/professional-solidity.svg?style=social)](https://github.com/你的GitHub用户名/你的项目仓库名)(GitHub上的点赞按钮)。 - -## 贡献 - -我们欢迎并鼓励您对所有文档、源代码以及相关视频的开放与免费访问。此外,我们还提供了配套的PDF文件,同样也是免费提供的。我们欢迎您在GitHub仓库上提交您的改动,以帮助我们不断优化内容。如果您对参与教程的修改和改进感兴趣,修改GitHub源文件是最简单的方式。 - +如果你喜欢这个项目,请给它点一个[![GitHub stars](https://img.shields.io/github/stars/anbang/professional-solidity.svg?style=social)](https://github.com/anbang/professional-solidity)(GitHub上的点赞按钮)。 -## 在线阅读 - **文字版在线阅读**: - 墙内版: 阿西河网站 @@ -76,10 +61,42 @@ - [在线视频观看: Youtube](https://www.youtube.com/watch?v=ao5nkAgx7kU&list=PLdXBX6Eqe4Net43OWIZychHW0n6CX_8Ih) - [在线视频观看: Bilibili](https://www.bilibili.com/video/BV1HR4y197Ag) -## TODO -下面是给这本书做的 TODO List +## 贡献 + +我们欢迎并鼓励您对所有文档、源代码以及相关视频的开放与免费访问。此外,我们还提供了配套的PDF文件,同样也是免费提供的。我们欢迎您在GitHub仓库上提交您的改动,以帮助我们不断优化内容。如果您对参与教程的修改和改进感兴趣,修改GitHub源文件是最简单的方式。 + +## 本地运行文档 + +克隆本仓库到你的电脑 + +``` +git clone git@github.com:anbang/professional-solidity.git +``` + +**安装 Sphinx**: + +``` +yum -y install git make python3 python3-pip +pip3 install sphinx +pip3 install sphinx-autobuild +pip3 install sphinx_rtd_theme +pip3 install recommonmark +pip3 install sphinx_markdown_tables +``` + +**本地运行**:根目录执行如下命令 + +``` +# 第一种 +sphinx-autobuild docs build/html + +# 第二种 +./start.sh +``` + +## TODO List 1. ✅ 最迟不晚于 2022 年 11 月提供前 3 章的内容用来做市场测试,如果有必要的话,提供配套的视频版 2. ✅ 最迟不晚于 2023 年 1 月提供 V0.0.1 版本,并在 V0.0.1 版本完成初步架构 3. ❌ 最迟不晚于 2023 年 3 月提供 V0.0.2 版本,并在 V0.0.2 开始提供完整的 PDF 版电子书籍 @@ -88,9 +105,9 @@ 6. ❌ 如果一切顺利的话,联系出版社给印刷出来。 7. ❌ 重新录制完整的视频配套教程 -7 个步骤,目前完成了 2 个。一切都按照计划进行中。 +目前已完成 2 个步骤,进度符合计划。 -最开始写了前面 3 章内容,并在 2022 年 10 月下旬录制了配套视频教程,并分享在 Bilibili 上,有赞的有吐槽的;但是后台数据上来看这类资料在 Solidity 这个垂直语言内还是很受欢迎的。目前在 B 站搜索常见的 Solidity 关键字,我分享的视频教程几乎都是系统默认推荐的第 1 名。总播放量也在 12 月份进了前 10。并在 2022 年 12 月下旬完成了 V0.0.1 版本内容,初步完成了这本书的架构。 +**里程碑回顾-2022.12.29**:编写了前三章的内容,并于2022年10月下旬录制了配套视频教程,并在Bilibili上分享。视频教程收到了赞扬和批评,但根据后台数据显示,在Solidity这个特定领域,这类资料仍然非常受欢迎。目前在Bilibili上搜索与Solidity相关的关键字,我分享的视频教程几乎都是系统默认推荐的第一名。总播放量也在12月份进入前十名。在2022年12月下旬,完成了V0.0.1版本内容,并初步完成了本书的架构。 ## 目录 @@ -295,35 +312,17 @@ * [3️⃣ 文件结构分享](./docs/forever/22.styleguide.md#3%EF%B8%8F⃣-文件结构分享) * [4️⃣ 命名约定](./docs/forever/22.styleguide.md#4%EF%B8%8F⃣-命名约定) * [5️⃣ 更多内容](./docs/forever/22.styleguide.md#5%EF%B8%8F⃣-更多内容) - - -本来是做了 24 章的内容,但是为了以后方便印刷成纸质,所以删除了**真实案例分析**和**合约常见错误分析**这两个含有大量代码演示的章节。(比如案例分析里,光 uniswap V2/V3 和 Compound 这 3 个合约,每个印出来都是几十页的内容,太浪费篇幅了,以后单独放出来) - -## 本地运行文档 - -克隆本仓库到你的电脑 - -``` -git clone git@github.com:anbang/professional-solidity.git -``` -**安装 Sphinx**: - -``` -yum -y install git make python3 python3-pip -pip3 install sphinx -pip3 install sphinx-autobuild -pip3 install sphinx_rtd_theme -pip3 install recommonmark -pip3 install sphinx_markdown_tables -``` + +
+

+ 您的贡献将帮助我们成长 +

+ + + +
-**本地运行**:根目录执行如下命令 +本来是做了 24 章的内容,但是为了以后方便印刷成纸质,所以删除了**真实案例分析**和**合约常见错误分析**这两个含有大量代码演示的章节。(比如案例分析里,光 uniswap V2/V3 和 Compound 这 3 个合约,每个印出来都是几十页的内容,太浪费篇幅了,以后单独放出来) -``` -# 第一种 -sphinx-autobuild docs build/html -# 第二种 -./start.sh -``` diff --git a/docs/source/01.hello.md b/docs/source/01.hello.md index 3d8464c..94f3369 100644 --- a/docs/source/01.hello.md +++ b/docs/source/01.hello.md @@ -444,36 +444,45 @@ contract MyContract is SomeInterface { ### 3.contract 关键字 -第 3 行的 `contract Hello {}` 是合约的基本结构;其中 `contract` 声明了当前代码块内是一个完整的合约。而 `Hello` 是当前合约的名字,合约名称是必须的,首字母一般采用大写字母开头。 +第 3 行的 `contract Hello {}` 是合约的基本结构;其中 `contract` 声明了当前代码块内是一个完整的合约。而 `Hello` 是当前合约的名称,合约名称是必须的,首字母一般采用首字母大写的驼峰命名法。 -`contract` 代表特殊的意义,这种有特殊意义的词,在编程界里一般被称为 `保留关键字`;保留关键字是现在或者将来被用到的特殊代指,都有固定意义,所以保留关键字不能作为变量名和函数名字。 +`contract` 是具有特殊意义的保留关键字.保留关键字在编程界中知道特定的概念,具有固定的含义.因此保留关键字不能作为变量名和函数名使用。 - **总结:** - 1. contract 基本结构是 `contract ContractName {}` - 2. Solidity 合约中,合约的名字是必须的。 - 3. 合约的名称,一般约定为 大驼峰命名方式 - 4. contract 是保留关键字 - 5. 保留关键字不能作为变量名和函数名 + 1. 合约 基本结构是 `contract ContractName {}`。 + 2. Solidity合约中,合约的名称是必须的。 + 3. 合约的名称通常采用大驼峰命法。 + 4. contract 是保留关键字。 + 5. 保留关键字不能作为变量名和函数名。 - **扩展:** - 1. 合约的编写规范,参照 [合约编码规范](/forever/903.styleguide.md) - 2. 更多保留关键字,参照 [变量名命名规则](/source/03.variable.html#id11) 详细阅读。 + 1. 可参考 [合约编码规范](/forever/903.styleguide.md)了解编码规范 + 2. 了解更多保留关键字,请参考[变量名命名规则](/source/03.variable.html#id11)。 -注:一份源文件可以包含多个版本声明、多个导入声明和**多个合约声明**。 +请注意:一个源文件可以包含多个版本声明、多个导入声明和**多个合约声明**。 #### ⓵ 变量 -合约内的 `message` 叫做状态变量,状态变量是永久地存储在合约存储中的值。关于变量的更多信息,会在后续 [变量](/source/03.variable.html) 那一章详细介绍 +在合约内部,`contract` 状态变量是永久地存储在合约存储中的值。关于变量的更多信息,会在后续 [变量](/source/03.variable.html) 章节中详细介绍 +状态变量是合约中用于存储和跟踪数据的重要组成部分。它们的值可以在合约的整个生命周期内持久存在,直到被修改或删除。 + +如果您对变量有更多的疑问或想要深入了解,请继续阅读后续的 变量 章节。那里将提供更详细的信息和解释。 #### ⓶ 函数 -函数是代码的可执行单元,是一组逻辑的集合。关于变量的更多信息,会在后续 [函数](/source/04.function.html) 那一章详细介绍 +函数是代码的可执行单元,它们由一组逻辑语句组成。关于变量的更多信息将在后续的 [函数](/source/04.function.html) 章节中详细介绍。 + +函数在 Solidity 合约中起着重要的作用,用于执行特定的任务和操作。通过函数,您可以定义代码逻辑、操作状态变量、处理输入参数等。 + +如果您对函数有更多的疑问或想要深入了解,请继续阅读后续的 函数 章节。那里将提供更详细的信息和解释。 #### ⓷ this 关键字 -Solidity 中 `this` 代表合约对象本身; +Solidity 中, `this`关键字代表合约对象本身 + +一下是关于`this`的两个常见用法: -- 可以通过 `address(this)` 获取合约地址。 -- 可以通过 `this.fnName` 获取 external 函数 +- 可以通过 `address(this)` 获取合约地址。 这在某些情况下会很有用,例如需要将合约地址传递给其他合约或外部调用者。 +- 可以通过 `this.fnName` 获取 external 函数。 ``` // SPDX-License-Identifier: MIT @@ -490,9 +499,16 @@ contract Demo { } ``` +在上面的示例中,`contractAddr`函数放回了当前合约的地址,而`textExternal`函数通过`this.contractAds()`调用了`contractAds`函数并返回合约地址。 + #### ⓸ 合约地址/合约创建者地/合约调用者地址 -这三个地址概念一定要完全理解。 +在 Solidity 中,有三个地址的概念: +- `address(this)`:合约地址 表示合约在区块链上的唯一标识 +- `msg.sender`:合约调用者地址 表示当前合约的调用者地址 +- `tx.origin`:合约创建者地址 表示当前合约的创建者地址 + +在下面的示例合约中,我们可以看到对这三个地址概念的应用: ``` // SPDX-License-Identifier: MIT @@ -503,29 +519,35 @@ contract Demo { address public owner; constructor() { - // 可以用在 constructor 内获取当前合约地址 - owner = address(this); - - // 不可以在构造函数内调用函数,因为此时合约还没有完成构建好。 - // this.caller(); 相当于从外部调用 caller 方法 - // owner = this.caller(); + owner = address(this); // 在构造函数中获取当前合约地址 } function caller() external view returns (address) { - return this.contractAds(); // 内部调用 external 可见性的函数 + return this.contractAds(); // 在合约内部调用 external 可见性的函数 } function contractAds() external view returns (address) { - return address(this); + return address(this); // 返回当前合约的地址 } } ``` +在上面的示例中,我们通过`address(this)`获取了当前合约的地址,并将其赋值给了`owner`变量。在`caller`函数中,我们通过`this.contractAds()`调用了`contractAds`函数并返回合约地址。 + +请注意:在构造函数中调用函数是不推荐的做法,因为在构造函数执行期间,合约可能还没有完全构建完毕.所以,构造函数内部调用函数时需要特别小心。 + + #### ⓹ 合约属性:type 关键字 -- `type(C).name`:获得合约名 -- `type(C).creationCode`:获得包含创建合约字节码的内存字节数组。它可以在内联汇编中构建自定义创建例程,尤其是使用 create2 操作码。 不能在合约本身或派生的合约访问此属性。 因为会引起循环引用。 -- `type(C).runtimeCode`:获得合约的运行时字节码的内存字节数组。这是通常由 C 的构造函数部署的代码。 如果 C 有一个使用内联汇编的构造函数,那么可能与实际部署的字节码不同。 还要注意库在部署时修改其运行时字节码以防范定期调用(guard against regular calls)。 与 .creationCode 有相同的限制,不能在合约本身或派生的合约访问此属性。 因为会引起循环引用。 +使用 `type` 关键字可以获取合约的属性,包括合约名、创建字节码和运行时字节码。 + +以下是关于`type`关键字的属性的详细说明: + +- `type(C).name`:获取合约名。在示例合约中,使用`type(C).name`返回合约`Hello`的名称 +- `type(C).creationCode`:获取包含创建合约字节码的内存字节数组。该属性主要是用于内联汇编,在构建自定义创建过程时特别有用。需要注意的是,合约本身或派生合约无法访问此属性,因为可能导致循环利用。 +- `type(C).runtimeCode`:获取合约的运行时字节码的内存字节数组。这是通常有合约的构造函数部署的代码。如果合约的构造函数使用了内联汇编,则实际部署的字节码可能与此属性返回的字节码不同。还需要注意的是,库合约在部署时会修改其运行时字节码以防范定期调用。合约本身或派生合约无法访问此属性。 + +在下面的示例合约中,我们演示了如何使用:`type`关键字获取合约的属性: ``` // SPDX-License-Identifier: MIT @@ -537,57 +559,71 @@ contract Hello { contract Demo { function name() external pure returns (string memory) { - return type(Hello).name; + return type(Hello).name; // 获取合约名 } function creationCode() external pure returns (bytes memory) { - return type(Hello).creationCode; + return type(Hello).creationCode; // 获取创建字节码 } function runtimeCode() external pure returns (bytes memory) { - return type(Hello).runtimeCode; + return type(Hello).runtimeCode; // 获取运行时字节码 } } ``` -除了上面介绍的版权声明,版本限制,contract 外,合约文件还包括 `import`, `interface`,`library`,一起展开介绍下 +除了上面介绍的版权声明,版本限制和`contract`关键字外,合约文件还包括 `import`, `interface`,`library`等内容。这些内容在后续章节中将详细展开介绍。 ### 4.import 导入声明 -功能:从其他文件内倒入需要的变量或者函数。 +导入声明的功能是从其他文件中导入所需的变量和函数。 #### ⓵ 导入方式 -既可以导入**本地文件**,也可以导入 **url**(网络上的 ipfs,http 或者 git 文件) +可以导入**本地文件**,也可以导入 **url**(如 IPFS、HTTP 或 Git 文件)。 -1. **导入所有的全局标志** `import "filename";` 到当前全局范围 - 1. 导入本地文件:`import "./ERC20.sol";`,其中`./`表示当前目录,查找路径参考 +1. **导入所有的全局标志** `import "filename";` 将所有全局标识符导入到当前的全局范围内。 + 1. 导入本地文件:`import "./ERC20.sol";`,其中`./`表示当前目录,具体的查找路径请参考具体规范 2. 导入网络文件:`import "https://github.com/aaa/.../tools.sol";` 3. 导入本地 NPM 库: 1. `$ npm install @openzeppelin/contracts` 2. `import "@openzeppelin/contracts/token/ERC721/ERC721.sol";` -2. **导入所有的全局标志,并创建新的全局符号** +2. **导入所有的全局标志并创建新的全局符号** 1. 方式一: `import * as symbolName from "filename";` 2. 方式二: `import "filename" as symbolName;` -3. 按需导入,按需修改名称 +3. 按需导入并修改名称 1. `import {symbol1 as aliasName, symbol2} from "filename";` -不推荐导入变量标示名到当前全局范围的方式,因为不可控,容易污染当前的命名空间。如果全局导入,推荐使用 `import "filename" as symbolName;` +不推荐将变量标识符导入到当前全局范围,因为这样做会导致命名空间的污染和不可控性。如果要全局导入,建议使用 `import "filename" as symbolName;`的方式。 -注:一份源文件可以包含多个版本声明、多**个导入声明**和多个合约声明。 +注意:一份源文件可以包含多个版本声明、多**个导入声明**和多个合约声明。 #### ⓶ 导入时候的本地路径 -上文中的 filename 总是会按路径来处理,以 `/` 作为目录分割符、以 `.` 标示当前目录、以 `..` 表示父目录。 当 `.` 或 `..` 后面跟随的字符是 `/` 时,它们才能被当做当前目录或父目录。 只有路径以当前目录 `.` 或父目录 `..` 开头时,才能被视为相对路径。 +在Solidity中,导入语句中`filename`总是按照路径处理,以`/`作为目录分隔符。这意味着可以使用相对路径或绝对路径来指定要导入文件的位置。 -用 `import "./x.sol" as x;` 语句导入当前源文件同目录下的文件 `x.sol` 。 如果用`import "x.sol" as x;` 代替,可能会引入不同的文件(在全局 `include directory` 中)。 +当使用相对路径时,可以使用`.`来表示当前目录,`..`表示父目录。只有路径以`.`或`..`开头,并且后面跟随的字符是`/`,才能被解析为当前目录或父目录。 -最终导入哪个文件取决于编译器(见下文)到底是怎样解析路径的。 通常,目录层次不必严格映射到本地文件系统, 它也可以映射到能通过诸如 ipfs,http 或者 git 发现的资源。 +例如,假设有以下文件结构: +``` +├── contracts +│ ├── main.sol +│ └── utils.sol +└── README.md +``` + +在`main.sol`,要导入同一目录下的`utils.sol`文件,可以使用相对路径`import "./utils.sol" as utils;`。这会告诉编译器在当前目录中查找`utils.sol`文件。 + +如果要导入`contracts`目录下的`utils.sol`文件,可以使用相对路径`import "../contracts/utils.sol" as utils;`。这里的`..`表示回到父目录,然后在进入`contracts`。 + +如果要使用绝对路径来导入文件,例如`import "/contracts/utils.sol" as utils;`,那么编译器将会在全局的根目录中查找`contracts`目录。这个全局的目录可以根据具体的Solidity编译器和设置而定。 + +总体来说,根据路径的不同,编译器会解析不同的文件。因此,在编写导入语句时,需要根据文件的实际位置,选择正确的路径表示 方式,以确保成功导入所需的文件。 ### 5.interface: 接口 #### ⓵ 接口使用案例 -在下面的例子中,定义了 cat 合约以及 dog 合约。他们都有 `eat` 方法.以此他们都可以被上面的 `animalEat` 接口所接收。 +在下面的例子中,定义了 Cat 合约以及 Dog 合约。它们都有 `eat` 方法.因此它们都可以被上面的 `AnimalEat` 接口所接收。 ``` // SPDX-License-Identifier: MIT @@ -633,9 +669,9 @@ contract Animal { #### ⓶ `type(I).interfaceId` -返回接口`I` 的 `bytes4` 类型的接口 ID,接口 ID 参考: [EIP-165](https://learnblockchain.cn/docs/eips/eip-165.html) 定义的, 接口 ID 被定义为 XOR (异或) 接口内所有的函数的函数选择器(除继承的函数。 +通过`type(I).interfaceId`可以返回接口`I` 的 `bytes4` 类型的接口ID,接口 ID 参考: [EIP-165](https://learnblockchain.cn/docs/eips/eip-165.html) , 接口ID 被定义为接口内所有函数的函数选择器的 XOR(异或)结果(除了继承的函数)。 -上面的代码种,可以增加如下的函数来查看 `interfaceId`; +上面的代码种,可以增加以下的函数来查看 `interfaceId`; ``` contract Animal { @@ -646,11 +682,13 @@ contract Animal { } ``` -更多内容在 [interface:接口](/source/13.interface.html) 那一章详细介绍。 +更多关于接口的内容可以在 [interface:接口](/source/13.interface.html) 这一章节中详细介绍。 ### 6.library:库合约 -库与合约类似,但它的目的是在一个指定的地址,且仅部署一次,然后通过 EVM 的特性来复用代码。 +库合约类似与普通合约,但是它们不能被单独部署,也不能被继承。库合约可以被其他合约使用,库合约中的函数可以被其他合约调用。 + +以下是一个库合约的示例: ``` library Set { @@ -659,14 +697,18 @@ library Set { } } ``` +其他合约可以通过直接调用库合约的名称来调用起函数,例如:`Set.test()`。 -其他合约调用库文件的内容直接通过库文件名.方法名例如:`Set.test()`。 +库合约的主要特点包括: +- 库合约只能包含库函数,不能包含状态变量。 +- 库合约的函数可以通过访问库中定义的结构体和存储变量。 +- 库函数在被调用时,会将其东盎用上下文的数据作为参数传递,而不需要显式指定引用。 -更多内容在 [Library:库](/source/14.library.html) 那一章详细介绍。 +库合约的主要优势是可以在多个合约中共享和重用代码逻辑,减少重复开发和部署的成本。 -## 5️⃣ 全局的以太币单位 +更多内容在 [Library:库](/source/14.library.html)章节有详细介绍。 -现实生活中经常会听说,提现 1.5 个以太币,或者某笔交易手续费花了 0.02 个以太坊等。这些带有小数点的数字是日常交流使用的。但是在合约内,却没有这种小数概念的货币金额。比如 1 个 ETH 的金额是 `10**18 wei`。 +## 5️⃣ 全局的以太币单位 ### 本节配套视频 @@ -675,13 +717,14 @@ library Set { ### 1.基础单位 -为了方便合约开发者操作,也提供如下这种便捷的换算方式。 - -以太币单位之间的换算就是在数字后边加上 `wei`、 `gwei`、 `ether` 来实现的,如果后面没有单位,缺省为 `wei`。例如 `1 ether == 1e18` 的逻辑判断值为 true。 +在以太坊合约中没有直接支持小数点的货币金额概念,而是使用基本单位来表示金额。为了方便,以太坊提供了一种编写的换算方式,可以在数值后面加上`wei`、`gwei`、`ether`等单位来表示不同的金额。 - `1 ether = 1 * 10^18 wei` - `1 ether = 1 * 10^9 gwei` +例如,`1 ether == 1e18`。 + +下面是一个示例合约,演示了基本单位的使用方法。 ``` // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; @@ -696,6 +739,10 @@ contract Demo { } ``` +在上述示例中,`test` 函数返回了三个布尔值,分别表示 `1 wei`、`1 gwei`、`1 ether` 是否等于 `1`。 + +通过这种方式,合约开发者可以方便的进行金额的计算,而不需要关心具体的单位。请注意使用适当的Solidity版本进行编译。 + ### 2.变量使用以太币单位 **注意: 这些后缀不能直接用在变量后边**。如果想用以太币单位来计算输入参数,你可以使用乘法来转换: `amountEth * 1 ether` @@ -728,17 +775,15 @@ contract Demo { ## 6️⃣ 接收 ETH -三个关键字 - -- payable - - 使用 payable 标记的**函数**可以用于发送和接收 Eth。 - - 使用 payable 标记的 **地址变量**,允许发送和接收 Eth。 -- fallback - - 一个合约可以最多有一个回退函数。 -- receive - - 一个合约最多有一个 `receive` 函数 +在以太坊智能合约中有几种方式是可以接受以太币(ETH). +- payable函数: + - 使用 payable 标记的**函数**可以用于发送和接收 Eth。当其他用户调用这个函数时,他们可以在交易中附带一定数量的以太币。 +- fallback函数: + - fallback 函数是一种特殊的函数,当合约接收到以太币时,会自动调用 fallback 函数。 +- receive函数: + - 个合约最多只能有一个 receive 函数。该函数没有参数,且没有函数名。当合约接收到以太币时,会自动调用该函数。您可以在 receive 函数中处理接收到的以太币 -fallback 和 receive 不是普通函数,而是新的函数类型,有特别的含义,它们前面不需要加 `function` 这个关键字。加上 `function` 之后,它们就变成了一般的函数,只能按一般函数来去调用。同时 `receive` 和 `fallback` 需要注意 gas 消耗。 +fallback 和 receive 是特殊类型的函数,不需要在签名加 `function` 这个关键字,他们具有特殊的含义和行为。相反,如果在它们前面加上 `function` 关键字,它们将被视为普通的函数,只能按普通函数的方式调用。同时 `receive` 和 `fallback` 需要注意 gas 消耗。 本节介绍的是合约如何接收 ETH,至于合约如何发送 ETH,请阅读 [两种形式的地址](/source/02.type-of-data.html#id24) 这一节。 @@ -758,23 +803,25 @@ fallback 和 receive 不是普通函数,而是新的函数类型,有特别 pragma solidity ^0.8.17; contract Payable { - // payable 标记函数 + // 使用 payable 标记的函数可以接受以太币 function deposit1() external payable {} function deposit2() external {} - // payable 标记地址 + // 使用 payable 标记的地址变量允许发送和接受以太币 function withdraw() external { payable(msg.sender).transfer(address(this).balance); } - // 通过 balance 属性,来查看余额。 + // 通过 balance 属性,来查看合约的余额。 function getBalance() external view returns (uint256) { return address(this).balance; } } ``` + + 可以使用 deposit 存款,但是如果使用 calldata 转账,则会失败,报错 _In order to receive Ether transfer the contract should have either 'receive' or payable 'fallback' function_ ### 2.fallback