本文探討以下問題:哪種智能合約語言更有優勢,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提升了智能合約的安全性和可審計性。
加密貨幣總市值為1.24萬億美元:金色財經報道,據CoinGecko數據顯示,當前加密貨幣市值為1,241,841,696,471美元,24小時交易量為37,059,463,204美元,當前比特幣市值為598,452,334,986美元,以太坊市值為230,934,822,487美元。[2023/6/25 21:58:49]
當前的情況
來源于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中汲取靈感。因此,如果你對于這幾種語言更熟悉的話,那就能很好地使用對應的智能合約語言。
Vyper旨在成為一種簡約、易于審計的編程語言,而Solidity旨在成為一種通用的智能合約語言。編碼的體驗在語法層面上也是如此,但每個人肯定都有自己的主觀感受。
一鯨魚在Aave上做多ETH:金色財經報道,據Lookonchain監測,一鯨魚在Aave上做多ETH,他在6月12日存入6000美元的ETH,借了600萬美元的USDT,并存入Binance。2小時前從Binance提取了3,622美元ETH,買入價為1,657美元。存款8,000美元,借入7百萬美元,2小時前再次存入Binance。[2023/6/16 21:42:04]
我不會過多地討論工具,因為大多數這些語言都有非常相似的工具。主流框架,包括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開發人員在寫更接近機器代碼時而創建的。
因此,在上圖中,我們比較了原始Yul、原始Solidity和Solidity-Yul組合。我們代碼的Solidity-Yul版本如下所示:
韓國檢方:Do Kwon的資產不在韓國:金色財經報道,韓國檢察官表示,Terraform Labs的9名前任和現任高管從該項目中非法籌集的4145億韓元(約合3.142億美元),其中約有914億韓元屬于該公司首席執行官Do Kwon,但確認他的資產都不在韓國管轄范圍內。
當地檢察官暫時沒收了屬于Terraform聯合創始人Shin Hyun-seung(又名Daniel Shin)的資產,包括房地產和進口汽車。該國檢方指控Daniel Shin從Terra-Luna項目中非法獲利超過1540億韓元。Do Kwon似乎將很大一部分財產換成比特幣,轉移到海外加密交易所。檢察官表示,他們已要求幣安阻止Do Kwon提取其加密貨幣。
此前消息,韓國檢方已搜查并扣押Terra高管和員工的總計超2億美元房產。[2023/4/7 13:49:59]
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)返回并結束調用。
烏克蘭大型電子產品零售店現接受比特幣支付:8月19日消息,Bitcoin Magazine發推稱,烏克蘭一些大型電子產品零售店現接受比特幣支付。[2022/8/19 12:36:27]
你還會注意到很多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。
Solidity做了更多的事情。Solidity做的第一件事是創建一個叫FreeMemoryPointer的東西。為了在內存中創建動態數組,你需要記錄內存的哪些部分是空閑可供使用的。我們不會在合約構造代碼中使用這個FreeMemoryPointer,但這是它在背后需要做的第一件事。這是語言之間的第一個主要區別:內存管理。每種語言處理內存的方式不同。
Just Women's Sports完成600萬美元融資,Dapper Labs參投:金色財經報道,女性體育媒體平臺 Just Women's Sports(JWS) 宣布完成 600 萬美元融資,阿里巴巴聯合創始人蔡崇信的家族辦公室藍池資本領投,網球明星Billie Jean King、Washington Spirit 老板Michele Kang、Bolt Ventures、SC Holdings、Muse Capital、NFT 開發商 Dapper Labs 等參投。JWS 主要構建原創播客系列、優質視頻節目、社交媒體、線上編輯內容,并提供獨家商品和現場活動組織服務,目前和全球20多個體育品牌合作,包括耐克、彪馬、安德瑪等。(雅虎財經)[2022/6/10 4:16:38]
接下來,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。
無論你選擇哪種語言,你都可以編寫一些很棒的智能合約。祝編碼愉快!
兩個月前,AnalyticsIndiaMagazine發表了一篇關于GPT-3如何悄悄挑戰Google搜索的報道.
1900/1/1 0:00:0012月5日,當前鏈上規模最大的無抵押貸款協議MapleFinance發布公告稱,因合作方?OrthogonalTrading?對其財務狀況存在重大陳述失實,致使協議面臨巨額違約風險.
1900/1/1 0:00:00密碼學和加密技術在今天的互聯網上得到了一定程度的信任,它們讓人們可以放心地使用密碼,而不必擔心是否有人能夠監控我們的連接并竊取它們。這些現有的信任原語非常有用,但它們的用途也很單一.
1900/1/1 0:00:00"一個新的世界,一個充滿無限可能的領域,一個虛擬現實的烏托邦。"你覺得這是在形容哪個項目的元宇宙世界?我個人覺得至少不是扎克伯格讓Meta做出來的那堆垃圾.
1900/1/1 0:00:00隨著過去幾年以太坊上金融活動的加速,那些確保網絡安全的人也有了獲利機會。當我們去年7月探討這個話題時,MEV軍備競賽正在進行,隨著以太坊在9月過渡到PoS,競賽愈演愈烈.
1900/1/1 0:00:00有關區塊鏈黑暗面的介紹,最早出現在Reddit上一篇題為“MinersFrontrunning”的帖子中,在這篇具有歷史意義的文章中,基于以太坊內存池的公開性.
1900/1/1 0:00:00