区块链是多年来进入技术领域的最具破坏性的力量之一。开发人员现在拥有创建下一波去中心化应用程序的能力,这些应用程序将为 Web 3.0 的未来提供动力。

目录
- 第 1 部分:您的第一个 Solidity 智能合约
- 第 2 部分:Solidity 基础知识
- 第 3 部分:Solidity 数组
- 第 4 部分:Solidity 映射
- 第 5 部分:Solidity 循环和条件
- 第 6 部分:完整的 Solidity 智能合约
第 1 部分:您的第一个 Solidity 智能合约
学习 Solidity 没有比编写它更好的方法了。在本节中,您将通过使用 Solidity 编程语言创建您的第一个以太坊智能合约来了解区块链的工作原理。
在我们开始之前,让我对 Solidity 说几句话。它是一种对初学者非常友好的语言,看起来很像其他常见的编程语言,如 C++、JavaScript 和 Python。如果您已经了解这些语言中的任何一种,那么您将拥有巨大的优势。如果没有,不用担心,它很容易拿起。
Solidity 是一种静态类型语言,支持继承和外部库。它是一种成熟的编程语言,能够编写复杂的应用程序。
正如所承诺的,我们将使用 Remix IDE 在我们的 Web 浏览器中对本教程的所有内容进行编码。前往remix.ethereum.org开始。
在 Remix 内部,我们将能够开发智能合约,编译它们,并直接从我们的浏览器将它们部署到区块链。我们不需要在我们的计算机上使用文本编辑器或终端。这是很不错的!
让我们通过单击文件资源管理器中的加号 (+) 按钮开始为我们的第一个智能合约创建一个文件,如下所示:
现在让我们输入我们的智能合约文件的名称。我们会调用它Counter.sol
。
伟大的!现在我们有一个地方可以为我们的第一个智能合约编写所有源代码。
在我们继续之前,让我们确保 Remix 设置正确。如果您是第一次使用 Remix,您可能需要做 2 件事:
- 添加编译器插件,以便您可以编译智能合约。
- 添加部署插件,以便您可以将智能合约部署到虚拟区块链。
要添加编译器插件,请单击左侧边栏上的插件管理器图标。然后搜索“Solidity 编译器”。一旦它出现在搜索结果中,请单击“激活”。您应该在左侧边栏中看到新安装的插件。
接下来,对部署插件重复此步骤。搜索“Deploy”,然后激活“Deploy & run transactions”插件。
一旦你安装了这些,你就可以在你的浏览器中编译和部署你的智能合约到一个虚拟区块链!
现在让我们回到编程。在Counter.sol
我们之前创建的文件中,让我们为我们的智能合约创建基本结构,如下所示:
pragma solidity ^0.6.0;
contract Counter {
}
在这里,我们声明了我们正在使用的 Solidity 版本,在本例中为0.6.0
.
接下来,我们使用contract
关键字创建基本的智能合约,后跟它的名称Counter
。我们将在花括号内编写所有剩余的代码{ }
。
顾名思义,我们将创建一个计算数字的智能合约。例如,我们可以使用这个智能合约在游戏中记分。它将允许我们将当前分数或计数存储在区块链上,并在我们想要的时候增加它。
为此,我们将创建一个新变量,count
如下所示:
pragma solidity ^0.6.0;
contract Counter {
uint count;
}
让我解释一下这条线的作用:
-
- 它创建了一个名为 的新变量
count
。 - 计数是 a
uint
,代表“无符号整数”。基本上,它是一个非负数。例如,1, 2, 3
是 uint,但-1, -2, -3
不是因为它们前面有一个“减号”“签名”(因为它们是负数)。
- 它创建了一个名为 的新变量
该变量的值将存储在区块链上,因为它是一种特殊类型的变量,称为“状态变量”,而不是“局部变量”。
接下来,让我们创建一种获取当前计数的方法。我们将创建一个函数来做到这一点:
pragma solidity ^0.6.0;
contract Counter {
uint count;
function getCount() public view returns(uint) {
return count;
}
}
这是一个名为的基本函数getCount()
,它简单地返回count
变量的值(注意:这个值可以在这个函数内部访问,因为它是一个状态变量)。让我注意有关此功能的其他一些关键点:
- 请注意,我们已经
public
为这个函数添加了可见性,这将允许我们很快将其称为 Remix 接口(如果我们没有的话,我们将无法做到)。 - 我们添加了
view
关键字,它告诉 Solidity 这是一个只读函数。 - 最后,我们指定该函数返回一个
uint
, withreturns(uint)
。
伟大的!现在我们有了一个读取当前计数的函数。接下来,我们将创建一个更新计数的函数,如下所示:
contract Counter {
// ...
function incrementCount() public {
count = count + 1;
}
}
该函数只是从count
状态变量中读取值,将其加 1,然后更新count
区块链上的变量。
最后,让我们创建一个初始值count
:
contract Counter {
// ...
constructor() public {
count = 0;
}
}
在这里,我们将初始count
值设置0
为智能合约constructor()
函数的内部。这是一个特殊功能,只要创建智能合约(即部署到区块链),就只运行一次。
太好了,就是这样!现在您完成的 Counter 智能合约应如下所示:
pragma solidity ^0.6.0;
contract Counter {
uint count;
constructor() public {
count = 0;
}
function getCount() public view returns(uint) {
return count;
}
function incrementCount() public {
count = count + 1;
}
}
现在让我们编译智能合约以便我们可以使用它。点击我们之前安装的编译器插件,点击“Compile Contract”(如果你有问题,可以看我在上面的视频里面做这一步)。
接下来,找到我们之前安装的“Deploy & Run Transaction”插件,点击橙色的“Deploy”按钮。
🎉耶!您已成功将您的智能合约添加到 Remix 的虚拟区块链中。
现在让我们与我们新创建的 Counter 合约进行交互。找到刚刚出现的“Deployed Contracts”部分,点击 Counter 合约旁边的向下箭头,找到 2 个函数:
incrementCount
getCount
尝试单击这些按钮来观察每个函数的行为。每当您单击incrementCount
它时,计数应加 1,单击getCount
时应返回当前计数!
在结束本节之前,我想快速演示一下我们可以用更简单的方式重写 Counter 智能合约,如下所示:
pragma solidity ^0.6.0;
contract Counter {
uint public count = 0;
function incrementCount() public {
count ++;
}
}
让我解释一下:
- 首先,我们删除该
constructor()
函数,因为我们可以将初始值存储在count = 0
. - 接下来,我们删除有
getCount()
利于将public
可见性添加到count
状态变量,即uint public count = 0;
。当我们这样做时,Solidity 会自动生成一个名为的公共函数count()
,我们可以使用它在智能合约之外读取此值。 - 最后,我们重写
incrementCount()
函数以使用增量运算符++
,它只是将当前变量值加 1。
🎉 恭喜!您刚刚使用 Solidity 编程语言创建了您的第一个以太坊智能合约。你正在成为区块链大师的路上。让我们进入下一节,我们将介绍 Solidity 编程语言的更多基础知识。
第 2 部分:Solidity 基础知识
现在让我们继续学习 Solidity。在本节中,我们将介绍编程语言的更多基础知识,重点是:
- 变量
- 数据类型
- 自定义数据结构
让我们创建一个新的智能合约来展示所有这些东西。我们会MyContract
这样称呼它:
pragma solidity ^0.6.0;
contract MyContract {
}
首先,让我们谈谈变量。Solidity 中有两种主要的变量类型:局部变量和状态变量。
contract MyContract {
function getValue() public pure returns(uint) {
uint value = 1;
return value;
}
}
在这里,我创建了一个名为的函数getValue()
,其中包含一个名为value
. 这个变量允许我们存储一个数字并在getValue()
函数内部用它做一些事情,例如数学。
因为value
是局部变量,所以它有几个独特的特点:
- 它只能在内部访问获取值()功能。您无法在智能合约之外或任何其他函数内直接读取其值。
- 它不存储在区块链上。它存储在内存中。
接下来,让我们创建一个状态变量,myUint
如下所示:
contract MyContract {
uint public myUint = 1;
// ...
}
它的行为与value
上一步中的局部变量不同。让我们看看如何:
- 例如,它可以从其他功能外部访问
myFunction()
。 - 它的值可以在智能合约之外读取,因为它是声明的
public
。 - 它的值被永久写入区块链,而不是像局部变量那样在内存中。
接下来,我们来看看 Solidity 中一些常见的变量类型:
contract MyContract {
string public myString = "Hello, world!";
bytes32 public myBytes32 = "Hello, world!";
int public myInt = 1;
uint public myUint = 1;
uint256 public myUint256 = 1;
uint8 public myUint8 = 1;
address public myAddress = 0x5A0b54D5dc17e0AadC383d2db43B0a0D3E029c4c;
}
我将解释这些是什么:
- 首先,我们有一个名为 的字符串
myString
,用于存储任意文本。 - 接下来我们调用 bytes32
myBytes32
,它有点像字符串,但性能更高。 - 接下来,
myInt
是一个整数,可以是正数或负数。 - 接下来,
myUint
是一个无符号整数,这意味着它不能为负数(它不能有符号,即减号)。 - 接下来,
myUint256
是一个 256 字节的无符号整数。这用于非常大的数字。请注意,这uint
是 的简写uint256
。 - 接下来,
myUint8
是一个 8 字节的无符号整数。这用于非常小的数字。 - 最后,
myAddress
用于为外部用户或智能合约存储特定的以太坊地址。
最后,让我们看看 Solidity 如何让我们创建自己的自定义数据类型:
contract MyContract {
// ...
struct MyStruct {
uint myUint;
string myString;
}
}
在这里,我们创建了一个名为的 Struct MyStruct
,它允许我们使用我们定义的特殊数据。在这种情况下,我们指定这个结构可以保存 2 个值:一个uint
被调用的myUint
,和一个被调用的字符串myString
。
这是 Solidity 编程语言的一个强大功能,因为这意味着我们可以对任何我们想要的任意数据进行建模。例如,我们可以使用 a和创建一个Person
结构体。本系列稍后会详细介绍。name
address
我们刚刚编写的代码只是定义了一个新MyStruct
结构。要创建一个新的,我们将这样做:
contract MyContract {
// ...
struct MyStruct {
uint myUint;
string myString;
}
MyStruct public myStruct = MyStruct(1, "Hello, World!");
}
现在你完成的智能合约应该是这样的:
pragma solidity ^0.6.0;
contract MyContract {
string public myString = "Hello, world!";
bytes32 public myBytes32 = "Hello, world!";
int public myInt = 1;
uint public myUint = 1;
uint256 public myUint256 = 1;
uint8 public myUint8 = 1;
address public myAddress = 0x5A0b54D5dc17e0AadC383d2db43B0a0D3E029c4c;
struct MyStruct {
uint myUint;
string myString;
}
MyStruct public myStruct = MyStruct(1, "Hello, World!");
function getValue() public pure returns(uint) {
uint value = 1;
return value;
}
}
现在是时候测试这个智能合约的所有功能了。轮到您编译合约并将其部署到 Remix 的虚拟区块链,以便您可以与之交互。请随时重新查看上一节中的说明,或在上面的视频中跟随我。
🎉 恭喜!您刚刚介绍了 Solidity 编程语言的所有基本数据类型。在下一节中,我们将继续深入介绍您必须了解的更多基本功能。
第 3 部分:Solidity 数组
现在让我们谈谈掌握 Solidity 必须知道的基本数据结构:数组。
数组允许我们存储可以稍后访问的信息的排序列表。例如,您可以存储像 1、2、3 这样的数字列表或像“Adam”、“Bryan”和“Carl”这样的名称列表。
让我们像这样创建我们的第一个数组:
pragma solidity ^0.6.0;
contract MyContract {
// Arrays
uint[] public uintArray = [1,2,3];
}
这是一个名为 的无符号整数数组uintArray
。让我解释:
- 首先,我们用 . 声明数组和将包含在数组中的数据类型
uint[]
。在这种情况下,指定此数组将存储uint
s。我们将无法在此处存储任何其他数据类型,例如字符串。 - 接下来,我们为数组命名并使用
public uintArray
. - 最后,我们将值分配给带有 的变量
[1,2,3]
。
接下来,我们可以像这样创建一个字符串数组:
contract MyContract {
// ...
string[] public stringArray = ['apple', 'banana', 'carrot'];
}
注意,我们string[]
用来声明这个数组包含字符串类型的元素。
接下来,让我们看看如何在函数内部使用数组:
contract MyContract {
// ...
function addValue(string memory _value) public {
values.push(_value);
}
}
此函数使用该函数将新项目添加到values
数组中push()
。每当我们调用这个函数时,我们可以传入任意数字,并将其添加到数组中。
接下来,让我们创建一个确定数组大小的函数:
contract MyContract {
// ...
function valueCount() public view returns(uint) {
return values.length;
}
}
此函数调用数组,如果数组中有项目,则返回数字.length
。values
最后,让我们看看数组的一个非常有趣的属性。你知道你可以在另一个数组中创建一个数组吗?这正是二维数组。我们可以像这样创建一个:
contract MyContract {
// ...
uint[][] public array2D = [ [1,2,3], [4,5,6] ];
}
这些二维数组对于存储复杂的数据集非常有用。
就是这样!现在你完成的智能合约应该是这样的:
pragma solidity ^0.6.0;
contract MyContract {
// Arrays
uint[] public uintArray = [1,2,3];
string[] public stringArray = ['apple', 'banana', 'carrot'];
string[] public values;
uint[][] public array2D = [ [1,2,3], [4,5,6] ];
function addValue(string memory _value) public {
values.push(_value);
}
function valueCount() public view returns(uint) {
return values.length;
}
}
现在是时候测试这个智能合约的所有功能了。轮到您编译合约并将其部署到 Remix 的虚拟区块链,以便您可以与之交互。请随时重新查看上一节中的说明,或在上面的视频中跟随我。
🎉 万岁!现在您已经了解了有关 Solidity 数组的所有知识。让我们进入下一节,了解另一个重要的数据结构:映射。
第 4 部分:Solidity 映射
现在让我们谈谈另一种称为“映射”的数据结构,它可以让您存储键值对。让我来说明这是如何工作的:
上表显示了键值对关系。有一组唯一的“键”对应于唯一的值。表中的任何信息都可以通过它的键来查找。例如,如果我们要求“k1”的值,那么它将返回“AAA,BBB,CCC”。同样,新信息可以存储在新密钥下。例如,我们可以将值“ABCDEFG”添加到名为“k6”的新键中。
这正是映射在 Solidity 中的工作方式,它们让您可以在智能合约中实现类似数据库的行为。例如,您可以在键所在的位置创建映射
ID
s,并且值是任意数据。
让我们看看如何在我们的智能合约中创建一个映射,如下所示:
pragma solidity ^0.6.0;
contract MyContract {
// Mappings
mapping(uint => string) public names;
}
我们用关键字声明它mapping
,并为键和值指定数据类型。在这种情况下,映射中的每个键都是 a uint
,每个对应的值都是 a string
。
我们将此映射视为名称数据库,其中每个键是一个 id ( 1
, 2
, 3
…),每个值是一个名称 (“Adam”、“Bryan”、“Carl”…)。
现在让我们看看这个映射是如何工作的。幸运的是,我们已经有了从这个映射中读取信息的方法,因为它已经被声明了public
。Solidity 将为我们提供一个名为的函数names()
,我们可以在其中传入一个键,然后它会返回值。
为了从映射中读取数据,我们必须首先为其添加一些名称。我们将在构造函数中这样做:
pragma solidity ^0.6.0;
contract MyContract {
// Mappings
mapping(uint => string) public names;
}
contract MyContract {
// ...
constructor() public {
names[1] = "Adam";
names[2] = "Bruce";
names[3] = "Carl";
}
}
请注意,我们只需在[]
括号内传递键,然后在符号string
后分配新值。=
是的,现在尝试在 Remix 中编译和运行你的智能合约来观察这种行为。只需调用该names()
函数,并传入一个 id(1、2 或 3)。例如,names(1)
。如果您遇到困难,可以在上面的视频中观看我的操作。😃
现在让我们看看如何通过智能合约使用映射来模拟更复杂的类似数据库的行为。让我们创建一个带有映射的书籍数据库。
首先,我们将创建一个新的Book
结构来存储有关一本书的特定数据,如下所示:
contract MyContract {
// ...
struct Book {
string title;
string author;
}
}
请注意,这本书的结构跟踪本书的标题和作者。
接下来,我们将创建一个映射,我们可以在其中存储基于唯一 ID 的书籍,如下所示:
contract MyContract {
// ...
mapping(uint => Book) public books;
看,这里我们可以指定 key 是uint
id,value 是 a Book
。
现在我们将创建一个函数来添加这样的新书:
contract MyContract {
// ...
function addBook(uint _id, string memory _title, string memory _author) public {
books[_id] = Book(_title, _author);
}
此函数接受 3 个参数:新书的 ID、标题和作者。在函数内部,我们Book
从这些参数构建一个新结构,并将其添加到books
具有新 id 的映射中。
此时,我们完整的书籍代码应如下所示:
contract MyContract {
// Mappings
mapping(uint => string) public names;
mapping(uint => Book) public books;
struct Book {
string title;
string author;
}
constructor() public {
names[1] = "Adam";
names[2] = "Bruce";
names[3] = "Carl";
}
function addBook(uint _id, string memory _title, string memory _author) public {
books[_id] = Book(_title, _author);
}
}
伟大的!现在,您可以在编译并运行合约后在 Remix 界面中测试此行为。您可以在视频中跟随我,看看如何。
现在让我们更进一步,创建一个更复杂的图书数据库。目前,所有书籍都存储在数据库中,没有个人所有权的概念,即一本书有“作者”,但没有“所有者”。我们通过将所有权分配给个人以太坊地址,为个人用户创造了一种拥有书籍的方式。
为此,我们将使用这样的嵌套映射:
pragma solidity ^0.6.0;
contract MyContract {
// ...
mapping(address => mapping(uint => Book)) public myBooks;
}
此myBooks
映射是嵌套映射,这意味着它是另一个映射内部的映射。在这种情况下,myBooks
映射使用以太坊地址作为键,代表书籍的所有者,这将返回一个新的映射作为其值:一个映射到书籍的 ids(很像books
前面示例中的映射)。
我们将新书添加到此映射中,如下所示:
contract MyContract {
// ...
function addMyBook(uint _id, string memory _title, string memory _author) public {
myBooks[msg.sender][_id] = Book(_title, _author);
}
}
这里我们将新书分配给myBooks
映射,使用发送者的地址作为键(msg.sender)
伟大的!现在尝试编译此代码并在 Remix 界面中对其进行测试。此时,您的最终智能合约应如下所示:
pragma solidity ^0.6.0;
contract MyContract {
// Mappings
mapping(uint => string) public names;
mapping(uint => Book) public books;
mapping(address => mapping(uint => Book)) public myBooks;
struct Book {
string title;
string author;
}
constructor() public {
names[1] = "Adam";
names[2] = "Bruce";
names[3] = "Carl";
}
function addBook(uint _id, string memory _title, string memory _author) public {
books[_id] = Book(_title, _author);
}
function addMyBook(uint _id, string memory _title, string memory _author) public {
myBooks[msg.sender][_id] = Book(_title, _author);
}
}
第 5 部分:Solidity 条件和循环
现在让我们谈谈 Solidity 中的条件。这些允许您根据一组预定义的标准执行代码。
例如,我们可以编写看起来有点像这样的代码:
// If condition met, then do something
// If not, then do something else
让我们看看这在solidity 中是如何工作的。让我们创建一个检查数字是否为偶数的函数:
contract MyContract {
function isEvenNumber(uint _number) public view returns(bool) {
if(_number % 2 == 0) {
return true;
} else {
return false;
}
}
}
在这个函数中,我们使用if
/else
语句来检查一个数字是否为偶数。如果是偶数,则返回true
. 如果不是,则返回false
.
这里我们使用模运算符 ( <%
),它检查除法后的余数。如果它等于0
,那么它是偶数。如果不是,那就奇怪了。例如,4 除以 2 没有余数,因此模数为 0(它是偶数)。但是 5 除以 2 的余数为 1,这不是 0(很奇怪)。
这就是条件句背后的基本思想。很简单,对吧?
让我们看看条件语句的实际作用,并探索 Solidiy 中的另一个新概念:循环。为了说明这一点,让我们创建一个计算一组偶数的函数:
contract MyContract {
uint[] public numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
function countEvenNumbers() public view returns (uint) {
uint count = 0;
for(uint i = 0; i < numbers.length; i++) {
if(isEvenNumber(numbers[i])) {
count ++;
}
}
return count;
}
function isEvenNumber(uint _number) public view returns(bool) {
if(_number % 2 == 0) {
return true;
} else {
return false;
}
}
}
让我解释一下这个countEvenNumbers()
函数的作用。
numbers
首先,我们将数字 1-10 的列表存储在合约顶部调用的数组中。
接下来,在countEvenNumbers()
函数内部,我们使用循环遍历numbers
数组中的每个项目。对于每个数字,它会检查该数字是否与我们之前在语句中 创建isEvenNumber()
的函数一致。if
如果数字是偶数,那么我们将count
变量值加 1(这就是++
运算符所做的)。如果没有,那么我们什么也不做,跳过这个数字。
最后,一旦这个循环检查了数组中的每个数字,它将返回count
变量的值,该值将是数组中偶数的总数。
第 6 部分:完整的 Solidity 智能合约
现在让我们创建一个完整的智能合约来展示几个新概念。这将不同于我们之前专注于个别主题的课程。
我们将创建一个酒店房间合同,让用户预订房间并使用加密货币支付费用。我们将从创建这样的合约开始:
contract HotelRoom {
// ...
}
让我们跟踪 Hotel Room 的所有者。这是在房间被预订时将获得报酬的人:
contract HotelRoom {
address payable public owner;
}
我们将所有权分配给在constructor()
函数内部部署智能合约的帐户,如下所示:
constructor() public {
owner = msg.sender;
}
接下来,我们将创建一个预订酒店房间的函数。这个函数将处理几个职责,但首先我们会在房间被预订时向酒店老板付款。
function book() payable {
owner.transfer(msg.value);
}
请注意,我们使用该transfer()
功能将以太币发送给所有者。这里msg.value
指的是调用函数时传入的以太币数量。此外,已声明此函数payable
以便通过交易接收 Ether。
接下来,让我们跟踪一下酒店房间的入住情况。例如,我们将确定酒店房间是“空置”还是“已占用”。我们可以这样做enum
:
contract HotelRoom {
enum Statuses { Vacant, Occupied }
Statuses currentStatus;
}
在这里,我们使用选项或创建了一个enum
调用。Statuses
Vacant
Occupied
然后,我们创建一个名为currentStatus
跟踪Statuses
枚举实例及其当前值的状态变量。例如,如果当前状态是Occupied
,那么currentStatus
变量将反映这一点。
现在让我们在构造函数中设置默认状态,如下所示:
constructor() public {
owner = msg.sender;
currentStatus = Statuses.Vacant;
}
现在让我们在预订后更改酒店房间的状态:
function book() external payable {
currentStatus = Statuses.Occupied;
owner.transfer(msg.value);
}
现在让我们要求酒店房间在预订时实际上是空的:
function book() external payable {
require(currentStatus == Statuses.Vacant, "Currently occupied.");
currentStatus = Statuses.Occupied;
owner.transfer(msg.value);
}
这使用 Solidity 的内置require()
函数来检查当前状态。它是这样工作的:如果传递给require()
函数的值为真,那么函数将恢复执行。如果它是假的,那么它将停止执行并抛出一个错误。在这种情况下,如果当前状态为“空置”,则该功能将继续。如果没有,它会抛出错误"Currently occupied."
。
接下来,我们可以使用修饰符对其进行重构。这是一段特殊的可重用代码,我们可以将其添加到我们的函数中:
modifier onlyWhileVacant {
require(currentStatus == Statuses.Vacant, "Currently occupied.");
_;
}
现在我们可以从我们的函数中删除该行并添加如下修饰符:
function book() external payable onlyWhileVacant {
currentStatus = Statuses.Occupied;
owner.transfer(msg.value);
}
接下来,我们可以为酒店房间添加价格。我们将指定它花费 2 Ether。我们将使用这样的新修饰符来做到这一点:
modifier costs(uint _amount) {
require(msg.value >= _amount, "Not enough Ether provided.");
_;
}
我们可以像这样将它添加到我们的函数中:
function book() external payable onlyWhileVacant costs(2 ether) {
currentStatus = Statuses.Occupied;
owner.transfer(msg.value);
}
接下来,让我们创建一个酒店房间已预订的通知。任何人都可以订阅此通知,以便在预订后立即了解。我们可以通过这样的方式来实现event
:
contract HotelRoom {
// ...
event Occupy(address _occupant, uint _value);
}
然后,我们可以像这样在函数内部发出事件:
function book() external payable onlyWhileVacant costs(2 ether) {
currentStatus = Statuses.Occupied;
owner.transfer(msg.value);
emit Occupy(msg.sender, msg.value);
}
最后,我们想重命名这个函数来简化我们智能合约的行为。我们将receive()
像这样使用特殊功能:
receive() external payable onlyWhileVacant costs(2 ether) {
currentStatus = Statuses.Occupied;
owner.transfer(msg.value);
emit Occupy(msg.sender, msg.value);
}
这将允许智能合约接收以太坊付款。每当发生这种情况时,它将运行函数内的代码并预订酒店房间。这极大地简化了智能合约界面。无需通过特定名称调用函数并传入参数,您只需从钱包中支付智能合约,它就会为您预订酒店房间!