作者:Joe Bender(Hiro PBC)

2021年7月27日



自1 月份Stacks 2.0上線以來,開發者們已經擁抱並探索了Clarity 的世界。

他們喜歡用熟悉的、基於LISP 的語言構建智能合約,該語言利用強大的靜態類型系統並重視安全性。 Clarity 還消除了像以太坊的Solidity 這樣的圖靈完備的智能合約語言帶來的諸多問題。現在Stacks 開發者社區正在大步向前並迅速成熟,我們開始看到一些模式的出現,例如最佳實踐、最佳智能合約結構、常見設計選擇以及最常用的功能和關鍵字。 Clarity 中的關鍵字函數是語言的基本構建塊,並執行操作區塊鏈上的數據和價值所需的機制。

為了幫助開發者更輕鬆地開始使用Clarity 構建智能合約,我們編制了一份最有用、最受歡迎的Clarity 關鍵字和函數列表。為此,我們使用了來自Clarity搜索(Clarity Search) 的API,Clarity搜索是一個用於顯示所有智能合約源代碼的搜索引擎,已成功部署在Stacks 主網上。對於想要瀏覽社區中其他開發者構建的Clarity 範例的開發者來說,這是一個很有用的工具。

我們的探索結果包括這五個要素:tx-sender、block-height、define-trait & impl-trait、define-map 和define-constant。

tx-sender

tx-sender 是Clarity語言中最常用的關鍵字。這是一個不需要輸入的全局關鍵字,但在Clarity 智能合約中使用時,tx-sender 將輸出一個主體(Principal)。主體是一種Clarity 原生類型,表示可以擁有代幣餘額的實體。 Stacks 上有兩種類型的主體:區塊鏈上可以發送交易的哈希錢包地址(由公鑰/私鑰保護),以及存在於網絡上的Clarity 智能合約(沒有私鑰,無法發送交易)。因此,每當在智能合約中使用tx-sender 時,它都會使用與最初調用合約的地址相關聯的標識符值來實例化該變量。在Stacks 上,錢包地址和主體由40 個字母數字字符組成的字符串表示。 (例如:STJRM2AMVF90ER6G3RW1QTF85E3HZH37006D5ER1)

了解下更多背景, Stacks 地址基於三個組成部分:前綴(S)、描述地址類型的版本字節(T =testnet、P = mainnet、M = multisig mainnet、N = multisig testnet),其餘的是hash160公鑰,表示為Crockford Base32字符串。

使用tx-sender 以獲取主體來轉移STX

在這個基本示例中,調用此合約的人試圖將一定數量的STX 從他們自己的地址轉移到朋友的錢包中。調用stx-transfer 函數,需要三個邏輯參數:數額、發送STX 的主體以及應接收代幣的地址。手動將地址輸入到合約中將是低效和有潛在危險的。這是一個極長的字符串,很容易拼寫錯誤,而且它還會通過消除抽象來破壞合約的公共可重用性。通過使用tx-sender 關鍵字,Clarity 合約可以使用調用合約的主體的地址自動地“自動填充” tx-sender 變量。這樣,區塊鏈就知道從哪裡提取資金以完成所需的交易機制。

tx-sender 似乎是在智能合約中實現歸屬的一種基本方式,但它是任何去中心化操作的組成部分。幾乎可以將其視為電子郵件中的“發件人”字段。 tx-sender 是一種將主體納入Clarity 智能合約的可靠易用的方式。

block-height

每個Clarity 開發者工具箱中的另一個關鍵字是block-height 。有時將有關區塊鏈操作的統計信息注入智能合約很有用。區塊鏈的核心是狀態機,擁有一個跟踪礦工驗證的最新狀態的變量可能很有價值。為了解決這個問題,在Clarity 中使用block-height 將返回一個代表Stacks 區塊鏈當前塊高度的無符號整數值。

它也可以用作時間估算器。由於區塊鍊是一個如此安全的計算機科學系統,因此很難將某些真實世界的信息輸入網絡。處理特定的時間和持續時間可能會有問題,因為區塊鏈沒有原生的內部時鐘。因此,可預測的塊數和它們之間一致的待處理時間可以用作時間流逝的有用相對跟踪器。

使用block-height的礦工註冊表

上面的例子是一個實用的Clarity 合約,用於創建在Stacks 網絡上成功開採區塊的礦工們的註冊表。為此,該開發者創建了一個簡單的地圖,用於跟踪每個新區塊號以及獲得區塊獎勵的礦工主體。這是在Stacks 上維護實時存檔和挖掘歷史記錄的超級有用的功能。從邏輯上講,這個地圖只需要一個獲勝礦工主體的元組,以及一個相關區塊號的無符號整數。使用區塊高度獲取有關最近挖出區塊的數據是一種簡單輕便的方法,可以快速獲取重要指標,並為合約中的特定操作添加基於時間的先決條件。

define-trait 和impl-trait

如果今天要為區塊鏈行業重現史蒂夫鮑爾默(Steve Ballmer)的經典視頻,我想他會喊“互操作性!互操作性!互操作性!”

區塊鏈仍然是處於實驗階段的一項極為新興的技術。有數千個不同的團隊致力於推動技術的極限,但重要的是要製定標準,讓斷開連接的智能合約和代幣在一起“玩得開心”。您看到早期互聯網中出現了相同類型的互操作性。 HTTP、藍牙、RSS、XML,甚至像.PDF 這樣的特定文件類型都在努力讓不同的硬件和軟件共享一個共同的詞彙。

Clarity 中的trait 系統是一種允許簡單定義和配置通用接口的機制。 define-trait函數概述了將合約視為trait 的必要名稱、函數、參數類型和返回類型。然後,這允許獨立的智能合約相信它們之間有一組共同的功能。一旦trait 被定義並公開部署到區塊鏈,一個簡單的impl-trait調用就會向網絡發出信號,表明智能合約確實在實現預先存在的trait 的特性。

Clarity 中的NFT trait定義,以及它必須執行的四個函數以符合標準

最近部署到Stacks 網絡上的非同質化代幣(NFT) trait是一個完美的例子。在測試網時代,由於開發者想要測試Clarity 的表達功能,因此在資源管理器和Github 上出現了各種NFT 實驗。然而,社區接受的、正式的NFT 標準尚未建立。這導致所有NFT 都實現了自己專門的、細緻入微的方法和功能,以實現NFT 功能。當社區開發NFT trait並部署到網絡中時,一切都發生了變化!現在,如上所示,智能合約必須包含四種不同的方法才能被視為Stacks上符合標準的NFT。這些方法允許合約設置原始所有者、連接元數據、檢查最後註冊的NFT 或轉移所有權。

實現NFT trait的Clarity 智能合約

一旦定義了trait,並且開發者希望將其執行到他們的新智能合約中,只需合約頂部的一行代碼,表明其遵守NFT trait。上述合約是在Stacks 區塊鏈上重創Beeple NFT 。這個合約的創建者所要做的就是使用函數impl-trait,後跟一個trait標識符,其中包含定義trait的合約的主體。一旦實現了trait,該合約就需要包含我們之前提到的四種必需方法,這些方法允許NFT 正常運行。它還允許NFT 元數據顯示在錢包或資源管理器等產品中。來看看資源管理器中的beeple.clar合約,自己調查源代碼和執行trait !

define-map

就編程語言而言,數據結構是處理和操作信息集不可或缺的一部分。您可能熟悉用於存儲信息的數組、列表、樹、圖形、表格、集合和其他數據結構。在Clarity 中,地圖(Maps)用於跟踪特定數據在內存中的位置。它們是關聯兩組信息的一種簡單但有效的方法。

定義一個地圖來保存包含用戶提交的quote到字符串

在上面的示例中,開發者將合約部署到Stacks 區塊鏈,以維護用戶提交的quote。要將quote添加到庫中,個人只需要調用合約的add-quote 方法並傳入他們選擇的字符串。這些quote及其相關標識符是如何存儲的?地圖被定義為一種數據結構,用於對quote的字符串和任意遞增的ID 進行編目。每當添加新quote時,都會使用map-insert函數將其附加到地圖中。由於地圖狀態存在於智能合約中的區塊鏈上,因此任何用戶都可以調用get-quote-by-id 方法,傳入報價標識符,並通過地圖接收與該ID 關聯的map-get函數。

雖然地圖可能看起來像一個簡單的元組列表,但它實際上解鎖了大量對開發者有價值的獨特功能。能夠以有組織的方式方便地存儲和調用數據對於為眾多用戶提供服務的更強大的合約至關重要。

define-constant

define-constant是Clarity 中最常用的函數之一,這是因為它的多功能性。有時,作為開發者,您需要一些持久的全局變量來在整個合約中執行必要的功能。實現這一點的最安全、最有組織的方法是使用define-constant 函數在您的Clarity 合約中實例化常量變量。傳遞到定義中的表達式在合約啟動時按照它在合約中提供的順序進行評估。與其他類型的定義語句類似,define-constant 只能在其他函數之外使用。你不能在函數本體的中間放置一個定義常量的語句,因為那樣它就不能被全局訪問。

定義常量變量的Boomboxes 智能合約

Boom.Money的新Stacking NFT:Boomboxes是一個實現常量特別好的Clarity 智能合約。這是一個極其複雜的軟件,它處理加密貨幣,與Stacks 的核心共識機制交互,並跟踪各種stacking (棧押) 週期的長度。實現Boombox 機制需要一些全局常量。

它定義了一個常量dplyr,並將tx-sender 分配給它。之前,我們知道tx-sender 只是部署合約的用戶的地址。該開發人員可能需要在合約後期提供他們的個人地址(例如,允許調用合約)。使用define-constant 將主體實例化為全局變量是在各種方法中使用它的一種快速簡便的方法。

它還使用定義常量設置某人需要參與的最小STX 數量。通過將minimum-amount 實例化為100000000 uSTX,該開發人員可以將該變量插入合約內的任何位置,以進行快速輸入驗證。

不過,這裡最有趣的定義常量用法是它如何幫助在智能合約中建立時間感。 Stacks 區塊鏈上的stacking是按週期完成的,一個stacking週期大約持續約2,100 個比特幣區塊或15 天。因此,對於此智能合約而言,能夠辨別特定週期何時開始、進行中或結束非常重要。首先,該開發者為時間限制常數分配了一個值u690950。這實際上與秒或分鐘無關,而是stacking週期將開始且stacking參與者必須貢獻其STX 的精確比特幣區塊號。然後,當該合約被調用開始支付NFT 時,它會檢查當前區塊高度是否小於time-limit 中定義的區塊數量。這是一種迂迴但天才的方法,可以確定現實世界中特定時間何時過去。

正如這個合約所證明的那樣,在合約開始時定義常量可能是一種超能力。該開發者實例化了五個巧妙的常量,然後在整個Clarity 合約中高效地使用它們。在為Stacks 開發軟件時,將它們視為友好的全局變量。