ethers-rs 后继者 Alloy 发布

  • Paradigm
  • 更新于 2024-06-22 22:38
  • 阅读 757

Alloy 是与基于 EVM 的区块链交互所需的所有实用工具

Alloy 到目前为止的故事

一年前,我们宣布了 Alloy,我们对与 EVM 区块链交互的低级构建模块进行了全面重写,从 RPC 类型到 ABI 编码器和原始类型。

今天,我们发布了 Alloy 0.1crates),这是 Alloy 项目的第一个版本,包括与基于 EVM 的区块链交互所需的所有实用工具。此版本包括多个 API 改进和新功能,我们很高兴终于能与用户分享这些内容。作为此版本的一部分,我们还正式停止维护 ethers-rs,并鼓励大家迁移到 Alloy

虽然我们现在才发布 Alloy 作为包含所有 JSON-RPC 功能的顶级包 ,但 Alloy Core 内部的低级类型和 ABI 编码器包已经发布了几个月,其中许多已经有超过 100 万次下载

为了帮助用户快速上手 Alloy,我们发布了 Alloy Book(包括 ethers 到 alloy 的迁移指南 ),AlloyAlloy Core 文档,以及大量示例 ,展示了使用 Alloy 的各种方式。

Alloy 已经集成到许多 Rust 代码库中,以及我们正在开发的所有项目中:RevmFoundryReth。我们自己已经在生产环境中使用了几个月,我们很高兴它能成为 Rust 以太坊生态系统的高性能和稳定基础,正如我们在最初的公告中所设想的那样。

说到这里,让我们深入了解一下!

Alloy 的亮点是什么?

Alloy 从底层开始编写,旨在高性能、安全使用和直观。我们从 4 年的 ethers-rs 开发中汲取了经验,并将其全部投入到设计一个与 EVM 区块链交互的优秀客户端库中。

这花费了很多时间,自我们宣布 Alloy 项目以来已经过去了一年,但我们为这个结果感到自豪。

今天我们很高兴揭示一些由我们的核心团队与 James Prestwich 共同架构和开发的令人兴奋的新功能,他在过去一年中与我们一起维护 Alloy。特别感谢 James 推动我们超越 ethers-rs 之前的限制,并为今天发布的成功交付提供了核心输入。

重新设计的 Provider Middleware 架构

RPC 客户端库中最重要的抽象是 Provider,它让你可以与网络交互。用户通常希望扩展基本的提供者操作:不同的 gas 估算、gas 升级、替代签名管道等。你希望以模块化的方式来实现这一点,从而使开发人员能够扩展核心库的行为,而不是修改它。

在 ethers-rs 中,我们将 Middleware 定义为修改 RPC 客户端行为的一站式解决方案。如果你想要管理 nonce,它是一个中间件。Gas 升级,它是一个中间件。签名交易?中间件。Flashbots 捆绑?中间件。这种方法有效,因为它给了我们很多灵活性,可以根据需要覆盖内容,但它也太容易出错,并且开发者体验不佳(例如,当 Middleware trait 所需的函数没有意义时,期望开发者 panic)。

Alloy 从头开始重新设计了这种抽象。我们将 Middleware 分为 3 个可覆盖的抽象:

  • 传输层: Transport 是将数据传输到节点的“线”。它现在实现了 Tower 的服务层,允许访问 Tower 生态系统 的常见中间件,如请求重试、速率限制等。
  • 提供者层: 模仿 Tower 的层,这允许覆盖各种 Provider 功能,例如,如果你想劫持 get_storage_at 方法,你需要实现一个 [ProviderLayer](https://alloy-rs.github.io/alloy/alloy/providers/trait.ProviderLayer.html)
  • 填充器(Fillers): 这可能是最令人兴奋的抽象,填充器处理交易生命周期的所有内容。用户通常提交部分填充的交易,提供者负责找出缺失的数据并填充它们。填充器可以独立于顺序安装,解决了 ethers-rs 的一个主要问题。我们提供了 .with_recommended_fillers() 方法,通过多个 [JoinFill](https://alloy-rs.github.io/alloy/alloy/providers/fillers/struct.JoinFill.html) ProviderLayers 聚合常用的填充器,方便使用:
    • [Wallet Filler](https://alloy-rs.github.io/alloy/alloy/providers/fillers/struct.WalletFiller.html):使用广泛套件中的凭证签署交易:从十六进制字符串解析的本地私钥、12/24 字助记词、密钥库文件、硬件钱包如 Trezor 或 Ledger,甚至是生产化的密钥管理系统如 YubiHSM、AWS KMS 或 GCP KMS。
    • [Nonce Filler](https://alloy-rs.github.io/alloy/alloy/providers/fillers/struct.NonceFiller.html):自动管理所有账户的 nonce。
    • [Gas Filler](https://alloy-rs.github.io/alloy/alloy/providers/fillers/struct.GasFiller.html):处理每笔交易的 gas 价格和 gas 限制的计算。
    • [ChainId Filler](https://alloy-rs.github.io/alloy/alloy/providers/fillers/struct.ChainIdFiller.html):根据连接的链将正确的链 ID 嵌入交易中。将所有内容整合在一起,我们拥有的功能堆栈如下所示:

Image 1

这个堆栈通过 ProviderBuilder 捆绑并暴露给用户,它有一个非常符合人体工程学的 API:

// 从随机私钥创建一个签名者。
let signer = PrivateKeySigner::random();

let provider = ProviderBuilder::new()
   // 配置所有填充器
  .with_recommended_fillers()
   // 设置签名者,允许配置多个签名者,这些签名者将根据你的交易的`from`字段进行选择。
  .wallet(EthereumWallet::from(signer))
  // 连接到链
  // 也可以使用`.on_http`、`.on_ws`或`.on_ipc`来避免动态调度
  // 也可以使用`.on_anvil`进行本地测试
  // 也可以使用`.on_client`来配置认证选项,例如 bearer 认证。
  .on_builtin("ws://localhost:8545")
  .await?;

let tx = TransactionRequest::new().with_to(...).with_value(...);
let receipt = provider.send_transaction(tx).await?.get_receipt().await?;
// 使用收据做一些事情

哦,我们还确保了 Provider 和 Signers 是对象安全的,以便通过Box它们更容易避免代码中的泛型。有关如何使用 providers 的更多信息,请参阅书籍

RPC 类型抽象

世界正在走向多链,这意味着 RPC 类型的差异更多!这是 ethers-rs 中最痛苦的事情之一,我们通过一个特性标志支持例如 Celo 相关字段。这意味着如果你想要对 Celo 和 Ethereum 进行类型安全的连接,你必须在两次导入库之间进行选择,或者期望 Celo 字段在所有 Ethereum 情况下都为 None。这是我们要解决的问题。

在 Alloy 中,我们定义了[Network](https://alloy-rs.github.io/alloy/alloy/network/trait.Network.html)特性,它定义了每个网络的所有 RPC 请求和响应的“形状”,其中每种类型必须实现某些特性,例如[Eip2718Envelope](https://alloy-rs.github.io/alloy/alloy/network/eip2718/trait.Eip2718Envelope.html)[TxReceipt](https://alloy-rs.github.io/alloy/alloy/consensus/trait.TxReceipt.html)[TransactionBuilder](https://alloy-rs.github.io/alloy/alloy/network/trait.TransactionBuilder.html),总结如下:

pub trait Network {
    /// 网络交易类型枚举。
    type TxType
    /// 网络交易信封类型。
    type TxEnvelope: Eip2718Envelope + Debug;
    /// 各种交易类型的枚举。
    type UnsignedTx: From<Self::TxEnvelope>;
    /// 网络收据信封类型。
    type ReceiptEnvelope: Eip2718Envelope + TxReceipt;
    /// 网络头类型。
    type Header;
    /// 交易请求的 JSON 主体。
    type TransactionRequest: RpcObject + TransactionBuilder<Self> + Debug + From<Self::TxEnvelope> + From<Self::UnsignedTx>;
    /// 交易响应的 JSON 主体。
    type TransactionResponse: RpcObject + TransactionResponse;
    /// 交易收据的 JSON 主体。
    type ReceiptResponse: RpcObject + ReceiptResponse;
    /// 头响应的 JSON 主体。
    type HeaderResponse: RpcObject;
}

这使我们可以在没有任何特性标志的情况下导入库,并且根据我们指定的网络,我们会得到不同的类型抽象。这太棒了!类型安全,没有冗余开销!这也是我们在 Reth 中采用的方法,允许任何开发人员在服务器端构建具有自定义 RPC 类型的链,例如具有本地账户抽象的网络。

我们提供了两个网络实现[Ethereum](https://alloy-rs.github.io/alloy/alloy/network/struct.Ethereum.html)[AnyNetwork](https://alloy-rs.github.io/alloy/alloy/network/struct.AnyNetwork.html)。Ethereum 包含你熟知和喜爱的所有类型。然而,AnyNetwork将每个 RPC 类型包装在[WithOtherFields<T>](https://alloy-rs.github.io/alloy/alloy/rpc/types/struct.WithOtherFields.html)类型中,该类型充当任何不匹配 Ethereum 结构的 RPC 响应字段的捕获。

开发人员可以使用[.network::()](https://alloy-rs.github.io/alloy/alloy/providers/struct.ProviderBuilder.html#method.network)方法在[ProviderBuilder](https://alloy-rs.github.io/alloy/alloy/providers/struct.ProviderBuilder.html)上选择他们的Network实现。这使我们能够以一种有原则的方式支持比 Ethereum 更多的网络,而不会增加核心维护过程的负担。你只需实现网络特性及其所有关联类型,在代码中导入它,你就完成了!

关注我们在op-alloy crate 中定义[OpStackNetwork](https://github.com/alloy-rs/op-alloy/issues/10)的工作,如果你想实现自己的网络,请联系我们!

sol! 宏

我们在初始帖子中首次谈到了 sol 宏。它是我们之前[abigen](https://docs.rs/ethers/latest/ethers/contract/macro.abigen.html)宏的重构,用于生成与合约 ABI 的类型安全绑定。sol 宏不是编译器,但它是 Rust 中 Solidity 类型系统的完整表示,这意味着你只需粘贴 Solidity 代码,它就会为其生成绑定,甚至支持自定义类型!

sol 宏还通过#[sol(rpc)]属性生成 JSON RPC 绑定。如果你传递#[sol(bytecode = "...")]属性,还会生成一个部署方法。例如,下面的代码将生成一个Counter::deploy函数以及一个Counter::increment(&self)方法,你可以用它来增加计数器。

sol! {
    // solc v0.8.26; solc a.sol --via-ir --optimize --bin
    #[sol(rpc, bytecode="608080...")]
    contract Counter {
        uint256 public number;

        function increment() public {
            number++;
        }
    }
}

要了解有关 sol 宏的更多信息,请查看书籍页面及其详细文档 。与智能合约交互类似于 ethers-rs(注意,不再有Arc!),在获取交易收据的 API 中有一些底层变化。

let provider = ProviderBuilder::new().on_builtin("...").await?;

// 部署合约。
let contract = Counter::deploy(&provider).await?;
println!("Deployed contract at address: {}", contract.address());

let receipt = contract.setNumber(U256::from(42)).send().await?.get_receipt().await?;
println!("Receipt: {receipt}");

// 将数字增加到 43(无需等待收据)
let tx_hash = contract.increment().send().await?.watch().await?;
println!("Incremented number: {tx_hash}");

// 检索数字,应该是 43。
let number = contract.number().await?.number.to_string();
println!("Retrieved number: {number}");

sol 宏的功能也与 Foundry 集成在一起,用于生成所有客户端集成的类型安全绑定。请查看更新的 Foundry Rust 模板,如果你对此感兴趣的话!

广泛的文档和测试

我们希望用户能够通过高级教程和示例来使用该项目,作为一个库。为此,我们提供了大量的文档:

  • Alloy 文档: Alloy 仓库中每个函数的 Rustdoc 文档。
  • Alloy Core 文档: Alloy Core 仓库中每个函数的 Rustdoc 文档。
  • Alloy 示例: 关于如何在日常中使用 Alloy 的各种代码示例。
  • Alloy 之书: 关于 Alloy 的教程和长篇文章。

让文档变得优秀是我们的首要任务,请在书籍上提出更多你想看到的教程。

Alloy 的下一步是什么?

今天的 0.1 版本发布标志着 Alloy 在功能上与 ethers-rs 相当甚至超越,同时也具有高性能、经过充分测试和良好文档记录。我们认为 Alloy 是高级用户的首选工具,但它也足够简单和直观,任何人都可以使用。

我们的下一个优先事项是 1.0 版本,这意味着我们将打磨我们的 API 并努力实现适当的稳定性。虽然我们不提供任何正式的稳定性保证,但大多数 API 已经成熟,我们不预计会有大的变化。

为了实现 Alloy 的长期成功,我们正在寻找一名全职的高级工程师加入 Alloy 团队,帮助推动项目的日常工作,并帮助我们扩大贡献者基础。如果上述内容对你有吸引力,请联系 georgios@paradigm.xyz

我们很高兴每个 ethers-rs 用户都能将他们的代码迁移到 Alloy,并用它构建新的服务!请阅读 alloy-corealloy 的文档、示例书籍

在那之前,Github 上见!

免责声明:本文仅供一般信息用途。它不构成投资建议或购买或出售任何投资的推荐或招揽,也不应在评估任何投资决策的优点时使用。它不应被依赖于会计、法律或税务建议或投资建议。本文反映了作者的当前意见,并非代表 Paradigm 或其附属机构的意见,也不一定反映 Paradigm、其附属机构或与 Paradigm 相关的个人的意见。本文所反映的意见如有变更,恕不另行通知。

本文由 AI 翻译,欢迎小伙伴们来校对

点赞 0
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Paradigm
Paradigm
Paradigm 是一家研究驱动型技术投资公司 https://www.paradigm.xyz/