本文探討以下問題:哪種智能合約語言更有優勢,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提升了智能合約的安全性和可審計性。
InsurAce Discord服務器遭入侵,請勿與其交互:8月2日消息,多鏈保險協議InsurAce.io Protocol發推提醒稱,今天早些時候發現有人未經授權訪問其Discord服務器,團隊正在努力處理該問題。請不要與其Discord服務器交互。[2023/8/2 16:14:03]
當前的情況
來源于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旨在成為一種通用的智能合約語言。編碼的體驗在語法層面上也是如此,但每個人肯定都有自己的主觀感受。
BitKeep Swap已支持zkSync Era:5月19日消息,多鏈錢包 BitKeep 發推稱,BitKeep Swap 現已支持 zkSync Era 上 token 單鏈、多鏈兌換,并提供 K 線行情查詢。BitKeep 表示會持續升級跨鏈服務,豐富跨鏈交易幣對,并正在配置 zkSync Era 借 Gas 交易功能,之后用戶可以在沒有 ETH 的情況下直接進行鏈上交易。[2023/5/19 15:14:01]
我不會過多地討論工具,因為大多數這些語言都有非常相似的工具。主流框架,包括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開發人員在寫更接近機器代碼時而創建的。
Web3數據倉庫平臺Space and Time正式推出測試版本:4月27日消息,Web3 數據倉庫平臺 Space and Time 正式推出數據倉庫和開發人員套件的測試版本,允許開發人員利用針對鏈上和鏈下數據的可證明計算來為 DApp、智能合約和可驗證的 AI 模型提供支持。[2023/4/27 14:29: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)返回并結束調用。
Tribe DAO第四次關于償還受害者的社區投票已通過:9月22日消息,Tribe DAO第四次關于償還受害者的社區投票已通過,決定償還4月份黑客攻擊中受害者被盜資金,最終用戶的付款將以FEI形式支付,以換取受影響Fuse池中的cToken,對于受影響的DAO和智能合約,將以DAI直接支付,總付款金額約為1268萬枚FEI和約2661萬枚DAI。[2022/9/22 7:13:11]
你還會注意到很多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,但這是它在背后需要做的第一件事。這是語言之間的第一個主要區別:內存管理。每種語言處理內存的方式不同。
Circle CEO:Circle目前在財務上處于歷史最佳水平,并將繼續提高透明度:7月4日消息,Circle創始人兼首席執行官Jeremy Allaire7月2日在其個人社交平臺上發布了一系列聲明并鏈接了博客文章,以澄清有關該公司發行的Stablecoin USDC在加密市場低迷期間幾近崩潰的傳言。
Allaire表示,Circle一直試圖讓自己達到所能承受的最高標準,以使得我們與監管機構、頂級保險公司和領先的金融機構合作,Circle目前在財務上處于有史以來最強大的水平,并將繼續提高透明度。[2022/7/4 1:48:47]
接下來,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。
無論你選擇哪種語言,你都可以編寫一些很棒的智能合約。祝編碼愉快!
隨著區塊鏈技術發展,主流金融機構的加入和投資者認知加深,虛擬資產快速崛起規模已近萬億美元,港交所也于日前批準港股虛擬資產期貨ETF發行.
1900/1/1 0:00:00注:在最近的一期?ZeroKnowledge播客中,主持人AnnaRose和KobiGurkan,以及來自斯坦福大學的計算機科學教授DanBoneh一起探索了最新的零知識研究以及去中心化網絡.
1900/1/1 0:00:00ConsenSys將從下周開始在內部測試其zkEVM「privateBeta」測試網。外部用戶將在2023年1月開始加入這個L2zk-rollup網絡.
1900/1/1 0:00:00MarsBitCryptoDaily2022年11月24日 一、今日要聞 ZkSync通過安全審計以太坊擴展協議ZkSync通過了第一次安全審計,準備在今年擴大對用戶的訪問范圍.
1900/1/1 0:00:00$Magic@Treasure_DAO上車一周年Recap1/N:BeaconPoweredbyTreasure最近Treasure生態的@The_Beacon_GG爆火.
1900/1/1 0:00:00昨天從幣安提走超過數十億美元。能夠跟蹤鏈上交易是加密玩家的超能力之一。這是一份快速指南,介紹如何使用免費工具自行監控交易所提款并成為鏈上偵探.
1900/1/1 0:00:00