從源碼角度看如何在TON Chain上創(chuàng)建一個(gè)NFT
在 EVM 中開發(fā) NFT 和在 TON Chain 上開發(fā) NFT 有哪些不同
發(fā)行一個(gè) FT 或 NFT 對于 DApp 開發(fā)者來說通常是最基本的需求。因此我也以此作為學(xué)習(xí)入口。首先讓我們來了解以下在 EVM 技術(shù)棧中開發(fā)一個(gè) NFT 和在 TON Chain 中的區(qū)別?;?EVM 的 NFT 通常會選擇繼承 ERC-721 的標(biāo)準(zhǔn)。所謂 NFT,指的是不可分割的加密資產(chǎn)類型,且每個(gè)資產(chǎn)具有唯一性,即存在某些專屬的特性。而 ERC-721 就是對這個(gè)類型的資產(chǎn)的一種通用的開發(fā)范式。讓我們看一個(gè)常見的 ERC721 合約需要實(shí)現(xiàn)哪些函數(shù)以及記錄哪些信息。下圖是一個(gè) ERC721 接口。可以看到與 FT 不同,在轉(zhuǎn)賬接口中需要輸入的是待轉(zhuǎn)賬的 tokenId 而非數(shù)量。這個(gè) tokenId 也是 NFT 資產(chǎn)唯一性最基本的體現(xiàn),當(dāng)然為了承載更多的屬性,通常會為每個(gè) tokenId 記錄一個(gè) metadata,這個(gè) metadata 是一個(gè)外部鏈接,保存了該 NFT 的其他可擴(kuò)展數(shù)據(jù),例如一張 PFP 圖片的鏈接,某些屬性名稱等。
對于熟悉 Solidity 或者熟悉面向?qū)ο蟮拈_發(fā)者來說,實(shí)現(xiàn)這樣一個(gè)智能合約是件容易的事,只要定義好合約中需要的數(shù)據(jù)類型,例如一些關(guān)鍵的映射關(guān)系 mapping,并根據(jù)所需功能開發(fā)相應(yīng)的對這些數(shù)據(jù)的修改邏輯,即可實(shí)現(xiàn)一個(gè) NFT。
然而在 TON Chain 中這一切變的不太相同,造成不同的核心原因有兩個(gè):
在 TON 中數(shù)據(jù)的存儲是基于 Cell 實(shí)現(xiàn)的,而同一個(gè)賬戶的 Cell 通過有向無環(huán)圖來實(shí)現(xiàn)。這樣就導(dǎo)致需要之久化存儲的數(shù)據(jù)不能無邊界的增長下去,因?yàn)橐粋€(gè)有向無環(huán)圖來說,數(shù)據(jù)深度決定的查詢成本,當(dāng)深度無限延伸之后,有可能造成查詢成本過高,從而導(dǎo)致合約陷入死鎖問題。
為了追求高并發(fā)性能,TON 舍棄了串行執(zhí)行的架構(gòu),轉(zhuǎn)而采用了一個(gè)專為并行而生的開發(fā)范式,Actor 模型,來重構(gòu)執(zhí)行環(huán)境。這就造成了一個(gè)影響,智能合約之間只能通過發(fā)送所謂內(nèi)部消息的方式異步調(diào)用,注意無論是狀態(tài)修改類型或只讀類型的調(diào)用都需要遵循這個(gè)原則,除此之外,也需要仔細(xì)考慮異步調(diào)用若失敗,如何處理數(shù)據(jù)回滾的問題。
當(dāng)然關(guān)于技術(shù)上其他不同點(diǎn)在上一篇文章中有過詳細(xì)的論述,本篇文章希望可以聚焦在智能合約開發(fā)上,所以不展開討論。上述兩條設(shè)計(jì)原則讓 TON 中智能合約開發(fā)與 EVM 產(chǎn)生了很大區(qū)別。在開始的論述中,我們知道一個(gè) NFT 合約中需要定義一些映射關(guān)系,也就是 mapping,來保存 NFT 相關(guān)的數(shù)據(jù)。其中最重要的就是 owners,這個(gè) mapping 存儲了某個(gè) tokenID 對應(yīng)的 NFT 的所有者地址的映射關(guān)系,決定了 NFT 的所有權(quán),轉(zhuǎn)賬就是對該所有權(quán)的修改。由于理論上這是一個(gè)可以無邊界的數(shù)據(jù)結(jié)構(gòu),需要盡量避免。因此官方推薦以是否存在無邊界數(shù)據(jù)結(jié)構(gòu)作為分片的標(biāo)準(zhǔn)。即當(dāng)有類似的數(shù)據(jù)存儲需求時(shí),通過主從合約的范式來替代,通過創(chuàng)建子合約的方式來管理每個(gè) key 對應(yīng)的數(shù)據(jù)。并通過主合約管理全局參數(shù),或幫助處理子合約之間的內(nèi)部信息交互。
這也就意味著在 TON 中的 NFT 也需要采用類似的架構(gòu)來設(shè)計(jì),每個(gè) NFT 都是一個(gè)獨(dú)立的子合約,保存了諸如所有者地址,metadata 等專屬數(shù)據(jù),并通過一個(gè)主合約來管理全局?jǐn)?shù)據(jù),例如 NFT name,symbol,總供應(yīng)量等。
在明確了架構(gòu)后,接下來就需要解決核心功能的需求了,由于采用了這個(gè)主從合約的方式,因此就需要明確哪些功能由主合約承載,哪些功能由子合約承載,并且兩者之間通過什么內(nèi)部信息溝通,同時(shí)當(dāng)出現(xiàn)執(zhí)行錯(cuò)誤時(shí),如何回滾之前的數(shù)據(jù)。通常情況下,在開發(fā)復(fù)雜的大型項(xiàng)目之前,通過一個(gè)類圖并明確彼此之間的信息流,并仔細(xì)思考內(nèi)部調(diào)用失敗后的回滾邏輯是必要的,當(dāng)然上述 NFT 開發(fā)雖然簡單,但也可以做類似驗(yàn)證。
從源碼學(xué)習(xí)開發(fā) TON 智能合約
TON 選擇了設(shè)計(jì)一種類 C 語言的、靜態(tài)類型語言,名為 Func 來作為智能合約開發(fā)語言,那么接下來就讓我們從源碼來學(xué)習(xí)如何開發(fā) TON 智能合約,我選擇了 TON 官方文檔中的 NFT 示例來進(jìn)行介紹,感興趣的小伙伴可以自行去查閱。在這個(gè) case 中實(shí)現(xiàn)了一個(gè)簡單的 TON NFT 例。讓我們看下合約結(jié)構(gòu),共分為兩個(gè)功能合約以及三個(gè)必要的庫。
這兩個(gè)主要的功能合約即按照上述的原則進(jìn)行設(shè)計(jì),首先讓我們來看下主合約 nft-collection 的代碼:
這引入了第一個(gè)知識點(diǎn),如何在 TON 智能合約中持久化存儲數(shù)據(jù),我們知道在 Solidity 中數(shù)據(jù)的持久化存儲是由 EVM 根據(jù)參數(shù)的類型自動處理的,通常情況下,智能合約的狀態(tài)變量將在執(zhí)行結(jié)束后根據(jù)最新值自動被持久化存儲,開發(fā)者并不需要考慮這個(gè)過程。但在 Func 中情況并不如此,開發(fā)者需要自己來實(shí)現(xiàn)相應(yīng)的處理邏輯,這個(gè)情況有點(diǎn)類似于 C 和 C++ 需要考慮 GC 的過程,但其他新的開發(fā)語言通常將這部分邏輯自動化處理。我們來看下代碼,首先引入一些需要的庫,然后看到第一個(gè)函數(shù) load_data 用于讀取被持久化存儲的數(shù)據(jù),其邏輯為首先通過 get_data 返回持久化合約存儲 cell,注意這是由標(biāo)準(zhǔn)庫 stdlib.fc 實(shí)現(xiàn)的,通常情況下可以將其中的一些函數(shù)視為系統(tǒng)函數(shù)來使用。
該函數(shù)的返回值類型為 cell,這是 TVM 中的 cell 類型。在之前的介紹中,我們已經(jīng)知道 TON 區(qū)塊鏈中的所有持久數(shù)據(jù)都存儲在 cell 樹中。每個(gè) cell 最多有 1023 位任意數(shù)據(jù)和最多四個(gè)對其他 cell 的引用。cell 在基于堆棧的 TVM 中用作內(nèi)存。cell 中保存的是緊編碼后的數(shù)據(jù),要想獲取其中具體的明文數(shù)據(jù),需要將 cell 轉(zhuǎn)換為被稱為 slice 的類型。cell 可以通過 begin_parse 函數(shù)轉(zhuǎn)換成為 slice 類型,然后可以通過從 slice 加載數(shù)據(jù)位和對其他 cell 的引用來獲得 cell 中的數(shù)據(jù)。注意 15 行代碼中的這種調(diào)用方法是一個(gè) func 中的語法糖,可以直接調(diào)用第一個(gè)函數(shù)的返回值的第二個(gè)函數(shù)。并在最后按照數(shù)據(jù)持久化順序依次加載相應(yīng)的數(shù)據(jù)。注意這個(gè)過程和 solidity 不同,并不是根據(jù) hashmap 調(diào)用,所以這個(gè)調(diào)用順序不能亂。
在 save_data 函數(shù)中,邏輯與之類似,只不過這是一個(gè)反向的過程,這就引入了下一個(gè)知識點(diǎn),一個(gè)新的類型 builder,這是 cell 構(gòu)建器的類型。數(shù)據(jù)位和對其他 cell 的引用可以存儲在構(gòu)建器中,然后構(gòu)建器可以最終化為新 cell。首先通過標(biāo)準(zhǔn)函數(shù) begin_cell 創(chuàng)建一個(gè) builder,并依次通過 store 相關(guān)函數(shù)存儲相關(guān)函數(shù),注意上文中調(diào)用順序與此處存儲順序需要保持一致。最后通過 end_cell 完成新 cell 構(gòu)建,這時(shí)該 cell 被管理在內(nèi)存中,最后通過最外層的 set_data,就可以完成對該 cell 的持久化存儲。
接下來讓我們來看下業(yè)務(wù)相關(guān)函數(shù),首先需要先介紹下一個(gè)知識點(diǎn),如何通過合約創(chuàng)建一個(gè)新的合約,這在剛剛介紹的主從架構(gòu)中將被經(jīng)常用到。我們知道在 TON 中,智能合約之間的調(diào)用是通過發(fā)送內(nèi)部消息的方式來實(shí)現(xiàn)的。這是通過一個(gè)名為 send_raw_message 來實(shí)現(xiàn)的,注意第一個(gè)參數(shù)是 message 編碼后的 cell,第二個(gè)參數(shù)是標(biāo)識位,用于表明該交易的執(zhí)行方式的區(qū)別,在 TON 中設(shè)置了不同的內(nèi)部消息發(fā)送的執(zhí)行方式,目前有 3 種消息 Modes 和 3 種消息 Flags??梢詫我?Mode 與多個(gè)(也許沒有)標(biāo)志組合以獲得所需的 mode。組合只是意味著將它們值的和填入即可。下面給出了 Modes 和 Flags 的描述表格:
那么讓我們來看第一個(gè)主要函數(shù),deploy_nft_item,顧名思義,這是一個(gè)用于創(chuàng)建或者說鑄造新 NFT 實(shí)例的函數(shù),經(jīng)過一番操作編碼一個(gè) msg 后,通過 send_raw_message 發(fā)送該內(nèi)部合約,并選擇了 flag 1 的發(fā)送標(biāo)識位,僅將編碼中指定的 fee 作為本次執(zhí)行的 gas fee。經(jīng)過上文的介紹我們很容易意識到,這個(gè)編碼規(guī)則應(yīng)該是對應(yīng)創(chuàng)建一個(gè)新的智能合約的方式。那讓我們來看看具體是怎么實(shí)現(xiàn)的。
讓我們直接看 51 行,上面兩個(gè)函數(shù)是用于生成 message 所需信息的輔助函數(shù),因此我們后面再來看,這是一個(gè)用于創(chuàng)建智能合約的內(nèi)部消息的編碼過程,中間的一些數(shù)字其實(shí)也是一些標(biāo)識位,用于說明該內(nèi)部消息的需求,這里要引入下一個(gè)知識點(diǎn),TON 選擇了一種名為 TL-B 的二進(jìn)制語言來描述消息的執(zhí)行方式,并且根據(jù)設(shè)置不同的標(biāo)記位來實(shí)現(xiàn)某些特定功能的內(nèi)部消息,最容易想到的兩個(gè)使用場景,新合約創(chuàng)建和已部署合約函數(shù)調(diào)用。而 51 行的這種方式即對應(yīng)了前者,創(chuàng)建一個(gè)新的 nft item 合約,而這主要是通過 55,56,57 三行指定的。首先 55 行這一大串?dāng)?shù)字是一系列標(biāo)識位,注意 store_uint 的第一個(gè)入?yún)⑹菙?shù)值,第二個(gè)是位長,其中決定了該內(nèi)部消息是合約創(chuàng)建的是后三個(gè)標(biāo)記位,以及相應(yīng)二進(jìn)制值位為 111(十進(jìn)制即為 4+2+1),其中前兩個(gè)表示該消息將附帶 StateInit 數(shù)據(jù),這個(gè)數(shù)據(jù)即為新合約的源碼,以及初始化所需的數(shù)據(jù)。而后一個(gè)標(biāo)記位表示內(nèi)部消息附載,即希望執(zhí)行相關(guān)邏輯以及需要的參數(shù)。因此你會看到在第 66 行代碼并沒有設(shè)置該三位數(shù)據(jù),則表明的是一次對已部署合約的函數(shù)調(diào)用。具體的編碼規(guī)則在這里查看。
那么 StateInit 的編碼規(guī)則即對應(yīng)了 49 行代碼,通過 calculate_nft_item_state_init 計(jì)算,注意 stateinit 數(shù)據(jù)的編碼也遵循了一種既定的 TL-B 編碼規(guī)則,除了一些標(biāo)記位之外,主要涉及到兩部分新合約 code 和以及初始化 data。data 的編碼順序需要與新合約指定的持久化 cell 的存儲順序保持一致。在 36 行可以看到,初始化數(shù)據(jù)有 item_index,即類似與 ERC721 中的 tokenId,以及由標(biāo)準(zhǔn)函數(shù) my_address 返回的當(dāng)前合約地址,即為 collection_address,這個(gè)數(shù)據(jù)的順序與 nft-item 中的聲明保持一致。
接下來一個(gè)知識點(diǎn)就是在 TON 中,所有未生成的智能合約而可以預(yù)先計(jì)算其生成后的地址,這點(diǎn)與 Solidity 中的 create2 函數(shù)類似,在 TON 中新地址的生成由兩部分組成,workchain 標(biāo)識位與 stateinit 的哈希值拼接而成,前者在之前的介紹中我們已經(jīng)知道是為了相應(yīng) TON 無限分片架構(gòu)而需要被指定的,當(dāng)前為統(tǒng)一值。由標(biāo)準(zhǔn)函數(shù) workchain 獲得。后者由標(biāo)準(zhǔn)函數(shù) cell_hash 獲得。因此回到該例子,calculate_nft_item_address 即為預(yù)先計(jì)算新合約地址的函數(shù)。并將生成值在第 53 行編碼到 message 中,作為該內(nèi)部消息的接收地址。而 nft_content 則對應(yīng)了對被創(chuàng)建合約的初始化調(diào)用,具體的實(shí)現(xiàn)在下一篇文章中介紹。
至于 send_royalty_params,則需要是對某只讀請求的內(nèi)部消息的相應(yīng),在之前的介紹中,我們特意強(qiáng)調(diào)了在 TON 中內(nèi)部消息不光包含可能會修改數(shù)據(jù)的操作,只讀操作也需要通過這種方式實(shí)現(xiàn),因此該合約即為此類操作,首先值得注意的是 67 行表示響應(yīng)該請求后對請求者回調(diào)函數(shù)的標(biāo)記,記下來即為返回的數(shù)據(jù),分別是請求的 item index,以及相應(yīng)的 royalty 數(shù)據(jù)。
接下來讓我們引入下一個(gè)知識點(diǎn),TON 中智能合約只有兩個(gè)統(tǒng)一的入口,名為 recv_internal 和 recv_external,其中前者為所有內(nèi)部消息的統(tǒng)一調(diào)用入口,后者為所有外部消息的統(tǒng)一調(diào)用入口,開發(fā)者需要在函數(shù)內(nèi)部根據(jù)需求,采用類似 switch 的方式根據(jù) message 指定的不同標(biāo)記位來響應(yīng)不同的請求,這里的標(biāo)記位即為上述 67 行的回調(diào)函數(shù)標(biāo)記?;氐皆摾樱紫葘?message 進(jìn)行空位檢查,通過后分別解析 message 中的信息,首先在 83 行解析獲得 sender_address,該參數(shù)將用于后續(xù)的權(quán)限檢查,注意這里的~操作符,屬于另一個(gè)語法糖。這里先不展開將。接下來解析 op 操作標(biāo)記位,而后根據(jù)不同的標(biāo)記位,分別處理相應(yīng)請求。其中即根據(jù)某些邏輯分別調(diào)用了上述的函數(shù)。例如響應(yīng)對 royalty 參數(shù)的請求,或鑄造新的 nft,并自增全局 index。
接下來一個(gè)知識點(diǎn)對應(yīng)了 108 行,想必大家通過命名也可以知道該函數(shù)的處理邏輯,與 Solidity 中的 require 函數(shù)類似,F(xiàn)unc 中通過標(biāo)準(zhǔn)函數(shù) throw_unless 來拋出異常,第一個(gè)入?yún)殄e(cuò)誤碼,第二個(gè)是檢查位布爾值,若位 false 則拋出異常,并附帶該錯(cuò)誤碼。而在這行中通過 equal_slices 來判斷上面解析到的 sender_address 是否等于該合約持久化存儲的 owner_address,做權(quán)限判斷。
最后為了使代碼結(jié)構(gòu)更清晰,開始閑了一系列幫助獲取持久化信息的輔助函數(shù),在這里就不展開介紹了,開發(fā)者可以參考這種結(jié)構(gòu)來開發(fā)自己的智能合約。
TON 生態(tài)的 DApp 開發(fā)實(shí)在是件有趣的事情,與 EVM 的開發(fā)范式有很大差異,因此我會通過一系列文章來介紹如何在 TON Chain 中開發(fā) DApp。
你可能感興趣的文章
-
什么是Toncoin(TON),如何購買?TON能投資嗎?
此文將詳細(xì)介紹什么是Toncoin(TON),以及如何購買(多種交易途徑)?也詳細(xì)給大家介紹TON是否是一項(xiàng)好的投資的詳細(xì)解讀,希望此篇文章能夠幫助大家更好的了解TON…
2024-06-23 -
MOMOAI是什么?MTOS幣怎么樣?未來前景如何?
作為前沿的區(qū)塊鏈游戲平臺,MOMO AI的發(fā)展案例為整個(gè)行業(yè)提供了寶貴的見解,它不僅展示了AI技術(shù)和區(qū)塊鏈的融合潛力,還突出了社區(qū)和社交 互動在現(xiàn)代游戲中的重要性,隨著技術(shù)…
2025-04-22 -
什么是ton倉鼠快打?從Hamster Kombat收到空投的方法
Hamster Kombat 是一款點(diǎn)擊即賺取游戲,結(jié)合了加密貨幣交易模擬器, 在該游戲中,玩家扮演的是一家新興加密貨幣交易所的首席執(zhí)行官,任務(wù)是將交易所發(fā)展到前所未有的新高度,…
2024-06-19 -
TON幣暴漲100%,未來TON幣價(jià)格走勢分析
關(guān)于TON的加權(quán)情緒,在撰寫本文時(shí),其值為1.6,這表明社交媒體平臺上圍繞altcoin的大多數(shù)討論都非常樂觀,這也意味著TON貿(mào)易商對其價(jià)格在短期內(nèi)將進(jìn)一步上漲持樂觀和信心,如…
2024-06-15 -
Toncoin行情井噴!TON集會勢頭能否延續(xù)?
幣安上更多的交易員押注于上漲!TON的衍生品數(shù)據(jù)顯示交易量有所下降,這可能表明在一段時(shí)間的活動增強(qiáng)后,交易量有所降溫,可能是在最近的價(jià)格飆升之后整體市場多空比略低…
2024-06-13 -
TON幣必將爆漲?為什么?
TON公鏈生態(tài)必將爆發(fā)?TON的鏈上總鎖倉價(jià)值(TVL)為3億7千多萬美金,月增漲率為85%,公鏈排名很快將躋身前20,TON公鏈每日交易數(shù)量高達(dá)600多萬,每日的鏈上激活錢包高達(dá)17萬…
2024-06-06 -
TON鏈上的幣有哪些?TON鏈上常見的代幣詳細(xì)介紹
TON鏈,作為一個(gè)區(qū)塊鏈網(wǎng)絡(luò),支持多種加密貨幣,這些代幣共同構(gòu)成了TON鏈的金融生態(tài),為用戶提供了豐富的選擇和可能性,那么,TON鏈上的幣有哪些?下面將為大家詳細(xì)介紹TON鏈…
2024-06-06 -
TON幣總量多少?TON幣發(fā)行量介紹
TON由Telegram Messenger的創(chuàng)始人Durov兄弟于2018年設(shè)計(jì),是一個(gè)靈活、超加密、用戶友好且功能齊全的多區(qū)塊鏈,換句話說,它在速度和可擴(kuò)展性方面超越了比特幣和以太坊,很多…
2024-05-31 -
TON幣發(fā)行價(jià)多少?TON幣發(fā)行價(jià)格和發(fā)行總量介紹
TON的中文名是開放網(wǎng)絡(luò),它是第三代權(quán)益證明、快速、安全、可擴(kuò)展的區(qū)塊鏈,它每秒能夠處理數(shù)百萬筆交易,TON網(wǎng)絡(luò)能夠適應(yīng)當(dāng)前批準(zhǔn)和設(shè)計(jì)的所有合理應(yīng)用,很多投資者想要知道…
2024-05-30 -
TON是什么幣種?一文了解Toncoin/TON幣
Toncoin(TON)是TON網(wǎng)絡(luò)的原生代幣,第1層是指區(qū)塊鏈開發(fā)的層次,代表給定區(qū)塊鏈網(wǎng)絡(luò)的關(guān)鍵框架或架構(gòu),TON被設(shè)計(jì)為可擴(kuò)展的,據(jù)稱能夠容納數(shù)十億用戶,很多投資者想要進(jìn)一步…
2024-05-30