EthBnb構建一個簡單的Solidity智慧合約
我一直對以太坊著迷,尤其是智慧合約。自我執行,不可改變和無信任問題合約的想法是改變遊戲規則的。經過多年的觀望,我終於決定試試。現在,我不是軟體開發人員。我是一名高階營銷人員,我從未接受任何正式的CS教育。我一直在營銷和技術的十字路口工作,我有一些編寫程式碼作為業餘愛好者的經驗,但學習一門全新的語言對我來說可不是說說那麼容易。
幸運的是,Solidity很簡單。它的語法非常清晰,幾乎就像普通英語一樣。我只花了一天的時間來編寫EthBnb,如果你過去曾接觸過計算機程式碼,你就會很好地理解它是如何工作的。
在本文中我不會介紹以太坊(區塊鏈,交易,合約,gas等)或Solidity語法的基礎知識;但如果你想開始編寫自己的合約,我可以指出你正確的方向。BlockGeeks和Eat The Blocks都是優秀的資源。我也不會談論如何使用Remix IDE。
產品規格
我想建立一個簡單的智慧合約來複制AirBnb的核心功能,但對於單個房東:
- 查詢公寓是否可以出租,並讓房東改變可用性。
- 看看誰是當前公寓的佔用者(如果被佔用)。
- 讓客人租一套可用的公寓,並用以太幣支付房東。
就是這樣,不多也不少。
EthBnb沒有附帶前端。這個練習的目的是建立智慧合約本身,並且——誠實地說,我在構建前端時非常糟糕。如果你喜歡的話,你仍然可以在Remix中玩合約,甚至在主網上玩合約。
我們開始編碼吧!
我幾乎會逐行細分合約,但如果你不耐煩並希望看到整個程式碼,請檢視GitHub repo 。
讓我們從在Remix中建立一個新檔案開始,並將其命名為EthBnb.sol
。
初始化合約
首先,讓我們指定Solidity編譯器版本,並宣告我們的合約:
pragma solidity ^0.5.0; // Most tutorials use version 0.4.something but we go big here :) contract EthBnb {
然後讓我們宣告我們的全域性變數。
首先是地址:
address payable landlordAddress; address payable tenantAddress;
我們需要將地址定義為應付地址,因為我們需要傳送資金(如果我們退款,則向房東傳送資金,也向租戶傳送資金)。
讓我們定義一個代表公寓的Flat結構。 我們將儲存價格(wei,最小的以太幣單位),當前佔用者的以太坊地址,以及該公寓是否可供出租:
struct Flat { uint256 priceInWei; address currentOccupant; bool flatIsAvailable; }
我們假設房東使用這個合約有八個公寓出租。我知道,這是一個非常懶惰的假設。我會更好地使合約變得靈活,讓房東新增和刪除公寓等等——但這將適用於本教程。讓我們宣佈一個由八個公寓組成的陣列:
Flat[8] flatDB;
接下來,我們將介紹一個修飾符modifier
。modifier
是Solidity中優雅的解決方案,可以動態地為函式新增程式碼。EthBnb中的一些功能只能供房東使用;所以我們檢查請求是否來自房東的地址(使用require
),如果是,我們繼續執行:
modifier landlordOnly() { require(msg.sender == landlordAddress); _; }
最後,我們需要新增一個建構函式constructor
。這隻在合約部署時執行一次。讓我們將合約的地址儲存為“房東”,並在我們的陣列中填入一些測試資料(我們將所有單位設定為“可用”,並將公寓價格設定為0.1或0.2以太)。這不是必要的,但給了我們一些可以玩的東西:
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:一個用於檢查是否有公寓,一個用於檢查公寓的價格,另一個用於檢查當前租用公寓的人:
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,一個改變一個單位的可用性,另一個改變價格:
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中嵌入這個,因為有些行很長:
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
,因為它將接收轉賬。它將返回一個整數,即預訂的夜數:
function rentAFlat(uint8 _flat) public payable returns(uint256) {
我們將呼叫函式的地址儲存在tenantAddress
變數中:
tenantAddress = msg.sender;
如果房東想要租房,那也沒關係!生活可以奇怪:)
然後我們檢查一些條件:
- 如果租戶傳送的金額合適。我們不想租用這個地方1.75晚,所以我們使用模數%運算子。
- 但是如果金額為0,則%運算子將評估為true,因此我們也需要檢查它。
- 如果這個地方確實可用。
這就是我們的if語句的樣子:
if (msg.value % flatDB[_flat].priceInWei == 0 && msg.value > 0 && flatDB[_flat].flatIsAvailable == true) {
我們檢查客人剛剛支付了多少晚(現在我們知道它是一個整數):
uint256 numberOfNightsPaid = msg.value / flatDB[_flat].priceInWei;
我們將公寓可用性設定為false:
flatDB[_flat].flatIsAvailable = false;
儲存客人的以太坊地址:
flatDB[_flat].currentOccupant = tenantAddress;
將租賃費轉讓給房東:
landlordAddress.transfer(msg.value);
最後,返回支付的夜數。你可以在應用程式本身中使用此資料:
return numberOfNightsPaid;
唷!現在讓我們看看如果事情出了狀況怎麼辦,客人發錯了金額,或者公寓不可用,會發生什麼。首先我們將Ether返還(減去gas):
} else { tenantAddress.transfer(msg.value);
然後我們返回零,所以應用程式知道租賃沒有通過:
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智慧合約