@solana/web3.js 2.0.0-preview.3.20240730084245.6f6d0ed2bb0000f0c000ce6ddff230c799be2da0 → 2.0.0-preview.3.20240730161108.1b391f9d971c87160ffaba66a915b05511e971f4
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +162 -188
- package/dist/index.development.js +5 -3
- package/dist/index.development.js.map +1 -1
- package/dist/index.production.min.js +3 -1
- package/package.json +18 -18
package/README.md
CHANGED
@@ -6,15 +6,15 @@
|
|
6
6
|
|
7
7
|
[code-style-prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square
|
8
8
|
[code-style-prettier-url]: https://github.com/prettier/prettier
|
9
|
-
[npm-downloads-image]: https://img.shields.io/npm/dm/@solana/web3.js/
|
10
|
-
[npm-image]: https://img.shields.io/npm/v/@solana/web3.js/
|
11
|
-
[npm-url]: https://www.npmjs.com/package/@solana/web3.js/v/
|
9
|
+
[npm-downloads-image]: https://img.shields.io/npm/dm/@solana/web3.js/preview.svg?style=flat
|
10
|
+
[npm-image]: https://img.shields.io/npm/v/@solana/web3.js/preview.svg?style=flat
|
11
|
+
[npm-url]: https://www.npmjs.com/package/@solana/web3.js/v/preview
|
12
12
|
[semantic-release-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
|
13
13
|
[semantic-release-url]: https://github.com/semantic-release/semantic-release
|
14
14
|
|
15
|
-
# Solana JavaScript SDK
|
15
|
+
# Solana JavaScript SDK 2.0 Release Candidate
|
16
16
|
|
17
|
-
If you build JavaScript applications on Solana, it’s likely you’ve worked with `@solana/web3.js` or a library powered by it. With
|
17
|
+
If you build JavaScript applications on Solana, it’s likely you’ve worked with `@solana/web3.js` or a library powered by it. With 350K+ weekly downloads on npm, it’s the most-used library in the ecosystem for building program clients, web applications, block explorers, and more.
|
18
18
|
|
19
19
|
Here’s an example of a common code snippet from `@solana/web3.js`:
|
20
20
|
|
@@ -25,37 +25,39 @@ const transaction = new Transaction().add(instruction);
|
|
25
25
|
await sendAndConfirmTransaction(connection, transaction, [payer]);
|
26
26
|
```
|
27
27
|
|
28
|
-
In response to your feedback, we began a process of modernizing the library to prepare for the next generation of Solana applications. A
|
28
|
+
In response to your feedback, we began a process of modernizing the library to prepare for the next generation of Solana applications. A Release Candidate of the new web3.js is now available for you to evaluate.
|
29
29
|
|
30
|
-
|
30
|
+
Before leaving the Release Candidate stage, we want to collect as much of your feeback and bug reports as possible. If you find a bug or can not achieve your goals because of the design of the new APIs, please file [a GitHub issue here](https://github.com/solana-labs/solana-web3.js/issues/new/choose).
|
31
31
|
|
32
|
-
|
32
|
+
# Installation
|
33
33
|
|
34
|
-
|
34
|
+
For use in a Node.js or web application:
|
35
35
|
|
36
36
|
```shell
|
37
|
-
npm install --save @solana/web3.js@
|
37
|
+
npm install --save @solana/web3.js@rc
|
38
38
|
```
|
39
39
|
|
40
|
-
|
40
|
+
For use in a browser, without a build system:
|
41
41
|
|
42
42
|
```html
|
43
43
|
<!-- Development (debug mode, unminified) -->
|
44
|
-
<script src="https://unpkg.com/@solana/web3.js@
|
44
|
+
<script src="https://unpkg.com/@solana/web3.js@rc/dist/index.development.js"></script>
|
45
45
|
|
46
46
|
<!-- Production (minified) -->
|
47
|
-
<script src="https://unpkg.com/@solana/web3.js@
|
47
|
+
<script src="https://unpkg.com/@solana/web3.js@rc/dist/index.production.min.js"></script>
|
48
48
|
```
|
49
49
|
|
50
|
-
|
50
|
+
# Examples
|
51
51
|
|
52
|
-
|
52
|
+
To get a feel for the new API, run and modify the live examples in the `examples/` directory. There, you will find a series of single-purpose Node scripts that demonstrate a specific feature or use case. You will also find a React application that you can run in a browser, that demonstrates being able to create, sign, and send transactions using browser wallets.
|
53
53
|
|
54
|
-
|
54
|
+
# What's New
|
55
55
|
|
56
|
-
|
56
|
+
Version 2.0 of the Solana JavaScript SDK is a response to many of the pain points you have communicated to us when developing Solana applications with web3.js. We’ve heard you loud and clear.
|
57
57
|
|
58
|
-
|
58
|
+
## Tree-Shakability
|
59
|
+
|
60
|
+
The object-oriented design of the web3.js (1.x) API prevents optimizing compilers from being able to ‘tree-shake’ unused code from your production builds. No matter how much of the web3.js API you use in your application, you have until now been forced to package all of it.
|
59
61
|
|
60
62
|
Read more about tree-shaking here:
|
61
63
|
|
@@ -67,46 +69,7 @@ One example of an API that can’t be tree-shaken is the `Connection` class. It
|
|
67
69
|
|
68
70
|
Needlessly large JavaScript bundles can cause issues with deployments to cloud compute providers like Cloudflare or AWS Lambda. They also impact webapp startup performance because of longer download and JavaScript parse times.
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
-
Depending on your use case and your tolerance for certain application behaviours, you may be willing to configure your application to make a different set of tradeoffs than another developer. The default tradeoffs that we codify into the web3.js API on the other hand have to work for as large a population as possible, in the common case.
|
73
|
-
|
74
|
-
The inability to customize web3.js has been a source of frustration for some:
|
75
|
-
|
76
|
-
- The Mango team wanted to customize the transaction confirmation strategy, but all of that functionality is hidden away behind `confirmTransaction` – a static method of `Connection`. [Here’s the code for `confirmTransaction` on GitHub](https://github.com/solana-labs/solana-web3.js/blob/69a8ad25ef09f9e6d5bff1ffa8428d9be0bd32ac/packages/library-legacy/src/connection.ts#L3734).
|
77
|
-
- Solana developer ‘mPaella’ [wanted us to add a feature in the RPC](https://github.com/solana-labs/solana-web3.js/issues/1143#issuecomment-1435927152) that would failover to a set of backup URLs in case the primary one failed.
|
78
|
-
- Solana developer ‘epicfaace’ wanted first-class support for automatic time-windowed batching in the RPC transport. [Here’s their pull request](https://github.com/solana-labs/solana/pull/23628).
|
79
|
-
- Multiple folks have expressed the need for custom retry logic for failed requests or transactions. [Here’s a pull request from ‘dafyddd’](https://github.com/solana-labs/solana/pull/11811) and [another from ‘abrkn’](https://github.com/solana-labs/solana-web3.js/issues/1041) attempting to modify retry logic to suit their individual use cases.
|
80
|
-
|
81
|
-
## Lagging Behind Modern JavaScript
|
82
|
-
|
83
|
-
The advance of modern JavaScript features presents an opportunity to developers of crypto applications, such as the ability to use native Ed25519 keys and to express large values as native `bigint`.
|
84
|
-
|
85
|
-
The Web Incubator Community Group has advocated for the addition of Ed25519 support to the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API), and support has already landed in _most_ modern JavaScript runtimes.
|
86
|
-
|
87
|
-
Support for `bigint` values has also become commonplace. The older `number` primitive in JavaScript has a maximum value of 2^53 - 1, whereas Rust’s `u64` can represent values up to 2^64.
|
88
|
-
|
89
|
-
## Class-Based Architecture
|
90
|
-
|
91
|
-
The object oriented, class-based architecture of the legacy library causes unnecessary bundle bloat. Your application has no choice but to bundle _all_ of the functionality and dependencies of a class no matter how many methods you actually use at runtime.
|
92
|
-
|
93
|
-
Class-based architecture also presents unique risks to developers who trigger the dual-package hazard. This describes a situation you can find yourself in if you build for both CommonJS and ES modules. The situation arises when two “copies” of the same class are present in the dependency tree, causing checks like `instanceof` to fail, which introduces aggravating and difficult to debug problems.
|
94
|
-
|
95
|
-
Read more about dual-package hazard:
|
96
|
-
|
97
|
-
- [NodeJS: Dual Package Hazard](https://nodejs.org/api/packages.html#dual-package-hazard)
|
98
|
-
|
99
|
-
# The New web3.js
|
100
|
-
|
101
|
-
Enter web3.js 2.0. The new API aims to deliver a re-imagined experience of building Solana applications, a high level of performance by default, and all with a minimum of code. From the ability to customize the behaviour of the library through composition, to the joy of being able to catch common errors during build time before they make it to production, we hope that you enjoy building with it as much as we’ve enjoyed creating it.
|
102
|
-
|
103
|
-
## Features
|
104
|
-
|
105
|
-
The new (2.0) version of `@solana/web3.js` aims to address shortcomings in the legacy library first, then goes even further.
|
106
|
-
|
107
|
-
### Tree-Shaking
|
108
|
-
|
109
|
-
The 2.0 library is tree-shakable, and that tree-shakeability is enforced in the CI. Anything you don’t use from web3.js 2.0 can now be discarded from your bundle by an optimizing compiler.
|
72
|
+
Version 2.0 is fully tree-shakable and will remain so, enforced by build-time checks. Optimizing compilers can now eliminate those parts of the library that your application does not use.
|
110
73
|
|
111
74
|
The new library itself is comprised of several smaller, modular packages under the `@solana` organization, including:
|
112
75
|
|
@@ -123,61 +86,73 @@ The new library itself is comprised of several smaller, modular packages under t
|
|
123
86
|
|
124
87
|
Some of these packages are themselves composed of smaller packages. For instance, `@solana/rpc` is composed of `@solana/rpc-spec` (for core JSON RPC specification types), `@solana/rpc-api` (for the Solana-specific RPC methods), `@solana/rpc-transport-http` (for the default HTTP transport) and so on.
|
125
88
|
|
126
|
-
Developers can use the default configurations within the library
|
89
|
+
Developers can use the default configurations within the main library (`@solana/web3.js@rc`) or import any of its subpackages where customization-through-composition is desired.
|
127
90
|
|
128
|
-
|
91
|
+
## Composable Internals
|
129
92
|
|
130
|
-
|
93
|
+
Depending on your use case and your tolerance for certain application behaviours, you may wish to configure your application to make a different set of tradeoffs than another developer. The web3.js (1.x) API imposed a rigid set of common-case defaults on _all_ developers, some of which were impossible to change.
|
131
94
|
|
132
|
-
|
95
|
+
The inability to customize web3.js up until now has been a source of frustration:
|
96
|
+
|
97
|
+
- The Mango team wanted to customize the transaction confirmation strategy, but all of that functionality is hidden away behind `confirmTransaction` – a static method of `Connection`. [Here’s the code for `confirmTransaction` on GitHub](https://github.com/solana-labs/solana-web3.js/blob/69a8ad25ef09f9e6d5bff1ffa8428d9be0bd32ac/packages/library-legacy/src/connection.ts#L3734).
|
98
|
+
- Solana developer ‘mPaella’ [wanted us to add a feature in the RPC](https://github.com/solana-labs/solana-web3.js/issues/1143#issuecomment-1435927152) that would failover to a set of backup URLs in case the primary one failed.
|
99
|
+
- Solana developer ‘epicfaace’ wanted first-class support for automatic time-windowed batching in the RPC transport. [Here’s their pull request](https://github.com/solana-labs/solana/pull/23628).
|
100
|
+
- Multiple folks have expressed the need for custom retry logic for failed requests or transactions. [Here’s a pull request from ‘dafyddd’](https://github.com/solana-labs/solana/pull/11811) and [another from ‘abrkn’](https://github.com/solana-labs/solana-web3.js/issues/1041) attempting to modify retry logic to suit their individual use cases.
|
101
|
+
|
102
|
+
Version 2.0 exposes far more of its internals, particularly where communication with an RPC is concerned, and allows willing developers the ability to compose new implementations from the default ones that manifest a nearly limitless array of customizations.
|
103
|
+
|
104
|
+
The individual modules that make up web3.js are assembled in a **default** configuration reminiscent of the legacy library as part of the npm package `@solana/web3.js@rc`, but those who wish to assemble them in different configurations may do so.
|
105
|
+
|
106
|
+
Generic types are offered in numerous places, allowing you to specify new functionality, to make extensions to each API via composition and supertypes, and to encourage you to create higher-level opinionated abstractions of your own.
|
133
107
|
|
134
108
|
In fact, we expect you to do so, and to open source some of those for use by others with similar needs.
|
135
109
|
|
136
|
-
|
110
|
+
## Modern JavaScript; Zero-Dependency
|
137
111
|
|
138
|
-
The
|
112
|
+
The advance of modern JavaScript features presents an opportunity to developers of crypto applications, such as the ability to use native Ed25519 keys and to express large values as native `bigint`.
|
139
113
|
|
140
|
-
|
114
|
+
The Web Incubator Community Group has advocated for the addition of Ed25519 support to the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API), and support has already landed in _most_ modern JavaScript runtimes.
|
141
115
|
|
142
|
-
|
116
|
+
Engine support for `bigint` values has also become commonplace. The older `number` primitive in JavaScript has a maximum value of 2^53 - 1, whereas Rust’s `u64` can represent values up to 2^64.
|
143
117
|
|
144
|
-
|
145
|
-
- [Node JS Documentation: Web Crypto API](https://nodejs.org/api/webcrypto.html)
|
146
|
-
- [Nieky Allen (Blog): The Web Crypto API in Action](https://medium.com/slalom-build/the-web-cryptography-api-in-action-89b2f68c602c)
|
118
|
+
Version 2.0 eliminates userspace implementations of Ed25519 cryptography, large number polyfills, and more, in favour of custom implementations or the use of native JavaScript features, reducing the size of the library. It has no third-party dependencies.
|
147
119
|
|
148
|
-
|
120
|
+
## Functional Architecture
|
149
121
|
|
150
|
-
|
122
|
+
The object oriented, class-based architecture of web3.js (1.x) causes unnecessary bundle bloat. Your application has no choice but to bundle _all_ of the functionality and dependencies of a class no matter how many methods you actually use at runtime.
|
151
123
|
|
152
|
-
|
124
|
+
Class-based architecture also presents unique risks to developers who trigger the dual-package hazard. This describes a situation you can find yourself in if you build for both CommonJS and ES modules. It arises when two copies of the same class are present in the dependency tree, causing checks like `instanceof` to fail. This introduces aggravating and difficult to debug problems.
|
153
125
|
|
154
|
-
|
126
|
+
Read more about dual-package hazard:
|
127
|
+
|
128
|
+
- [NodeJS: Dual Package Hazard](https://nodejs.org/api/packages.html#dual-package-hazard)
|
129
|
+
|
130
|
+
Version 2.0 implements no classes (with the notable exception of the `SolanaError` class) and implements the thinnest possible interfaces at function boundaries.
|
155
131
|
|
156
132
|
## Statistics
|
157
133
|
|
158
|
-
Consider these statistical comparisons between
|
134
|
+
Consider these statistical comparisons between version 2.0 and the legacy 1.x.
|
159
135
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
| Total minified size of library
|
163
|
-
| Total minified size of library (when runtime supports Ed25519)
|
164
|
-
| Bundled size of a web application that
|
165
|
-
| Bundled size of a web application that
|
166
|
-
|
|
167
|
-
|
|
168
|
-
| First-load size for Solana Explorer | 311 KB | 228 KB | -26% |
|
136
|
+
| | 1.x (Legacy) | 2.0 | +/- % |
|
137
|
+
| ------------------------------------------------------------------------------------------------------ | ------------ | ---------- | ----- |
|
138
|
+
| Total minified size of library | 81 KB | 57.5 KB | -29% |
|
139
|
+
| Total minified size of library (when runtime supports Ed25519) | 81 KB | 53 KB | -33% |
|
140
|
+
| Bundled size of a web application that executes a transfer of lamports | 111 KB | 23.9 KB | -78% |
|
141
|
+
| Bundled size of a web application that executes a transfer of lamports (when runtime supports Ed25519) | 111 KB | 18.2 KB | -83% |
|
142
|
+
| Performance of key generation, signing, and verifying signatures (Brave with Experimental API flag) | 700 ops/s | 7000 ops/s | +900% |
|
143
|
+
| First-load size for Solana Explorer | 311 KB | 228 KB | -26% |
|
169
144
|
|
170
145
|
The re-engineered library achieves these speedups and reductions in bundle size in large part through use of modern JavaScript APIs.
|
171
146
|
|
172
147
|
To validate our work, we replaced the legacy 1.x library with the new 2.0 library on the homepage of the Solana Explorer. Total first-load bundle size dropped by 26% without removing a single feature. [Here’s an X thread](https://twitter.com/callum_codes/status/1679124485218226176) by Callum McIntyre if you would like to dig deeper.
|
173
148
|
|
174
|
-
# A
|
149
|
+
# A Tour of the Version 2.0 API
|
175
150
|
|
176
151
|
Here’s an overview of how to use the new library to interact with the RPC, configure network transports, work with Ed25519 keys, and to serialize data.
|
177
152
|
|
178
153
|
## RPC
|
179
154
|
|
180
|
-
|
155
|
+
Version 2.0 ships with an implementation of the [JSON RPC specification](https://www.jsonrpc.org/specification) and a type spec for the [Solana JSON RPC](https://docs.solana.com/api).
|
181
156
|
|
182
157
|
The main package responsible for managing communication with an RPC is `@solana/rpc`. However, this package makes use of more granular packages to break down the RPC logic into smaller pieces. Namely, these packages are:
|
183
158
|
|
@@ -188,11 +163,11 @@ The main package responsible for managing communication with an RPC is `@solana/
|
|
188
163
|
- `@solana/rpc-spec-types`: Shared JSON RPC specifications types and helpers that are used by both `@solana/rpc` and `@solana/rpc-subscriptions` (described in the next section).
|
189
164
|
- `@solana/rpc-types`: Shared Solana RPC types and helpers that are used by both `@solana/rpc` and `@solana/rpc-subscriptions`.
|
190
165
|
|
191
|
-
|
166
|
+
The main `@solana/web3.js` package re-exports the `@solana/rpc` package so, going forward, we will import RPC types and functions from the library directly.
|
192
167
|
|
193
|
-
###
|
168
|
+
### RPC Calls
|
194
169
|
|
195
|
-
|
170
|
+
You can use the `createSolanaRpc` function by providing the URL of a Solana JSON RPC server. This will create a default client for interacting with the Solana JSON RPC API.
|
196
171
|
|
197
172
|
```ts
|
198
173
|
import { createSolanaRpc } from '@solana/web3.js';
|
@@ -205,9 +180,9 @@ const rpc = createSolanaRpc('http://127.0.0.1:8899');
|
|
205
180
|
const slot = await rpc.getSlot().send();
|
206
181
|
```
|
207
182
|
|
208
|
-
###
|
183
|
+
### Custom RPC Transports
|
209
184
|
|
210
|
-
The `createSolanaRpc` function communicates with the RPC server using a default HTTP transport that should satisfy most use cases.
|
185
|
+
The `createSolanaRpc` function communicates with the RPC server using a default HTTP transport that should satisfy most use cases. You can provide your own transport or wrap an existing one to communicate with RPC servers in any way you see fit. In the example below, we explicitly create a transport and use it to create a new RPC client via the `createSolanaRpcFromTransport` function.
|
211
186
|
|
212
187
|
```ts
|
213
188
|
import { createSolanaRpcFromTransport, createDefaultRpcTransport } from '@solana/web3.js';
|
@@ -223,11 +198,11 @@ const rpc = createSolanaRpcFromTransport(transport);
|
|
223
198
|
const slot = await rpc.getSlot().send();
|
224
199
|
```
|
225
200
|
|
226
|
-
|
201
|
+
A custom transport can implement specialized functionality such as coordinating multiple transports, implementing retries, and more. Let's take a look at some concrete examples.
|
227
202
|
|
228
203
|
#### Round Robin
|
229
204
|
|
230
|
-
|
205
|
+
A ‘round robin’ transport is one that distributes requests to a list of endpoints in sequence.
|
231
206
|
|
232
207
|
```ts
|
233
208
|
import { createDefaultRpcTransport, createSolanaRpcFromTransport, type RpcTransport } from '@solana/web3.js';
|
@@ -239,7 +214,7 @@ const transports = [
|
|
239
214
|
createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-3.com' }),
|
240
215
|
];
|
241
216
|
|
242
|
-
// Set up the round-robin
|
217
|
+
// Set up the round-robin transport.
|
243
218
|
let nextTransport = 0;
|
244
219
|
async function roundRobinTransport<TResponse>(...args: Parameters<RpcTransport>): Promise<TResponse> {
|
245
220
|
const transport = transports[nextTransport];
|
@@ -247,21 +222,53 @@ async function roundRobinTransport<TResponse>(...args: Parameters<RpcTransport>)
|
|
247
222
|
return await transport(...args);
|
248
223
|
}
|
249
224
|
|
250
|
-
// Create
|
225
|
+
// Create an RPC client using the round-robin transport.
|
251
226
|
const rpc = createSolanaRpcFromTransport(roundRobinTransport);
|
252
227
|
```
|
253
228
|
|
254
229
|
#### Sharding
|
255
230
|
|
256
|
-
|
231
|
+
A sharding transport is a kind of distributing transport that sends requests to a particular server based on something about the request itself. Here’s an example that sends requests to different servers depending on the name of the method:
|
257
232
|
|
258
233
|
```ts
|
259
|
-
|
234
|
+
import { createDefaultRpcTransport, createSolanaRpcFromTransport, type RpcTransport } from '@solana/web3.js';
|
235
|
+
|
236
|
+
// Create multiple transports.
|
237
|
+
const transportA = createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-1.com' });
|
238
|
+
const transportB = createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-2.com' });
|
239
|
+
const transportC = createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-3.com' });
|
240
|
+
const transportD = createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-4.com' });
|
241
|
+
|
242
|
+
// Function to determine which shard to use based on the request method.
|
243
|
+
function selectShard(method: string): RpcTransport {
|
244
|
+
switch (method) {
|
245
|
+
case 'getAccountInfo':
|
246
|
+
case 'getBalance':
|
247
|
+
return transportA;
|
248
|
+
case 'getTransaction':
|
249
|
+
case 'getRecentBlockhash':
|
250
|
+
return transportB;
|
251
|
+
case 'sendTransaction':
|
252
|
+
return transportC;
|
253
|
+
default:
|
254
|
+
return transportD;
|
255
|
+
}
|
256
|
+
}
|
257
|
+
|
258
|
+
// Create a transport that selects the correct transport given the request method name.
|
259
|
+
async function shardingTransport<TResponse>(...args: Parameters<RpcTransport>): Promise<TResponse> {
|
260
|
+
const payload = args[0].payload as { method: string };
|
261
|
+
const selectedTransport = selectShard(payload.method);
|
262
|
+
return (await selectedTransport(...args)) as TResponse;
|
263
|
+
}
|
264
|
+
|
265
|
+
// Create an RPC client using the sharding transport.
|
266
|
+
const rpc = createSolanaRpcFromTransport(shardingTransport);
|
260
267
|
```
|
261
268
|
|
262
|
-
#### Retry
|
269
|
+
#### Retry
|
263
270
|
|
264
|
-
|
271
|
+
A custom transport is a good place to implement global retry logic for every request:
|
265
272
|
|
266
273
|
```ts
|
267
274
|
import { createDefaultRpcTransport, createSolanaRpcFromTransport, type RpcTransport } from '@solana/web3.js';
|
@@ -307,55 +314,19 @@ const rpc = createSolanaRpcFromTransport(retryingTransport);
|
|
307
314
|
|
308
315
|
#### Failover
|
309
316
|
|
310
|
-
Support for handling
|
317
|
+
Support for handling network failures can be implemented in the transport itself. Here’s an example of some failover logic integrated into a transport:
|
311
318
|
|
312
319
|
```ts
|
313
320
|
// TODO: Your turn; send us a pull request with an example.
|
314
321
|
```
|
315
322
|
|
316
|
-
|
323
|
+
### Augmenting/Constraining the RPC API
|
317
324
|
|
318
|
-
|
325
|
+
Using the `createSolanaRpc` or `createSolanaRpcFromTransport` methods, we always get the same API that includes the Solana RPC API methods. Since the RPC API is described using types only, it is possible to augment those types to add your own methods.
|
319
326
|
|
320
|
-
|
321
|
-
import { createDefaultRpcTransport, createSolanaRpcFromTransport, type RpcTransport } from '@solana/web3.js';
|
327
|
+
When constraining the API scope, keep in mind that types don’t affect bundle size. You may still like to constrain the type-spec for a variety of reasons, including reducing TypeScript noise.
|
322
328
|
|
323
|
-
|
324
|
-
const transportA = createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-1.com' });
|
325
|
-
const transportB = createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-2.com' });
|
326
|
-
const transportC = createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-3.com' });
|
327
|
-
const transportD = createDefaultRpcTransport({ url: 'https://mainnet-beta.my-server-4.com' });
|
328
|
-
|
329
|
-
// Function to determine which shard to use based on the request method.
|
330
|
-
function selectShard(method: string): RpcTransport {
|
331
|
-
switch (method) {
|
332
|
-
case 'getAccountInfo':
|
333
|
-
case 'getBalance':
|
334
|
-
return transportA;
|
335
|
-
case 'getTransaction':
|
336
|
-
case 'getRecentBlockhash':
|
337
|
-
return transportB;
|
338
|
-
case 'sendTransaction':
|
339
|
-
return transportC;
|
340
|
-
default:
|
341
|
-
return transportD;
|
342
|
-
}
|
343
|
-
}
|
344
|
-
|
345
|
-
const rpc = createSolanaRpcFromTransport(async <TResponse>(...args: Parameters<RpcTransport>): Promise<TResponse> => {
|
346
|
-
const payload = args[0].payload as { method: string };
|
347
|
-
const selectedTransport = selectShard(payload.method);
|
348
|
-
return (await selectedTransport(...args)) as TResponse;
|
349
|
-
});
|
350
|
-
```
|
351
|
-
|
352
|
-
### Scoping the RPC API
|
353
|
-
|
354
|
-
Using the `createSolanaRpc` or `createSolanaRpcFromTransport` methods, we always get the same RPC API, including all Solana RPC methods. However, since the RPC API is described using types only, it is possible to scope the API to a specific set of methods and even add your own custom methods.
|
355
|
-
|
356
|
-
When reducing the API scope, keep in mind that types don’t affect bundle size. However, you may choose to scope the type-spec for a variety of reasons, including reducing TypeScript noise.
|
357
|
-
|
358
|
-
#### Scoping by Cluster
|
329
|
+
#### Constraining by Cluster
|
359
330
|
|
360
331
|
If you're using a specific cluster, you may wrap your RPC URL inside a helper function like `mainnet` or `devnet` to inject that information into the RPC type system.
|
361
332
|
|
@@ -369,11 +340,11 @@ const devnetRpc = createSolanaRpc(devnet('https://api.devnet.solana.com'));
|
|
369
340
|
// ^? RpcDevnet<SolanaRpcApiDevnet>
|
370
341
|
```
|
371
342
|
|
372
|
-
In the example above, `devnetRpc.requestAirdrop(..)` will work, but `mainnetRpc.requestAirdrop(..)` will
|
343
|
+
In the example above, `devnetRpc.requestAirdrop(..)` will work, but `mainnetRpc.requestAirdrop(..)` will raise a TypeScript error since `requestAirdrop` is not a valid method of the mainnet cluster.
|
373
344
|
|
374
345
|
#### Cherry-Picking API Methods
|
375
346
|
|
376
|
-
You
|
347
|
+
You can constrain the API’s type-spec even further so you are left only with the methods you need. The simplest way to do this is to cast the created RPC client to a type that only includes the required methods.
|
377
348
|
|
378
349
|
```ts
|
379
350
|
import { createSolanaRpc, type Rpc, type GetAccountInfoApi, type GetMultipleAccountsApi } from '@solana/web3.js';
|
@@ -381,7 +352,7 @@ import { createSolanaRpc, type Rpc, type GetAccountInfoApi, type GetMultipleAcco
|
|
381
352
|
const rpc = createSolanaRpc('http://127.0.0.1:8899') as Rpc<GetAccountInfoApi & GetMultipleAccountsApi>;
|
382
353
|
```
|
383
354
|
|
384
|
-
Alternatively, you
|
355
|
+
Alternatively, you can explicitly create the RPC API using the `createSolanaRpcApi` function. You will need to create your own transport and bind the two together using the `createRpc` function.
|
385
356
|
|
386
357
|
```ts
|
387
358
|
import {
|
@@ -401,38 +372,44 @@ const rpc = createRpc({ api, transport });
|
|
401
372
|
|
402
373
|
Note that the `createSolanaRpcApi` function is a wrapper on top of the `createRpcApi` function which adds some Solana-specific transformers such as setting a default commitment on all methods or throwing an error when an integer overflow is detected.
|
403
374
|
|
404
|
-
#### Creating
|
375
|
+
#### Creating Your Own API Methods
|
405
376
|
|
406
377
|
The new library’s RPC specification supports an _infinite_ number of JSON-RPC methods with **zero increase** in bundle size.
|
407
378
|
|
408
|
-
This means the library can support future additions to the official [Solana JSON RPC](https://docs.solana.com/api), or [custom RPC methods](https://
|
379
|
+
This means the library can support future additions to the official [Solana JSON RPC](https://docs.solana.com/api), or [custom RPC methods](https://docs.helius.dev/compression-and-das-api/digital-asset-standard-das-api/get-asset) defined by some RPC provider.
|
409
380
|
|
410
|
-
Here’s an example of how a developer at
|
381
|
+
Here’s an example of how a developer at might build a custom RPC type-spec for an RPC provider's implementation of the Metaplex Digital Asset Standard's `getAsset` method:
|
411
382
|
|
412
383
|
```ts
|
413
384
|
import { RpcApiMethods } from '@solana/web3.js';
|
414
385
|
|
415
386
|
// Define the method's response payload.
|
416
|
-
type
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
387
|
+
type GetAssetApiResponse = Readonly<{
|
388
|
+
interface: DasApiAssetInterface;
|
389
|
+
id: Address;
|
390
|
+
content: Readonly<{
|
391
|
+
files?: readonly {
|
392
|
+
mime?: string;
|
393
|
+
uri?: string;
|
394
|
+
[key: string]: unknown;
|
395
|
+
}[];
|
396
|
+
json_uri: string;
|
397
|
+
links?: readonly {
|
398
|
+
[key: string]: unknown;
|
399
|
+
}[];
|
400
|
+
metadata: DasApiMetadata;
|
401
|
+
}>;
|
402
|
+
/* ...etc... */
|
426
403
|
}>;
|
427
404
|
|
428
405
|
// Set up an interface for the request method.
|
429
|
-
interface
|
406
|
+
interface GetAssetApi extends RpcApiMethods {
|
430
407
|
// Define the method's name, parameters and response type
|
431
|
-
|
408
|
+
getAsset(args: { id: Address }): GetAssetApiResponse;
|
432
409
|
}
|
433
410
|
|
434
411
|
// Export the type spec for downstream users.
|
435
|
-
export type
|
412
|
+
export type MetaplexDASApi = GetAssetApi;
|
436
413
|
```
|
437
414
|
|
438
415
|
Here’s how a developer might use it:
|
@@ -441,17 +418,19 @@ Here’s how a developer might use it:
|
|
441
418
|
import { createDefaultRpcTransport, createRpc, createRpcApi } from '@solana/web3.js';
|
442
419
|
|
443
420
|
// Create the custom API.
|
444
|
-
const api = createRpcApi<
|
421
|
+
const api = createRpcApi<MetaplexDASApi>();
|
445
422
|
|
446
|
-
// Set up an HTTP transport.
|
447
|
-
const transport = createDefaultRpcTransport({
|
423
|
+
// Set up an HTTP transport to a server that supports the custom API.
|
424
|
+
const transport = createDefaultRpcTransport({
|
425
|
+
url: 'https://mainnet.helius-rpc.com/?api-key=<api_key>',
|
426
|
+
});
|
448
427
|
|
449
428
|
// Create the RPC client.
|
450
|
-
const
|
451
|
-
// ^? Rpc<
|
429
|
+
const metaplexDASRpc = createRpc({ api, transport });
|
430
|
+
// ^? Rpc<MetaplexDASApi>
|
452
431
|
```
|
453
432
|
|
454
|
-
As long as a particular JSON RPC method adheres to the [official JSON RPC specification](https://www.jsonrpc.org/specification), it will be supported by
|
433
|
+
As long as a particular JSON RPC method adheres to the [official JSON RPC specification](https://www.jsonrpc.org/specification), it will be supported by version 2.0.
|
455
434
|
|
456
435
|
### Aborting RPC Requests
|
457
436
|
|
@@ -463,7 +442,7 @@ const pendingRequest: PendingRpcRequest<Slot> = rpc.getSlot();
|
|
463
442
|
const slot: Slot = await pendingRequest.send();
|
464
443
|
```
|
465
444
|
|
466
|
-
|
445
|
+
The arguments of the `getSlot` method are reserved for the request payload, but the `send` method is where additional arguments such as an `AbortSignal` can be accepted in the context of the request.
|
467
446
|
|
468
447
|
Aborting RPC requests can be useful for a variety of things such as setting a timeout on a request or cancelling a request when a user navigates away from a page.
|
469
448
|
|
@@ -484,7 +463,7 @@ function onUserNavigateAway() {
|
|
484
463
|
const slot = await rpc.getSlot().send({ abortSignal: abortController.signal });
|
485
464
|
```
|
486
465
|
|
487
|
-
Read more about `AbortController`
|
466
|
+
Read more about `AbortController` here:
|
488
467
|
|
489
468
|
- [Mozilla Developer Docs: `AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
|
490
469
|
- [Mozilla Developer Docs: `AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
|
@@ -507,7 +486,7 @@ Since the main `@solana/web3.js` library also re-exports the `@solana/rpc-subscr
|
|
507
486
|
|
508
487
|
### Getting Started with RPC Subscriptions
|
509
488
|
|
510
|
-
To get started with RPC Subscriptions, you may use the `createSolanaRpcSubscriptions` function by providing the WebSocket URL of
|
489
|
+
To get started with RPC Subscriptions, you may use the `createSolanaRpcSubscriptions` function by providing the WebSocket URL of a Solana JSON RPC server. This will create a default client for interacting with Solana RPC Subscriptions.
|
511
490
|
|
512
491
|
```ts
|
513
492
|
import { createSolanaRpcSubscriptions } from '@solana/web3.js';
|
@@ -517,7 +496,7 @@ const rpcSubscriptions = createSolanaRpcSubscriptions('ws://127.0.0.1:8900');
|
|
517
496
|
// ^? RpcSubscriptions<SolanaRpcSubscriptionsApi>
|
518
497
|
```
|
519
498
|
|
520
|
-
### Subscriptions as
|
499
|
+
### Subscriptions as `AsyncIterators`
|
521
500
|
|
522
501
|
The new subscriptions API vends subscription notifications as an `AsyncIterator`. The `AsyncIterator` conforms to the [async iterator protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols), which allows developers to consume messages using a `for await...of` loop.
|
523
502
|
|
@@ -615,7 +594,7 @@ for await (const notification of accountNotifications) {
|
|
615
594
|
}
|
616
595
|
```
|
617
596
|
|
618
|
-
### Failed
|
597
|
+
### Failed vs. Aborted Subscriptions
|
619
598
|
|
620
599
|
It is important to note that a subscription failure behaves differently from a subscription abort. A subscription failure occurs when the subscription goes down and will throw an error that can be intercepted in a `try/catch`. However, an aborted subscription will not throw an error, but will instead exit the `for await...of` loop.
|
621
600
|
|
@@ -671,11 +650,11 @@ const rpcSubscriptions = createSolanaRpcSubscriptionsFromTransport(transport);
|
|
671
650
|
// ^? RpcSubscriptions<SolanaRpcSubscriptionsApi>
|
672
651
|
```
|
673
652
|
|
674
|
-
###
|
653
|
+
### Augmenting/Constraining the RPC Subscriptions API
|
675
654
|
|
676
|
-
Using the `createSolanaRpcSubscriptions` or `createSolanaRpcSubscriptionsFromTransport` functions, we always get the same RPC Subscriptions API, including all Solana RPC stable subscriptions. However, since the RPC Subscriptions API is described using types only, it is possible to
|
655
|
+
Using the `createSolanaRpcSubscriptions` or `createSolanaRpcSubscriptionsFromTransport` functions, we always get the same RPC Subscriptions API, including all Solana RPC stable subscriptions. However, since the RPC Subscriptions API is described using types only, it is possible to constrain the API to a specific set of subscriptions or even add your own custom subscriptions.
|
677
656
|
|
678
|
-
####
|
657
|
+
#### Constraining by Cluster
|
679
658
|
|
680
659
|
If you're using a specific cluster, you may wrap your RPC URL inside a helper function like `mainnet` or `devnet` to inject that information into the RPC type system.
|
681
660
|
|
@@ -711,7 +690,7 @@ const rpcSubscriptions = createSolanaRpcSubscriptionsFromTransport_UNSTABLE(tran
|
|
711
690
|
|
712
691
|
#### Cherry-Picking API Methods
|
713
692
|
|
714
|
-
You may
|
693
|
+
You may constrain the scope of the Subscription API even further so you are left only with the subscriptions you need. The simplest way to do this is to cast the created RPC client to a type that only includes the methods you need.
|
715
694
|
|
716
695
|
```ts
|
717
696
|
import {
|
@@ -881,7 +860,7 @@ const signedTransaction = await signTransaction([signer], transactionMessageWith
|
|
881
860
|
// => "Property 'lifetimeConstraint' is missing in type"
|
882
861
|
```
|
883
862
|
|
884
|
-
### Calibrating
|
863
|
+
### Calibrating a Transaction Message's Compute Unit Budget
|
885
864
|
|
886
865
|
Correctly budgeting a compute unit limit for your transaction message can increase the probability that your transaction will be accepted for processing. If you don't declare a compute unit limit on your transaction, validators will assume an upper limit of 200K compute units (CU) per instruction.
|
887
866
|
|
@@ -1098,7 +1077,7 @@ const blockWithRewardsAndTransactionsResponse = await rpc
|
|
1098
1077
|
|
1099
1078
|
### Catching Compile-Time Bugs with TypeScript
|
1100
1079
|
|
1101
|
-
As previously mentioned, the type coverage in
|
1080
|
+
As previously mentioned, the type coverage in version 2.0 allows developers to catch common bugs at compile time, rather than runtime.
|
1102
1081
|
|
1103
1082
|
In the example below, a transaction message is created and then attempted to be signed without setting the fee payer. This would result in a runtime error from the RPC, but instead you will see a type error from TypeScript as you type:
|
1104
1083
|
|
@@ -1297,11 +1276,11 @@ account.data.lastExtendedSlot; // Slot
|
|
1297
1276
|
account.data.lastExtendedSlotStartIndex; // number
|
1298
1277
|
```
|
1299
1278
|
|
1300
|
-
### How
|
1279
|
+
### How Does This Work?
|
1301
1280
|
|
1302
1281
|
All of this code is 100% auto-generated by Kinobi from a tree of standardized nodes that represent our programs. It contains obvious nodes such as `AccountNode` but also more specified nodes such as `ConditionalValueNode` that allows us to resolve account or argument default values conditionally.
|
1303
1282
|
|
1304
|
-
Kinobi allows us to hydrate our tree of nodes from IDLs which are typically generated by program frameworks such as [Anchor](https://github.com/coral-xyz/anchor) or [Shank](https://github.com/metaplex-foundation/shank). Additionally, visitors can be used on our nodes to expand the knowledge of our programs since the IDL itself doesn’t yet contain that level of information. Finally, special visitors called
|
1283
|
+
Kinobi allows us to hydrate our tree of nodes from IDLs which are typically generated by program frameworks such as [Anchor](https://github.com/coral-xyz/anchor) or [Shank](https://github.com/metaplex-foundation/shank). Additionally, visitors can be used on our nodes to expand the knowledge of our programs since the IDL itself doesn’t yet contain that level of information. Finally, special visitors called ‘renderers’ visit our tree to generate clients such as this JavaScript client.
|
1305
1284
|
|
1306
1285
|
Currently, there is one other renderer that generates Rust clients but this is only the beginning. In the future, you can expect renderers for auto-generated Python clients, documentation, CLIs, etc.
|
1307
1286
|
|
@@ -1327,12 +1306,6 @@ This [`create-solana-program`](https://github.com/solana-program/create-solana-p
|
|
1327
1306
|
|
1328
1307
|
When selecting the JavaScript client, you will get a fully generated library compatible with the new web3.js much like the `@solana-program` packages showcased above.
|
1329
1308
|
|
1330
|
-
# Going Forward
|
1331
|
-
|
1332
|
-
This Technology Preview is just that, and development on the new web3.js is ongoing. We are working on tooling to accompany the new library to make building web applications on Solana easier, safer, and more scalable.
|
1333
|
-
|
1334
|
-
Although this new approach to JavaScript tooling is drastically different than the tooling you are used to, we are confident that the customizability, performance, bundle size, and safety characteristics of the new library will make it worth the migration. We’re here to help you every step of the way, via Github issues when you find problems with the library, and on the [Solana Stack Exchange](https://sola.na/sse) when you have questions on how something is supposed to work.
|
1335
|
-
|
1336
1309
|
## GraphQL
|
1337
1310
|
|
1338
1311
|
Though not directly related to web3.js, we wanted to hijack your attention to show you something else that we’re working on, of particular interest to frontend developers. It’s a new API for interacting with the RPC: a GraphQL API.
|
@@ -1442,15 +1415,16 @@ See more in the package’s [README on GitHub](https://github.com/solana-labs/so
|
|
1442
1415
|
|
1443
1416
|
## Development
|
1444
1417
|
|
1445
|
-
You can see all development of
|
1418
|
+
You can see all development of this library and associated GraphQL tooling in the web3.js repository on GitHub.
|
1446
1419
|
|
1447
|
-
https://github.com/solana-labs/solana-web3.js
|
1420
|
+
- https://github.com/solana-labs/solana-web3.js
|
1448
1421
|
|
1449
|
-
|
1422
|
+
You can follow along with program client generator development in the `@solana-program` org and the `@kinobi-so/kinobi` repository.
|
1450
1423
|
|
1451
|
-
|
1424
|
+
- https://github.com/solana-program/
|
1425
|
+
- https://github.com/kinobi-so/kinobi
|
1452
1426
|
|
1453
|
-
|
1427
|
+
Solana Labs develops these tools in public, as open source. We encourage any and all developers who would like to work on these tools to contribute to the codebase.
|
1454
1428
|
|
1455
1429
|
## Thank you
|
1456
1430
|
|