Cointime

扫码下载App
iOS & Android

Solidity与Vyper | EVM智能合约语言的比较

项目方

本文作者:Patrick Collins;编译:Cointime Freya

我们比较了最流行的智能合约语言Solidity和Vyper的gas优化、开发者体验等,并使用Huff和Yul作为基准。

简介

最近,有很多关于“最好的”智能合约语言的争论,每一方的maxis都主张他们所选择的语言。

我在这里要回答这场争论的本质问题:我应该使用哪种智能合约语言?

为了弄清问题的本质,我们将首先讨论工具和可用性,然后再考虑智能合约开发者面临的一个主要问题之一:gas优化。具体来说,我们将研究四种EVM语言(在以太坊、Avalanche、Polygon等链上工作的语言):Solidity、Vyper、Huff和Yul。至于Rust,抱歉,你需要等待一篇涵盖非EVM链的文章。

但首先,剧透一下。

Solidity、Vyper、Huff和Yul都是可以完成工作的优秀语言。Solidity和Vyper是大多数人应该使用的绝妙的高级语言。如果您对编写Assembly代码感兴趣,Yul和Huff就可以胜任。

所以,如果你纠结于选择哪一种语言,那就抛硬币吧:我保证,无论你选择哪种语言,你都会成功。如果你是智能合约编程语言的新手,你可以用你最喜欢的或随机选择的语言做一些很棒的事情。

此外,这些语言不断变化,你可以很容易地挑选智能合约和数据,使不同的语言看起来更好或更差。当我们进行gas优化比较时,请记住这一点。我们选择了一个最小的合约进行比较,如果你认为你有一个更好的例子,我们很乐意看到它!

现在,让我们深入了解这些语言吧!

玩家

我们将要研究的四种语言如下:

  • Solidity:目前DeFi TVL最受欢迎的高级别语言,类似于JavaScript;
  • Vyper:目前DeFi TVL排名第二的最受欢迎的高级别语言,类似于Python;
  • Huff:一种类似于Assembly的低级语言;
  • Yul:一种类似于Assembly的低级语言,内置于Solidity中(尽管有人认为它仍然太高级)。

为什么是这四种语言?

我们使用这四种语言是因为它们都与EVM兼容,其中Solidity和Vyper是最受欢迎的两种语言。我添加了Yul,是因为在不考虑Yul的情况下,与Solidity进行gas优化比较是不公平的。我们添加了Huff,是因为我们想以一种与用操作码编写几乎相同但不是Yul的语言作为基准。

就EVM而言,在Vyper和Solidity之后,第三、四、五名的受欢迎程度大幅下降。对于那些没有进入这种比较的语言,采用率还并不高。然而,许多有前途的智能合约语言正在崛起,我期待着在未来尝试它们。

目前的情况

来自DefiLlama

根据DefiLlama的数据,在DeFi领域,Solidity智能合约保证了87%的TVL,而Vyper智能合约保证了8%。

因此,如果您正在寻找纯粹的受欢迎程度,那么你只选择Solidity就够了。

比较相同的合约

现在让我们来了解一下每种语言的外观,然后比较它们的gas性能。

以下是用每种语言编写的四份几乎几乎相同的合约,用每种语言写成。它们都做着类似的事情,它们都:

  1. 在存储槽0处有一个私人号码(uint256);
  2. 有一个带有readNumber()函数签名的函数,来读取存储槽0上的内容;
  3. 允许你使用storeNumber(uint256)函数签名更新这个数字。

就是这样,下面是这些合同。

我们用来比较语言的所有代码都位于这个 GitHub 存储库中。

Solidity

Solidity代码

Vyper

Vyper代码

Huff

Huff代码

Yul

Yul代码

开发者体验

仅仅通过观察这四张图片,我们就可以大致了解每种语言的编写感受。就开发者的经验而言,编写Solidity和Vyper的代码要快很多。 这很有意义:这些语言是更高级别的,而Yul和Huff则是低级代码。仅仅因为这个原因,我们就很容易理解为什么这么多人采用Vyper和Solidity(以及它们各自存在的时间更长的事实)。

关注一下Vyper和Solidity,你可以看到Vyper从Python中汲取灵感,Solidity从JavaScript和Java中汲取灵感。所以,如果你喜欢其中一种语言的感觉,那很好——请使用它。

Vyper旨在成为一种简洁、易于审计的编程语言,而Solidity则旨在成为一种通用智能合约语言。在语法层面上,编程的体验肯定也有类似的感觉,但我会让你在这个主观问题上做出决定。

我不会过多地讨论工具问题,因为这些语言大多都有类似的工具。大多数的主要框架,包括Hardhat、ape、Titanoboa、Brownie和Foundry,都有Vyper和Solidity支持。Solidity在大多数框架中都具有“优先公民身份”,而Vyper则需要使用插件来使用Hardhat等工具。然而,Titanoboa是专门为Vyper工作而构建的,并且大多数工具都很容易使用,可以与Vyper一起使用。

哪种智能合约语言的gas优化程度更高?

现在是最重要的事情就是,在比较智能合约的gas性能时,需要牢记两点:

  1. 合约创建gas成本;
  2. 运行时gas成本。

你需要如何实现智能合约才会对这些因素产生重大影响?例如,你可以在你的合约代码中存储大量数组,从而使部署成本更高,但运行函数的成本较低。或者,你可以让你的函数动态生成数组,从而使合约的部署成本更低,但运行成本更高。

那么,现在让我们看看这四个合约,并将它们的合约创建gas成本与运行时的gas成本进行比较。你可以在我的 sc-language-comparison repo 中找到关于这方面的所有代码,包括用于比较它们的框架和工具。

gas成本比较总结

下图展示了我们是如何为本节编译合约的:

注意:我也可以在Solidity的编译中使用-via-ir标志。还要注意的是,Vyper和Solidity在其合同的末尾添加了“元数据”。这在整体gas成本中占了一小部分,但还不足以改变下面的排名。我将在元数据部分详细讨论这一点。

结果:

1. 创建成本

正如我们所见,像Huff和Yul这样的低级语言比Vyper和Soliditygas的效率更高,但这是为什么呢?Vyper似乎比Solidity更有效率,而我们有这个新的“Sol和Yul”部分。那是因为你可以在“Solidity里面写Yul”。Yul是作为Solidity开发者在需要更接近机器代码时编写的一种语言而创建的。

所以在上面的图表中,我们比较了原始的Yul,原始的Solidity,以及Solidity-Yul组合。我们代码的Solidity-Yul版本如下所示:

2. SOLYUL

稍后你会看到这样一个例子,其中inline-Yul在gas成本上产生了主要的差异。稍后我们将研究为什么存在这些gas差异,但现在,让我们先来看看与Foundry中的单个测试相关的gas成本。

3. Foundry测试

这将测试:把数字77存储在存储器中,然后从存储器中读取该数字的gas成本。下面是运行这个测试的结果:

4. 运行时的gas成本

我们没有Yul的数据,因为我们必须做一个Yul-Foundry插件,我不想这样做,而且我敢打赌结果会与Huff相似。请记住,这是运行整个测试函数的gas成本,而不仅仅是单个函数。

gas成本比较

现在让我们分析一下这些数据。我们需要回答的第一个问题是。为什么Huff和Yul的合约创建比Vyper和Solidity的gas效率高那么多?我们可以通过直接查看这些合约的字节码来找到答案。

当你编译一个合约时,它通常被分成两个或三个不同的部分:

  1. 合约创建代码;
  2. 运行时代码;
  3. 元数据(可选)

对于这一部分,了解操作代码的基本知识很重要。OpenZeppelin关于解构合约的博客就是一个很好的起点。

合约创建代码

合约创建代码是字节码的第一部分,它引导EVM将该合约粘贴在链上。你通常可以通过在生成的二进制文件中寻找CODECOPY操作码(39),然后查找它在链上的位置,然后找到它把它粘在链上并使用RETURN操作码(f3)返回,然后结束调用。

您还会注意到很多fe操作码,这是INVALID操作码。Solidity添加这些作为标记以显示运行时、契约创建和元数据代码之间的差异。f3f3是RETURNRETURN操作码,通常是函数或上下文的结束。

您可能会认为,因为Yul-Solidity拥有最大的合约创建字节码,而Huff拥有最小的合约创建字节码,这就是为什么Huff最便宜而Yul-Solidity最贵的原因。但是当你复制整个代码库并将其粘贴到链上时,代码库的大小会产生很大的差异,并且是主要的决定因素。然而,这段合约创建代码确实让我们了解了这些编译器各自的思维方式,并将让我们深入了解它们是如何编译我们的合约的。

如何读取操作码和堆栈

现在,EVM是一个基于堆栈的机器,这意味着你所做的大部分“事情”都是从堆栈中推送和拉取内容。你会看到左边是操作码,右边是两个斜杠(//),表示它们是注释,以及在同一行执行操作码后堆栈的样子,堆栈的顶部在左边,堆栈的底部在右边。

Huff 解释

Huff合约的创建只是做了它能做的最简单的事情。它抓取你编写的代码,并将其返回到链上。

Yul 解释

Yul也是这样做的,它使用一些不同的操作码,但基本上,它只是把你的代码放在链上,并尽可能减少操作码,以及一个INVALID操作码。

Vyper解释

Vyper也是一样的。

Solidity解释

现在让我们来看看Solidity操作码。

Solidity做了很多事情。Solidity做的第一件事是创建一个所谓的自由内存指针。为了在内存中创建动态数组,你需要追踪内存中的哪些部分可以自由使用。我们不会在我们的合约创建代码中使用这个自由内存指针,但它总是最先做这件事。这是我们发现的语言之间的第一个主要差异之一:内存管理。每种语言处理内存的方式都不同。

接下来,Solidity编译器会查看你的代码,并注意到你没有指定要支付的构造函数。因此,为了确保你不会自投罗网——在创建合约时意外地发送了ETH,它会使用CALLVALUE操作码进行检查,以确保你在创建合约时没有发送任何代币。这就引出了语言之间的第二个主要区别:对于常见问题,它们有不同的检查和保护措施。

最后,Solidity做了其他语言所做的事情:它把你的合约粘在链上。

我们将跳过Solidity- yul,它的工作方式与solididity类似。

检查和保护

从这个意义上讲,Solidity似乎更“安全”,因为相比其他语言,它有更多的保护措施。但是,如果向Vyper代码添加一个构造函数,然后重新编译,你就会注意到以些不同之处。

1. Vyper与构造函数

编译后,你的合约创建代码会看起来更像Solidity的代码。

它仍然没有 Solidity 所具有的内存管理,但是你会看到它使用构造函数检查CALLVALUE。 如果让构造函数可支付并重新编译,则该检查将再次消失。

因此,通过观察这些合约创建设置,我们可以得出两个结论:

  • 在Huff和Yul中,你需要明确检查并自己编写。
  • Solidity和Vyper将为你做检查,Solidity可能会做更多的开箱即用。

这将是语言之间最大的权衡之一:他们在底层执行进行了哪些检查?用Huff和Yul编写会更有效,因为这两种语言都不打算在底层做任何事情。因此,你的代码当然会更节省gas,但是对你来说跟踪所有正在发生的事情会更困难。

运行时代码

现在我们已经对底层的工作有了一些的了解,我们可以看看合约的不同功能是如何执行的,以及为什么它们会以这样的方式执行。

让我们看看调用storeNumber()函数,每种语言的值都是77。我通过使用像Forge test -debug“testStorageAndReadSol”这样的命令通过Forge调试功能来获得操作码。我还使用了Huff VSCode扩展。

Huff解释

有趣的是,如果我们没有STOP操作码,我们的Huff代码实际上会增加一组操作码来返回我们刚刚存储的值,这使得它比我们的Vyper代码更昂贵。但是这段代码看起来还是非常简单的,所以让我们看看Vyper是如何做到这一点的。现在,让我们暂时跳过Yul,因为其结果会非常相似。

Vyper解释

我们可以看到,我们在存储值的同时还做了一些检查:

调用数据是否有足够的字节用于函数选择器?

它们的价值是否与调用一起发送?

调用数据的大小是否是函数选择器+uint256的大小?

所有这些检查都给我们的计算增加了gas,但这也意味着我们更有可能避免搬起石头砸自己的脚。

Solidity解释

这里有很多东西需要解读。这与Huff代码之间的主要区别是什么?

  • 我们设置了一个自由内存指针;
  • 我们检查了发送的值;
  • 我们检查了函数选择器的调用数据大小;
  • 我们检查了 uint256 的大小。

Solidity和Vyper之间的主要区别是什么?

  • 自由内存指针设置;
  • 在某些时候,堆栈要深得多。

这两者结合起来似乎是Vyper比Solidity便宜的原因。同样有趣的是,Solidity使用ISZERO进行检查,而Vyper使用XOR;但两者似乎都需要相同的gas。正是这些设计上的细微差别造就了所有的不同!

所以我们现在可以明白为什么Huff和Yul在gas上更便宜:它们非常会明确地按照你的指令运行,但仅此而已,而Vyper和Solidity试图保护你不做错事。

自由内存指针

那么这个自由内存指针有什么作用呢?Solidity与Vyper之间的气gas消耗似乎存在很大的差异。自由内存指针是一个控制内存管理的功能——无论你想什么时间向内存数组添加东西,你的自由内存指针都只会指向它的末端,如下图所示:

1. Solidity内存

这很好,因为我们可能需要将动态数组等数据结构加载到内存中。对于动态数组,我们不知道它有多大,所以我们需要知道内存的结束位置。

在Vyper中,如果没有动态数据结构,你就不得不说出像数组这样的对象到底有多大。了解了这一点,Vyper就可以在编译时分配内存,并且没有自由内存指针。

2. Vyper内存

这意味着在内存管理方面,Vyper可以比Solidity更加gas优化。缺点是使用Vyper时,你需要明确说明你的数据结构的大小,并且不能有动态内存。然而,Vyper团队实际上却将此视为一个优势。

动态数组

抛开内存的问题不谈,使用Vype确实需要声明数组的边界。在Solidity中,你可以声明一个没有大小的数组。在Vyper中,你可以有一个动态数组,但它必须是“有界的”。

这可能会让开发者感到沮丧,然而,在Web3中,这也可以被看作是对拒绝服务攻击的保护措施,并防止在你的函数中产生大量的gas成本。

如果你的数组变得太大,并且你需要对其进行迭代,则可能会消耗大量gas。但是,如果你明确说明了数组的边界,你就会将清楚地知道智能合约在最坏情况下的性能将是什么。

Solidity vs. Yul vs. SolYul

看看我上面的图表,用Solidity和Yul工作似乎是最糟糕的选择,因为合约创建代码更昂贵。这可能适用于小型项目,因为Solidity做了一些操作才能让Yul运行起来,但如果是大型项目呢?

用Solidity版本和SolYul版本编写的最受欢迎的项目之一就是Seaport项目。

Seaport标志

使用这些语言的最方便的一点就是,你可以直接从源代码运行命令来测试每个合约的gas有效性。我们添加了一个拉取请求来帮助测试纯Solidity合约的gas成本的命令,因为Sol-Yul合约已经进行了测试。这样做的结果是非常惊人的,你可以在gas-report.txt和gas-report-reference.txt中看到所有的数据。

Seaport的gas差异

平均而言,函数调用在SolYul版本上的性能提高了25%,而合约创建的性能提高了40%。

这节省了大量的gas。我想知道他们在纯粹的Yul中可以节省多少?我想知道他们在Vyper vs. Sol-Yul又能节省多少?

元数据

最后是元数据。Vyper和Solidity都在合约末尾附加了一些额外的“元数据”。这是一个很小的量,我们基本上会忽略它在这里的比较。你可以手动删除它(并为你的Solidity代码的长度调整标记),但Solidity团队也在开发一个PR,你可以在编译时将它删除。

以下是我对这些语言的看法:

总结

如果你正在编写智能合约,请使用Vyper或Solidity。它们都是高级语言,可以通过查看调用数据大小以及是否在不应该发送ETH的时候不小心发送了ETH,来防止你搬起石头砸自己的脚。它们都是很棒的语言,所以可以选择其中一种并从中获得乐趣。

如果您需要超级特别高性能的代码,Yul和Huff都是非常棒的学习资源或工具。我不建议大多数人使用这些语言写作,但我认为它们都非常值得学习和理解。它们都会让你更好地了解 EVM。

Solidity和Vyper在gas成本上的主要区别之一是Solidity中的自由内存指针——一旦你达到了高级水平,并希望了解工具之间的潜在差异之一,那么请记住这一点。

展望未来

这些语言将继续发展,我们可能也会看到更多的语言出现,比如Reach编程语言和fe。

Solidity和Vyper团队致力于中间表示编译步骤。Solidity团队在生产中有一个via -ir标志,这将有助于优化Solidity代码,而Vyper团队也有他们的venom用于中间表示。

无论你选择哪种语言,你都可以编写一些很棒的智能合约。编码快乐!

*本文由CoinTime整理编译,转载请注明来源。

评论

所有评论

推荐阅读

  • Vitalik:PoW也相当中心化,PoW只是转向PoS之前的临时阶段

    以太坊联合创始人Vitalik Buterin在社交平台上表示,PoW也相当中心化。只是没有被过多地讨论,因为每个人都知道这只是转向 PoS 之前的临时阶段。 这甚至没有涉及到如何可能主要避免 ASIC,只因为即将到来的 PoS 转换意味着没有动机去建造它们

  • 美SEC起诉比特币矿企Geosyn,指控其创始人欺诈560万美元

    SEC 针对比特币矿企 Geosyn Mining 及其联合创始人提起诉讼,指控其谎报运营中的加密挖矿设备数量,同时将客户资金用于个人开支,从而诈骗投资者 560 万美元。

  • 香港现货虚拟资产ETF如果出现溢价而后卖出,可在港交所场内换成港币

    目前可以通过打新方式认购香港比特币ETF的券商(PD/分销商)只有少数几家有虚拟资产零售牌照的香港券商,而在ETF正式登录港交所后,所有几百家香港券商和银行均可购买。获批的虚拟资产ETF采用跟随芝商所CF比特币指数(亚太收市价)表现的方式,因此现金认购比特币ETF的盈亏风险与直接购买比特币风险基本一致。而由于比特币和比特币ETF之间是固定兑换比例,若是在IOP阶段使用实物认购,即以比特币认购比特币ETF,相关ETF在上市后如果出现溢价而后卖出,则可在港交所场内换成港币,再同时买回比特币即可赚取场内场外差价。(财华社)

  • Arthur Hayes:美财长耶伦或将推出天量流动性注入计划,加速加密牛市回归

    4月26日消息,BitMEX联合创始人Arthur Hayes在社交媒体发文表示,随着美国财政部预期税收将为财政总账户(TGA)增加约2000亿美元,美国财政部长JanetYellen有望在下周公布2024年第二季度的国债发行计划时为市场注入大量流动性。 Hayes提出了三种可能的选择:一是停止发行国债,将TGA降至零,这将为市场注入1万亿美元的流动性;二是将更多借款转移到短期国库券,从而从隔夜逆回购工具(RRP)中抽走4000亿美元,为市场注入流动性;三是结合前两种做法,不发行长期债券,只发行短期国库券,同时消耗TGA和RRP,这将为市场注入1.4万亿美元的流动性。

  • SEC起诉比特币矿企Geosyn,指控其创始人欺诈560万美元

    4月26日消息,美国SEC针对比特币矿企Geosyn Mining及其联合创始人提起诉讼,指控其谎报运营中的加密挖矿设备数量,同时将客户资金用于个人开支,从而诈骗投资者560万美元。

  • 港交所将于4月30日开始交易嘉实基金的比特币和以太坊现货ETF

    市场消息:港交所将于4月30日开始交易嘉实基金(Harvest)的比特币和以太坊现货ETF。

  • 稳定币总市值超1580亿美元,USDT市占率为69.8%

    DefiLlama 数据显示,稳定币总市值达 1581.97 亿美元,7 日增幅为 0.16%。其中 UDST 市值为 1104.26 亿美元,市占率达 69.8%。

  • CARV宣布完成1000万美元A轮融资,OKX Ventures 参投

    CARV 宣布完成 1000 万美元 A 轮融资,此轮融资由 Tribe Capital 和 IOSG Ventures 领投,Consensys、OKX Ventures、Fenbushi Capital、No Limit Holdings、Draper Dragon、Arweave、ARPA、MARBLEX 等参投,旨在打造最大的游戏和人工智能模块化数据层、以及最大限度地推动数据创新的同时,确保个人用户可以在互联网分享中获得价值。

  • 萨尔瓦多官方比特币钱包反驳其被黑客攻击的谣言

    萨尔瓦多官方加密货币钱包 Chivo Wallet 否认了有关其软件源代码和与 KYC 程序相关的 500 多万用户数据遭到黑客攻击的报道。该钱包的管理部门澄清道,其数据安全性没有受到损害,泄露数据并非来自他们的系统。此外,Chivo 表示,唯一泄露的文件来自一台于 2023 年 3 月 21 日被盗的 Chivo ATM 机。这些文件包含的信息与 ATM 机的操作严格相关,不包括任何个人用户数据。(fFnance Feeds)此前 4 月初消息,据黑客称,自上周六以来,超过 500 万萨尔瓦多人的个人信息遭泄露,成为该国历史上最严重的数据安全事件。被盗信息包括姓名、生日、电话、地址等,甚至高清无水印的头像也一并曝光。这一泄露牵涉到绝大多数萨尔瓦多成年人。泄露数据从 8 月起以 250 美元售价在暗网流通,但据称黑客未能收到赎金,因此决定免费发布数据。

  • 支付巨头Stripe今夏将接受USDC在线支付

    支付处理商 Stripe 从今年夏天开始将允许使用其技术的公司接受USDC进行在线支付,Stripe 最初将支持以太坊、Solana 和 Polygon 网络上的 USDC 交易。