文章/教程 Solana@Web3.js-2.0:新特性

clbigdata · 2024年11月30日 · 124 次阅读

2.0 版新增功能

Solana JavaScript SDK 2.0 版,是在使用 web3.js 开发 Solana 应用程序时,对许多痛点进行了优化。

Tree-Shakability

web3.js(1.x)API 的面向对象设计使优化编译器无法从生产构建中,“tree-shake”未使用的代码。无论您在应用程序中使用了多少 web3.js API,到目前为止,都必须将其全部打包。

在这里了解有关 tree-shaking 的更多信息:

一个 API 不能是树 shaken 的例子是 Connection 类。它有几十个方法,但因为它是一个类,你别无选择,只能将每个方法都包含在应用程序的最终包中,无论你实际使用了多少方法。

微小的 JavaScript 捆绑包可能会导致部署到 Cloudflare 或 AWS Lambda 等云计算提供商时出现问题。由于下载和 JavaScript 解析时间较长,它们还会影响 Web 应用程序的启动性能。

2.0 版本是完全树抖动的,并将通过构建时检查强制执行。优化编译器现在可以消除应用程序不使用的库部分。

新库本身由@solana组织下的几个较小的模块化包组成,包括:

  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/accounts</font>:用于获取和解码账户
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/codecs</font>:用于从一组原语中编写数据(反)序列化器或构建自定义数据(反)序列化器
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/errors</font>:用于识别和细化<font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana</font>命名空间中抛出的编码错误
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc</font>:用于发送 RPC 请求
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc-subscriptions</font>:用于订阅 RPC 通知
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/signers</font>:用于构建消息和/或交易签名者对象
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/sysvars</font>:用于获取和解码 sysvar 帐户
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/transaction-messages</font>:用于构建和转换 Solana 交易消息对象
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/transactions</font>:用于编译和签署交易以提交给网络
  • 还有更多!

其中一些包本身由较小的包组成。例如,由(针对核心 JSON RPC 规范类型)、(针对 Solana 特定的 RPC 方法)、(针对默认 HTTP 传输)等<font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc</font>组成。<font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc-spec</font><font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc-api</font><font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc-transport-http</font>

开发人员可以使用主库()中的默认配置<font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/web3.js@next</font>,或者导入任何需要通过组合进行定制的子包。

可组合内部构件

根据您的用例和对某些应用程序行为的容忍度,您可能希望将您的应用程序配置为与其他开发人员进行不同的权衡。web3.js(1.x)API 对所有开发人员强加了一套严格的通用默认设置,其中一些是不可能改变的。

到目前为止,无法自定义 web3.js 一直是令人十分伤心的原因:

Mango 团队希望定制交易确认策略,但所有这些功能都隐藏在 confirmTransaction 后面——一种静态的 Connection 方法。这是 GitHub 上 confirmTransaction 的代码

Solana 开发人员'mPaella'希望我们在 RPC 中添加一个功能,在主 URL 失败的情况下,该功能将故障转移到一组备份 URL。

Solana 开发人员“epicface”希望在 RPC 传输中为自动时间窗口批处理提供一流的支持。这是他们的拉取请求。

许多人表示需要为失败的请求或事务定制重试逻辑。这是来自“dafyddd”的 pull 请求和来自“abrkn”的另一个 pull 请求,它们试图修改重试逻辑以适应各自的用例。

2.0 版本暴露了更多的内部内容,特别是在与 RPC 通信的情况下,并允许有意愿的开发人员从默认实现中组合新的实现,这些实现体现了几乎无限的自定义数组。

组成 web3.js 的各个模块以默认配置组装,让人联想到作为 npm 包@solana一部分的遗留库/web3.js@next,但那些希望以不同配置组装它们的人可以这样做。

通用类型在许多地方提供,允许您指定新的功能,通过组合和超类型对每个 API 进行扩展,并鼓励您创建自己的更高层次的固执己见的抽象。

事实上,我们希望你这样做,并将其中一些开源,供其他有类似需求的人使用。

现代 JavaScript;零依赖

现代 JavaScript 功能的进步为加密应用程序的开发人员提供了机会,例如使用本机 Ed25519 密钥并将大值表示为本机 bigint 的能力。

Web 孵化器社区小组主张在 Web Crypto API 中添加 Ed25519 支持,并且大多数现代 JavaScript 运行时都已获得支持。

引擎对 bigint 值的支持也变得司空见惯。JavaScript 中较旧的数字基元的最大值为 2^53-1,而 Rust 的 u64 可以表示高达 2^64 的值。

2.0 版本消除了 Ed25519 密码学、大量 polyfills 等的用户空间实现,转而支持自定义实现或使用原生 JavaScript 功能,从而减小了库的大小。它不依赖于第三方。

功能架构

web3.js(1.x)的面向对象、基于类的架构导致了不必要的包膨胀。你的应用程序别无选择,只能捆绑一个类的所有功能和依赖关系,无论你在运行时实际使用了多少方法。

基于类的架构也给触发双包危险的开发人员带来了独特的风险。这描述了如果你同时为 CommonJS 和 ES 模块构建,你可能会遇到的情况。当依赖树中存在同一类的两个副本时,就会出现这种情况,导致 instanceof 等检查失败。这引入了加重和难以调试的问题。

阅读更多关于双包装危险的信息:

NodeJS:双包危险

2.0 版本没有实现任何类(SolanaError 类除外),并在函数边界实现了尽可能薄的接口。

统计数据

2.0 版和旧版 1.x 之间的统计对比。

1.x (Legacy) 2.0 +/- %
Total minified size of library 81 KB 57.5 KB -29%
Total minified size of library (when runtime supports Ed25519) 81 KB 53 KB -33%
Bundled size of a web application that executes a transfer of lamports 111 KB 23.9 KB -78%
Bundled size of a web application that executes a transfer of lamports (when runtime supports Ed25519) 111 KB 18.2 KB -83%
Performance of key generation, signing, and verifying signatures (Brave with Experimental API flag) 700 ops/s 7000 ops/s +900%
First-load size for Solana Explorer 311 KB 228 KB -26%

重新设计的库在很大程度上通过使用现代 JavaScript API 实现了这些加速和捆绑包大小的减少。

为了验证我们的工作,我们在 Solana Explorer 的主页上用新的 2.0 库替换了旧的 1.x 库。在不删除任何功能的情况下,首次加载包的总大小下降了 26%。如果你想深入了解,这里有一个 Callum McIntyre 的 X 线程。

2.0 版 API 概览

以下是如何使用新库与 RPC 交互、配置网络传输、使用 Ed25519 密钥以及序列化数据的概述。

RPC

2.0 版附带了 JSON RPC 规范的实现和Solana JSON RPC的类型规范。

负责管理与 RPC 通信的主要包是<font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc</font>。但是,此包利用更细粒度的包将 RPC 逻辑分解为更小的部分。即,这些包是:

  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc</font>:包含与发送 Solana RPC 调用相关的所有逻辑。
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc-api</font>:使用类型描述所有 Solana RPC 方法。
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc-transport-http</font>:提供使用 HTTP 请求的 RPC 传输的具体实现。
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc-spec</font>:定义用于发送 RPC 请求的 JSON RPC 规范。
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc-spec-types</font><font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc</font>:和两者使用的共享 JSON RPC 规范类型和帮助程序<font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc-subscriptions</font>(在下一节中描述)。
  • <font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc-types</font><font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc</font>:和使用的共享 Solana RPC 类型和帮助程序<font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc-subscriptions</font>

<font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/web3.js</font>包重新导出<font style="color:rgb(34, 34, 34);background-color:rgb(242, 244, 248);">@solana/rpc</font>包,因此,接下来我们将直接从库中导入 RPC 类型和函数。

例子

自己先创建一个前端项目,然后引入相关的依赖

npm install --save @solana/web3.js@next

所有的代码示例托管到了,github

发送一个请求

createSolanaRpc函数使用应满足大多数用例的默认 HTTP 传输与 RPC 服务器通信。您可以提供自己的传输或包装现有的传输,以您认为合适的任何方式与 RPC 服务器通信。在下面的示例中,我们显式创建了一个传输,并使用它通过 createSolanaRpcFromTransport 函数创建一个新的 RPC 客户端。

import { createSolanaRpcFromTransport, createDefaultRpcTransport } from '@solana/web3.js';

// Create an HTTP transport or any custom transport of your choice.
const transport = createDefaultRpcTransport({ url: 'https://api.devnet.solana.com' });

// Create an RPC client using that transport.
const rpc = createSolanaRpcFromTransport(transport);
//    ^? Rpc<SolanaRpcApi>

// Send a request.
const slot = await rpc.getSlot().send();

点击按钮弹出如下信息

自定义传输可以实现专门的功能,如协调多个传输、实现重试等。让我们来看一些具体的例子。

轮询

“轮询”传输是一种按顺序将请求分发到端点列表的传输。

官方给出的 demo

import { createDefaultRpcTransport, createSolanaRpcFromTransport, type RpcTransport } from '@solana/web3.js';

// Create an HTTP transport for each RPC server.
const transports = [
    createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-1.com' }),
    createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-2.com' }),
    createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-3.com' }),
];

// Set up the round-robin transport.
let nextTransport = 0;
async function roundRobinTransport<TResponse>(...args: Parameters<RpcTransport>): Promise<TResponse> {
    const transport = transports[nextTransport];
    nextTransport = (nextTransport + 1) % transports.length;
    return await transport(...args);
}

// Create an RPC client using the round-robin transport.
const rpc = createSolanaRpcFromTransport(roundRobinTransport);

我们改造一下

// 创建多个RPC客户端用于轮询(这里简化为创建两个示例)
  async createRpcClients() {
    const rpc1 = createSolanaRpc('http://127.0.0.1:8899');
    const rpc2 = createSolanaRpc('http://127.0.0.1:8898');
    this.rpcClients.push(rpc1, rpc2);
  },
  async roundRobinSlotRequest() {
    if (this.rpcClients.length === 0) {
      await this.createRpcClients();
    }
    try {
      const currentRpc = this.rpcClients[this.currentRpcIndex];
      this.slot = await currentRpc.getSlot().send();
      alert(`通过轮询获取到的slot信息为: ${this.slot}`);
      // 更新当前使用的RPC客户端索引,实现轮询效果
      this.currentRpcIndex = (this.currentRpcIndex + 1) % this.rpcClients.length;
    } catch (error) {
      alert(`轮询获取slot信息时出错: ${error}`);
    }
  }
},

通过多次点击按钮弹,会分别弹出如下信息

分片

分片传输是一种分布式传输,它根据请求本身的某些信息向特定服务器发送请求。下面是一个根据方法名称向不同服务器发送请求的示例:

官方的 demo

import { createDefaultRpcTransport, createSolanaRpcFromTransport, type RpcTransport } from '@solana/web3.js';

// Create multiple transports.
const transportA = createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-1.com' });
const transportB = createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-2.com' });
const transportC = createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-3.com' });
const transportD = createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-4.com' });

// Function to determine which shard to use based on the request method.
function selectShard(method: string): RpcTransport {
    switch (method) {
        case 'getAccountInfo':
        case 'getBalance':
            return transportA;
        case 'getLatestBlockhash':
        case 'getTransaction':
            return transportB;
        case 'sendTransaction':
            return transportC;
        default:
            return transportD;
    }
}

// Create a transport that selects the correct transport given the request method name.
async function shardingTransport<TResponse>(...args: Parameters<RpcTransport>): Promise<TResponse> {
    const payload = args[0].payload as { method: string };
    const selectedTransport = selectShard(payload.method);
    return (await selectedTransport(...args)) as TResponse;
}

// Create an RPC client using the sharding transport.
const rpc = createSolanaRpcFromTransport(shardingTransport);

改造后

// 创建多个传输对象(用于sharding)
    createTransports() {
      this.transportA = createDefaultRpcTransport({url: 'https://mainnet-beta.my-server-1.com'});
      this.transportB = createDefaultRpcTransport({url: 'https://mainnet-beta.my-server-2.com'});
      this.transportC = createDefaultRpcTransport({url: 'https://mainnet-beta.my-server-3.com'});
      this.transportD = createDefaultRpcTransport({url: 'https://mainnet-beta.my-server-4.com'});
    },
    // 根据请求方法选择分片(用于sharding)
    selectShard(method) {
      switch (method) {
        case 'getAccountInfo':
        case 'getBalance':
          return this.transportA;
        case 'getLatestBlockhash':
        case 'getTransaction':
          return this.transportB;
        case 'sendTransaction':
          return this.transportC;
        default:
          return this.transportD;
      }
    },
    // 实现分片传输逻辑(用于sharding)
    async shardingTransport(args) {
      const payload = args[0].payload;
      const method = payload.method;
      const selectedTransport = this.selectShard(method);

      return await selectedTransport.apply(null, args);
    },
    // 创建RPC客户端并执行sharding按钮点击操作
    async shardingButtonClick() {
      if (!this.transportA || !this.transportB || !this.transportC || !this.transportD) {
        this.createTransports();
      }
      // 创建RPC客户端
      this.rpc = createSolanaRpcFromTransport(this.shardingTransport);

      try {
        // 这里可以添加具体要执行的请求操作示例,比如获取账户信息
        const accountInfo = await this.rpc.getAccountInfo('your_account_address').send();
        alert(`获取到的账户信息为: ${accountInfo}`);
      } catch (error) {
        alert(`执行sharding操作时出错: ${error}`);
      }
    },
  },

这就是 solana web3.js sdk 2.0 其中一部分新特性的演示。

详情请参考

https://solana-labs.github.io/solana-web3.js/#a-tour-of-the-version-20-api

文中代码托管到了 github

https://github.com/rogers3333/solana-web3.js-learn

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号