EthBnb构建一个简单的Solidity智能合约

我一直对以太坊着迷,尤其是智能合约。自我执行,不可改变和无信任问题合约的想法是改变游戏规则的。经过多年的观望,我终于决定试试。现在,我不是软件开发人员。我是一名高级营销人员,我从未接受任何正式的CS教育。我一直在营销和技术的十字路口工作,我有一些编写代码作为业余爱好者的经验,但学习一门全新的语言对我来说可不是说说那么容易。

幸运的是,Solidity很简单。它的语法非常清晰,几乎就像普通英语一样。我只花了一天的时间来编写EthBnb,如果你过去曾接触过计算机代码,你就会很好地理解它是如何工作的。

在本文中我不会介绍以太坊(区块链,交易,合约,gas等)或Solidity语法的基础知识;但如果你想开始编写自己的合约,我可以指出你正确的方向。BlockGeeks和Eat The Blocks都是优秀的资源。我也不会谈论如何使用Remix IDE。

产品规格

我想创建一个简单的智能合约来复制AirBnb的核心功能,但对于单个房东:

  • 查询公寓是否可以出租,并让房东改变可用性。
  • 看看谁是当前公寓的占用者(如果被占用)。
  • 让客人租一套可用的公寓,并用以太币支付房东。

就是这样,不多也不少。

EthBnb没有附带前端。这个练习的目的是建立智能合约本身,并且——诚实地说,我在构建前端时非常糟糕。如果你喜欢的话,你仍然可以在Remix中玩合约,甚至在主网上玩合约。

我们开始编码吧!

我几乎会逐行细分合约,但如果你不耐烦并希望看到整个代码,请查看GitHub repo

让我们从在Remix中创建一个新文件开始,并将其命名为EthBnb.sol

初始化合约

首先,让我们指定Solidity编译器版本,并声明我们的合约:

1
2
3
pragma solidity ^0.5.0;
// Most tutorials use version 0.4.something but we go big here :)
contract EthBnb {

然后让我们声明我们的全局变量。

首先是地址:

1
2
address payable landlordAddress;
address payable tenantAddress;

我们需要将地址定义为应付地址,因为我们需要发送资金(如果我们退款,则向房东发送资金,也向租户发送资金)。

让我们定义一个代表公寓的Flat结构。 我们将存储价格(wei,最小的以太币单位),当前占用者的以太坊地址,以及该公寓是否可供出租:

1
2
3
4
5
struct Flat {
uint256 priceInWei;
address currentOccupant;
bool flatIsAvailable;
}

我们假设房东使用这个合约有八个公寓出租。我知道,这是一个非常懒惰的假设。我会更好地使合约变得灵活,让房东添加和删除公寓等等——但这将适用于本教程。让我们宣布一个由八个公寓组成的数组:

1
Flat[8] flatDB;

接下来,我们将介绍一个修饰符modifiermodifier是Solidity中优雅的解决方案,可以动态地为函数添加代码。EthBnb中的一些功能只能供房东使用;所以我们检查请求是否来自房东的地址(使用require),如果是,我们继续执行:

1
2
3
4
modifier landlordOnly() {
require(msg.sender == landlordAddress);
_;
}

最后,我们需要添加一个构造函数constructor。这只在合约部署时运行一次。让我们将合约的地址存储为“房东”,并在我们的数组中填入一些测试数据(我们将所有单位设置为“可用”,并将公寓价格设置为0.1或0.2以太)。这不是必要的,但给了我们一些可以玩的东西:

1
2
3
4
5
6
7
8
9
10
11
constructor() public {
landlordAddress = msg.sender;
for (uint i=0; i<8; i++) {
flatDB[i].flatIsAvailable = true;
if (i % 2 == 0) {
flatDB[i].priceInWei = 0.1 ether;
} else {
flatDB[i].priceInWei = 0.2 ether;
}
}
}

写getters

我们在合约中需要三个getters:一个用于检查是否有公寓,一个用于检查公寓的价格,另一个用于检查当前租用公寓的人:

1
2
3
4
5
6
7
8
9
function getFlatAvailability(uint _flat) view public returns(bool) {
return flatDB[_flat].flatIsAvailable;
}
function getPriceOfFlat(uint _flat) view public returns(uint256) {
return flatDB[_flat].priceInWei;
}
function getCurrentOccupant(uint _flat) view public returns(address) {
return flatDB[_flat].currentOccupant;
}

如果没有人租用这个地方,最后一个将返回零地址(0x0)。

写setters

首先,让我们快速写下两个需要的setters,一个改变一个单位的可用性,另一个改变价格:

1
2
3
4
5
6
7
8
9
10
11
12
function setFlatAvailability(uint8 _flat, bool _newAvailability) landlordOnly public
{
flatDB[_flat].flatIsAvailable = _newAvailability;
if (_newAvailability) {
flatDB[_flat].currentOccupant = address(0);
}
}

function setPriceOfFlat(uint8 _flat, uint256 _priceInWei) landlordOnly public
{
flatDB[_flat].priceInWei = _priceInWei;
}

请注意,我们将landlordOnly修饰符添加到这些函数中,因此只有房东可以调用它们。

如果将公寓设置为“可用”,我们不要忘记移除当前占用者。请注意,Solidity中没有“null”,并且地址不能设置为0(这是类型不匹配),因此我们需要使用address(0)

价格需要用wei设定。与wei合作并不容易,但幸运的是,available online提供了多种以太网单位转换工具。

我们就快好了!除了我们需要编写实际租用地点的功能。我会从GitHub中嵌入这个,因为有些行很长:

1
2
3
4
5
6
7
8
9
10
11
12
13
function rentAFlat(uint8 _flat) public payable returns(uint256) {
tenantAddress = msg.sender;
if (msg.value % flatDB[_flat].priceInWei == 0 && msg.value > 0 && flatDB[_flat].flatIsAvailable == true) {
uint256 numberOfNightsPaid = msg.value / flatDB[_flat].priceInWei;
flatDB[_flat].flatIsAvailable = false;
flatDB[_flat].currentOccupant = tenantAddress;
landlordAddress.transfer(msg.value);
return numberOfNightsPaid;
} else {
tenantAddress.transfer(msg.value);
return 0;
}
}

让我们逐行来看看!

首先,我们将功能定义为应付款payable,因为它将接收转账。它将返回一个整数,即预订的夜数:

1
function rentAFlat(uint8 _flat) public payable returns(uint256) {

我们将调用函数的地址存储在tenantAddress变量中:

1
tenantAddress = msg.sender;

如果房东想要租房,那也没关系!生活可以奇怪:)

然后我们检查一些条件:

  • 如果租户发送的金额合适。我们不想租用这个地方1.75晚,所以我们使用模数%运算符。
  • 但是如果金额为0,则%运算符将评估为true,因此我们也需要检查它。
  • 如果这个地方确实可用。

这就是我们的if语句的样子:

1
if (msg.value % flatDB[_flat].priceInWei == 0 && msg.value > 0 && flatDB[_flat].flatIsAvailable == true) {

我们检查客人刚刚支付了多少晚(现在我们知道它是一个整数):

1
uint256 numberOfNightsPaid = msg.value / flatDB[_flat].priceInWei;

我们将公寓可用性设置为false:

1
flatDB[_flat].flatIsAvailable = false;

存储客人的以太坊地址:

1
flatDB[_flat].currentOccupant = tenantAddress;

将租赁费转让给房东:

1
landlordAddress.transfer(msg.value);

最后,返回支付的夜数。你可以在应用程序本身中使用此数据:

1
return numberOfNightsPaid;

唷!现在让我们看看如果事情出了状况怎么办,客人发错了金额,或者公寓不可用,会发生什么。首先我们将Ether返还(减去gas):

1
2
} else {
tenantAddress.transfer(msg.value);

然后我们返回零,所以应用程序知道租赁没有通过:

1
return 0;

而且……就是这样! 我们有一个智能合约!

它肯定缺少一些部分。跟踪天数并自动“移除”租户会很好,但这在Solidity中是不切实际的。应用程序本身(以NodeJS,Python或Go编码)应该处理逻辑,并在时间调用setFlatAvailability函数,可能使用像Ethereum Alarm Clock这样的东西。

我希望你发现本教程很有帮助。合约在线并在Rinkeby testnet上部署,如果你想玩它在这里

你可以在我的GitHub上找到完整代码。它被评论为死亡,因此希望很容易遵循。如果你认为应该采取不同的方式,请随意发送拉取请求。

快乐的编码!

======================================================================

分享一些以太坊、比特币、EOS等区块链相关的交互式在线编程实战教程:

  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • ERC721以太坊通证实战,课程以一个数字艺术品创作与分享DApp的实战开发为主线,深入讲解以太坊非同质化通证的概念、标准与开发方案。内容包含ERC-721标准的自主实现,讲解OpenZeppelin合约代码库二次开发,实战项目采用Truffle,IPFS,实现了通证以及去中心化的通证交易所。
  • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • c#比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在C#代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是C#工程师不可多得的比特币开发学习课程。
  • EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
  • 深入浅出玩转EOS钱包开发,本课程以手机EOS钱包的完整开发过程为主线,深入学习EOS区块链应用开发,课程内容即涵盖账户、计算资源、智能合约、动作与交易等EOS区块链的核心概念,同时也讲解如何使用eosjs和eosjs-ecc开发包访问EOS区块链,以及如何在React前端应用中集成对EOS区块链的支持。课程内容深入浅出,非常适合前端工程师深入学习EOS区块链应用开发。
  • tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。

汇智网原创翻译,转载请标明出处。这里是EthBnb构建一个简单的Solidity智能合约