本文探討以下問題:哪種智能合約語言更有優勢,Solidity還是Vyper?最近,關于哪種是“最好的”智能合約語言存在很多爭論,當然了,每一種語言都有它的支持者。
這篇文章是為了回答這場辯論最根本的問題:
我應該使用哪一種智能合約語言?
為了弄清問題的本質,我們將先討論語言的工具和可用性,然后再考慮智能合約開發者主要關心的問題之一:gas優化。具體來說,我們將研究四種EVM語言:Solidity、Vyper、Huff和Yul。Rust并不在其中,它應該出現在一篇關于非EVM鏈的文章。
但首先,劇透一下結果。
Solidity、Vyper、Huff和Yul都是可以讓你完成工作的優秀語言。Solidity和Vyper是高級語言,大多數人都會用到。但是如果你有興趣編寫近乎匯編的代碼,那Yul和Huff也可以勝任。
所以如果你堅持選擇其中一個使用,那就拋硬幣吧:因為無論你選擇哪種語言,都是可以完成項目的。如果你是智能合約的新手,完全可以使用任何一種語言來開始你旅程。
此外,這些語言也一直在變化,你可以挑選特定的智能合約和數據,從而使得運行它們的不同的語言,表現出來的更好或者更差的效果。所以請注意,為了避免不客觀,我們在比較不同語言在gas優化上的優劣時,都選擇了最簡的智能合約作為例子,如果你有更好的例子,也請分享給我們!
現在,如果你是這個領域的老手,讓我們深入了解這些語言,看看它們的細節吧。
EVM編程語言
我們將要研究的四種語言如下:
Solidity:目前DeFiTVL占比最大的語言。是一種高級語言,類似于JavaScript。Vyper:目前DeFiTVL排名第二的語言。也是一種高級語言,類似于Python。Huff:一種類似于匯編的底層語言。Yul:一種類似于匯編的底層語言,內置于Solidity。為什么是這四個?
使用這四種語言,是因為它們都與EVM兼容,而且其中的Solidity和Vyper是迄今為止最受歡迎的兩種語言。我添加了Yul,因為在不考慮Yul的情況下,與Solidity進行gas優化比較是不全面的。我們添加了Huff是因為想以一種不是Yul,但是與幾乎就是在用opcode編寫合約的語言作為基準。
就EVM而言,在Vyper和Solidity之后,第三、第四和第五的流行程度也越來越高。對于沒有在本文中比較的語言;只是因為它們的使用度不高。然而,有許多很有前景的智能合約語言正在興起,我期待能夠在未來嘗試它們。
什么是Solidity?
Solidity是一種面向對象的編程語言,用于在以太坊和其他區塊鏈上來編寫智能合約。Solidity深受C++、Python和JavaScript的影響,并且專為EVM而設計。
什么是Vyper?
Vyper是一種面向合約的類似于Python的編程語言,也是為EVM設計的。Vyper增強了可讀性,并且限制了某些用法,從而改進了Solidity。理論上,Vyper提升了智能合約的安全性和可審計性。
數據:3月Solana網絡上合約部署數量增加:金色財經報道,根據Artemis的統計數據,近期部署在Solana網絡上的獨立合約數量有所增加,達到全年的最高水平。截至本文撰寫時,有75個不同的合約處于活躍狀態。幾周前,這一數字增加到98,進一步證明了3月份的部署量。
此外,Solana的開發活動在3月份有所增加,反映了獨立合約的數量。該指標似乎正在穩步恢復,盡管仍然低于Solana網絡故障之前的水平。(AMBCrypto)[2023/3/20 13:14:33]
當前的情況
來源于DefiLlama語言分析數據
根據DefiLlama的數據,截至目前,在DeFi領域,Solidity智能合約獲得了87%的TVL,而Vyper智能合約獲得了8%。
因此,如果你純粹基于受歡迎程度來選擇語言的話,除了Solidity,就不需要看別的了。
比較相同的合約
現在讓我們了解每種語言寫出的合約的是什么樣的,然后比較它們的gas性能。
這是用每種語言編寫的四份幾乎相同的合同。做了大致相同的事情,它們都:
Storageslot0有一個私有變量number(uint256)。有一個帶有readNumber()函數簽名的函數,它讀取storageslot0中的內容。允許你使用storeNumber(uint256)函數簽名更新該變量。這就是這個合約做的操作。
我們用來比較語言的所有代碼都在這個GitHubrepo中:
https://github.com/PatrickAlphaC/sc-language-comparison
Solidity
Vyper
Huff
Yul
開發體驗
通過查看這四張圖片,我們可以大概了解編寫每種語言的感受。就開發人員經驗而言,編寫Solidity和Vyper代碼要快得多。這些語言是高級語言,而Yul和Huff是更底層的語言。僅出于這個原因,就很容易理解為什么這么多人采用Vyper和Solidity。
看一下Vyper和Solidity,你可以清楚地感覺到Vyper是從Python中汲取了靈感,而Solidity是從JavaScript和Java中汲取靈感。因此,如果你對于這幾種語言更熟悉的話,那就能很好地使用對應的智能合約語言。
Waves創始人:將發行新Stablecoin,同時啟動USDN脫錨解決方案:12月20日消息,Waves創始人SashaIvanov在社交媒體上發文表示將推出一個新的Stablecoin,同時在此之前會有一個USDN脫錨情況的解決方案。此外,Sasha還表示新的Stablecoin將被構建為無法脫錨。[2022/12/20 21:56:42]
Vyper旨在成為一種簡約、易于審計的編程語言,而Solidity旨在成為一種通用的智能合約語言。編碼的體驗在語法層面上也是如此,但每個人肯定都有自己的主觀感受。
我不會過多地討論工具,因為大多數這些語言都有非常相似的工具。主流框架,包括Hardhat、ape、titanoboa、Brownie和Foundry,都支持Vyper和Solidity。Solidity在這大多數框架中,都被優先支持,而Vyper需要使用插件才能與Hardhat等工具一起使用。然而,titanoboa是專為與Vyper一起工作而構建的,除此以外,大多數工具對二者支持都很好。
哪一種智能合約語言更節省gas?
現在是重頭戲。在比較智能合約的gas性能時,需要牢記兩點:
合約創建gas成本運行時gas成本
你如何實現智能合約會對這些因素產生重大影響。例如,你可能在合約代碼中存儲大量數組,這使得部署成本高昂但運行函數的成本更低。或者,你可以讓你的函數動態生成數組,從而使合約的部署成本更低,但運行函數成本更高。
那么,讓我們看看這四個合約,并將它們的合約創建gas消耗與其運行時gas消耗進行比較。你可以在我的?sc-language-comparisonrepo?中找到所有的代碼,包括用于比較它們所使用的框架和工具。
sc-language-comparisonrepo:
https://github.com/PatrickAlphaC/sc-language-comparison
Gas消耗比較-總結
以下是我們如何編譯本節的智能合約:
注意:我也可以為Solidity編譯使用–via-ir標志。另請注意,Vyper和Solidity在其合約末尾添加了“metadata”。這占總gas成本的一小部分增加,但不足以改變下面的排名。我將在metadata部分詳細討論這一點。
結果:
創建合約時各個語言所消耗的gas費
正如我們所見,像Huff和Yul這樣的底層語言比Vyper和Solidity的gas效率更高,但這是為什么呢?Vyper似乎比Solidity更高效,我們有這個新的“SolandYul”部分。那是因為你實際上可以在Solidity中編寫Yul。Yul是作為Solidity開發人員在寫更接近機器代碼時而創建的。
Transit Swap更新:第4次攻擊者竊取的約24.6萬美元已全額退還:10月6日消息,Transit Finance發布Transit Swap攻擊事件更新,稱在BlockSec的幫助下,第4位攻擊者竊取的約24.6萬美元已全額退還。相關鏈上地址為:0x0000000038b8889b6ab9790e20FC16fdC5714922 , 0x8ab713888ba2b70c7bd3f43aacb17731e9a1ec47。[2022/10/6 18:40:55]
因此,在上圖中,我們比較了原始Yul、原始Solidity和Solidity-Yul組合。我們代碼的Solidity-Yul版本如下所示:
Yul和Solidity結合的合約
稍后你將看到一個示例,其中這個inline-Yul對gas消耗產生了重大影響。稍后我們將看看為什么存在這些gas差異,但現在讓我們看看與Foundry中的單個測試相關的gas消耗。
我們的測試函數
這將測試將數字77存儲在storage中,然后從storage中讀取這個數字的gas成本。以下是運行此測試的結果。
SimpleStorage讀和寫的gas對比
我們沒有Yul的數據,因為獲取這個數據必須制作一個Yul-Foundry插件,我不想做-而且結果可能會與Huff相似。請記住,這是運行整個測試函數的gas成本,而不僅僅是單個函數。
Gas消耗對
好,我們來分析一下這個數據。我們需要回答的第一個問題是:為什么Huff和Yul合約的創建比Vyper和Solidity的gas效率高得多?我們可以通過直接查看這些合約的字節碼來找到答案。
當你寫智能合約時,它通常被分成兩個或三個不同的部分。
合約創建代碼運行時代碼Metadata(非必需)
對于這部分,了解opcode的基礎知識很重要。OpenZeppelin關于解構合約的博客幫助你從零開始學習相關知識:
https://blog.openzeppelin.com/deconstructing-a-solidity-contract-part-i-introduction-832efd2d7737/
合約創建代碼
合約創建代碼是字節碼的第一部分,告訴EVM將該合約寫到到鏈上。你通常可以通過在生成的二進制文件中查找CODECOPYopcode(39),然后找到它在鏈上的位置,并使用RETURNopcode(f3)返回并結束調用。
元宇宙 Memecoin Tamadoge 通過Beta 銷售籌集 100 萬美元:金色財經報道,P2E 元宇宙 meme 硬幣 Tamadoge 在 Beta 版籌集了 100 萬美元。Beta 版銷售于 7 月 25 日開始,計劃持續到 8 月 31 日。
Tamadoge 最終的目標是籌集 1000 萬美元來建立其通過游戲賺錢的 GameFi 生態系統,其中 200 萬美元來自正在進行的測試銷售,其中 1 個 TAMA 可以以 0.01 USDT 購買。Beta 版銷售也接受 ETH 和法定貨幣。(cryptonews)[2022/8/12 12:20:03]
你還會注意到很多feopcode,這是INVALID操作碼。Solidity添加這些作為標記以顯示運行時、合約創建和metadata代碼之間的差異。f3是RETURN操作碼,通常是函數或context的結尾。
你可能會認為,因為Yul-Solidity的合約創建字節碼所占空間最大而Huff的字節碼所占空間最小,所以Huff最便宜而Yul-Solidity最貴。但是當你復制整個代碼庫并將其發到到鏈上時,代碼庫的大小會產生很大的差異,這才是決定性因素。然而,這個合約創建代碼確實讓我們了解了編譯器的工作原理,即他們將如何編譯合約。
怎么讀取Opcode和Stack
目前,EVM是一個基于堆棧的機器,這意味著你所做的大部分“事情”都是從堆棧中push和pull內容。你會在左邊看到我們有opcode,在右邊我們有兩個斜杠(//)表示它們是注釋,以及在同一行執行opcode后堆棧的樣子,左邊是棧頂部,右邊是棧底。
Huffopcode的解釋
Huff合約的創建只做了它能做的最簡單的事情。它獲取你編寫的代碼,并將其返回到鏈上。
Yulopcode的解釋
Yul做同樣的事情,它使用了一些不同的opcode,但本質上,它只是將你的合約代碼放在鏈上,使用盡可能少的操作碼和一個INVALIDopcode。
Vyperopcode解釋
Vyper也基本做了同樣的事情。
Solidityopcode解釋
現在讓我們看看Solidity的opcode。
四川法院在一起虛擬貨幣交易案件中判決借條無效且交易平臺停止造成的損失風險自擔:6月27日消息,近日,四川自由貿易試驗區人民法院開庭審理一起虛擬貨幣交易案件。該案中,王某向李某某轉款150萬元,約定由李某某在虛擬貨幣交易平臺投資,后因為虛擬貨幣交易價格下跌,李某某未能按期返還收益,王某限制其人身自由,讓李某某出具借條一份,約定李某某轉給王某一定數量的虛擬貨幣,作為最終在交易平臺出售實際金額以抵銷上述債務。李某某按約轉虛擬貨幣給王某后,因為交易平臺停止運營,王某只出售了部分虛擬貨幣并提現,剩余部分無法交易,故訴至法院,請求李某某償還剩余款項。
法院經審理認為,此案不符合一般民間借貸的邏輯和交易習慣,故認定雙方不存在借貸合意。其次,投資虛擬貨幣,是未經批準的非法投資行為,不能受到法律保護,剩余虛擬貨幣未予抵銷系因虛擬貨幣交易平臺停止,由此產生的風險和損失,應由王某自行承擔。故判決駁回王某的訴訟請求,后王某提出上訴,二審法院駁回上訴,維持原判。(央廣網)[2022/6/27 1:33:32]
Solidity做了更多的事情。Solidity做的第一件事是創建一個叫FreeMemoryPointer的東西。為了在內存中創建動態數組,你需要記錄內存的哪些部分是空閑可供使用的。我們不會在合約構造代碼中使用這個FreeMemoryPointer,但這是它在背后需要做的第一件事。這是語言之間的第一個主要區別:內存管理。每種語言處理內存的方式不同。
接下來,Solidity編譯器查看你的代碼,并注意到你的構造函數不是payable。因此,為了確保你不會在創建合約時錯誤地發送了ETH,它使用CALLVALUEopcode檢查以確保你沒有在創建合約時發送任何通證。這是語言之間的第二個主要區別:它們各自對常見問題有不同的檢查和保護。
最后,Solidity也做了其他語言所做的事情:它將你的合約發到在鏈上。
我們將跳過Solidity-Yul,它的工作方式與Solidity自身類似。
檢查和保護
從這個意義上說,Solidity似乎“更安全”,因為它比其他語言有更多的保護。但是,如果你要向Vyper代碼添加一個構造函數然后重新編譯,你會注意到一些不同之處。
Vyper語言的構造函數
編譯它,你的合約創建代碼看起來更像Solidity的。
它仍然沒有Solidity所具有的內存管理,但是你會看到它使用構造函數檢查callvalue。如果你使構造函數payable并重新編譯,則該檢查將消失。
因此,僅通過查看這些合約創建時的配置,我們就可以得出兩個結論:
在HuffandYul中,你需要自己顯性地寫檢查操作。而Solidity和Vyper將為你進行檢查,Solidity可能會做更多的檢查和保護。
這將是語言之間最大的權衡之一:它們在幕后執行哪些檢查?Huff和Yul這兩種語言不會在幕后做任何事情。所以你的代碼會更省gas,但你會更難避免和追蹤錯誤。
運行時代碼
現在我們對幕后發生的事情有了一定的了解,我們可以看看合約的不同函數是如何執行的,以及它們為何以這種方式執行。
讓我們看看調用storeNumber()函數,在每種語言中,它的值都為77。我通過使用像forgetest–debug“testStorageAndReadSol”這樣的命令使用ForgeDebugFeature來獲取opcode。我還使用了HuffVSCodeExtension。
Huffopcode解釋
有趣的是,如果我們沒有STOP操作碼,我們的Huff代碼實際上會添加一組opcode來返回我們剛剛存儲的值,使其比Vyper代碼更貴。不過這段代碼看起來還是很直觀的,那我們就來看看Vyper是怎么做的吧。我們暫時跳過Yul,因為結果會非常相似。
Vyperopcode解釋
可以看到在存儲值的同時做了一些檢查:
對于functionselector來說,calldata是否有足夠的字節?他們的value是通過call發送的嗎?calldata的大小和functionselector+uint256的大小一樣嗎?所有這些檢查都增加了我們的計算量,但它們也意味著我們更有可能不犯錯誤。
Solidityopcode解釋
這里有很多東西要解釋。這與Huff代碼之間的一些主要區別是什么?
我們設置了一個freememorypointer。我們檢查了發送的value。我們檢查了functionselector的calldata大小。我們檢查了uint256的大小。
Solidity和Vyper之間的主要區別是什么?
Freememorypointer的設置。Stack在某些時候要深度要大很多。這兩者結合起來似乎是Vyper比Solidity便宜的原因。同樣有趣的是,Solidity使用ISZEROopcode進行檢查,而Vyper使用XORopcode;兩者似乎都需要大約相同的gas。正是這些微小的設計差異造成所有的不同。
所以我們現在可以明白為什么Huff和Yul在gas上更便宜:它們只執行你告訴他們的操作,僅此而已,而Vyper和Solidity試圖保護你不犯錯誤。
FreeMemoryPointer
那么這個freememorypointer有什么用呢?Solidity與Vyper之間的gas消耗似乎存在很大差異。freememorypointer是一個控制內存管理的特性——任何時候你添加一些東西到你的內存數組,你的freememorypointer都只是指向它的末尾,就像這樣:
這很有用,因為我們可能需要將動態數組等數據結構加載到內存中。對于動態數組,我們不知道它有多大,所以我們需要知道內存在哪里結束。
在Vyper中,因為沒有動態的數據結構,你不得不說出像數組這樣的對象到底有多大。知道這一點,Vyper可以在編譯時分配內存,并且沒有freememorypointer。
這意味著在內存管理方面,Vyper可以比Solidity進行更多的gas優化。缺點是使用Vyper你需要明確說明你的數據結構的大小并且不能有動態內存。然而,Vyper團隊實際上將此視為一個優勢。
動態數組
暫且不談內存問題,使用Vyper確實必須聲明數組的邊界。在Solidity中,你可以聲明一個沒有大小的數組。在Vyper中,你可以有一個動態數組,但它必須是“有界的”。
這對開發人員體驗很不好,但是,在Web3中,這也可以被視為針對拒絕服務攻擊的保護措施,并防止你的函數中產生大量gas成本。
如果你的數組變得太大,并且你對其進行遍歷,則可能會消耗大量gas。但是,如果你顯性地聲明數組的邊界,你將知道最壞情況。
Solidityvs.Yulvs.SolYul
看看我上面的圖表,使用Solidity和Yul似乎是最糟糕的選擇,因為合約創建代碼要貴得多。這可能適用于較小的項目,因為Solidity做了一些操作來讓Yul運行,但大規模呢?
以Solidity版本和SolYul版本編寫的最受歡迎的項目之一是Seaport項目。
Seaport項目Logo
使用這些語言的最佳方面之一是你可以運行命令來直接從源代碼測試每個合約的gas使用效率。我們添加了一個PR來幫助測試純Solidity合約的gas消耗的命令,因為Sol-Yul合約已經進行了測試。結果非常驚人,你可以在gas-report.txt和gas-report-reference.txt中看到所有數據。
Seaport中合約創建gas消耗的差別
Seaport中函數調用gas消耗的差別
平均而言,函數調用在SolYul版本上的性能提高了25%,而合約創建的性能提高了40%。
這節省了大量的gas。我想知道他們在純粹的Yul中可以節省多少?我想知道他們在Vypervs.Sol-Yul中會節省多少?
Metadata
最后,Metadata。Vyper和Solidity都在合約末尾附加了一些額外的“Metadata”。雖然數量很少,但我們在這里的比較中基本上會忽略它。你可以手動將其刪除,但Solidity團隊也在建一個PR,你可以在編譯時將其刪除。
總結
以下是我對這些語言的看法:
如果你正在編寫智能合約,請使用Vyper或Solidity。它們都是高級語言,有檢查和保護,比如說檢查調用數據大小以及你是否在不應該的情況下不小心發送了ETH。它們都是很棒的語言,所以選擇其中一個并慢慢學習。如果你需要性能特別的高的代碼,Yul和Huff是很棒的工具。雖然我不建議大多數人用這些語言編程,但它們還是值得學習和理解,會讓你更好地了解EVM。Solidity和Vyper之間gas成本的主要區別之一是Solidity中的freememorypointer-一旦你達到高級水平并希望了解工具之間的潛在差異之一,請記住這一點。LookingForward
這些語言將繼續發展,我們也可能會看到更多的語言出現,比如Reachprogramminglanguage和fe。
Solidity和Vyper團隊致力于開發intermediaterepresentationcompilationstep。Solidity團隊有一個–via-ir的flag,這將有助于優化Solidity代碼,Vyper團隊也有他們的venom作為intermediaterepresentation。
無論你選擇哪種語言,你都可以編寫一些很棒的智能合約。祝編碼愉快!
AztecNetwork是以太坊上的第一個私有ZK-rollup,使去中心化應用程序能夠訪問隱私和擴展。Aztec的匯總由領先的零知識擴展項目使用的行業標準PLONK證明機制提供保障.
1900/1/1 0:00:00社區提問:我是今年八月份入圈新人,找不到方向無頭蒼蠅一樣,新人應該從哪塊學習?想在行業立足賺到錢,如何一步步達成?這個問題其實很有代表性,我寫了六步,很多新人進圈就開始合約,只關注刺激新聞行情.
1900/1/1 0:00:00主持人:各位能否先自我介紹一下?Kristof:我在2021年5月加入Nethermind,大約在去年11月的時候,我在想我接下來要在這個領域做什么.
1900/1/1 0:00:00常言道,熊市是建設的最佳時機。對于普通用戶而言,參與建設的最佳方式就是體驗各種項目,為項目的發展提出反饋與建議。如今,越來越多的項目也會將這部分用戶納入獎勵體系.
1900/1/1 0:00:00原文作者|OriginsNFT 原文編譯|白澤研究院 能源行業正面臨著來自環保人士、媒體和公眾的巨大壓力,要求他們將燃料生產轉向更環保的替代品.
1900/1/1 0:00:00盡管從標題的結構看起來像是枯燥乏味的論文,內容似乎是捕風捉影的小報,但確實沒有更加貼切的表述來概括本文.
1900/1/1 0:00:00