@solana/web3.js 1.41.1 → 1.41.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.browser.cjs.js +593 -375
- package/lib/index.browser.cjs.js.map +1 -1
- package/lib/index.browser.esm.js +639 -422
- package/lib/index.browser.esm.js.map +1 -1
- package/lib/index.cjs.js +610 -378
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +77 -26
- package/lib/index.esm.js +657 -426
- package/lib/index.esm.js.map +1 -1
- package/lib/index.iife.js +12157 -12148
- package/lib/index.iife.js.map +1 -1
- package/lib/index.iife.min.js +2 -24
- package/lib/index.iife.min.js.map +1 -1
- package/package.json +9 -5
- package/src/connection.ts +657 -486
- package/src/index.ts +1 -0
- package/src/system-program.ts +39 -10
package/src/connection.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import bs58 from 'bs58';
|
|
2
2
|
import {Buffer} from 'buffer';
|
|
3
3
|
import crossFetch from 'cross-fetch';
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
import fastStableStringify from 'fast-stable-stringify';
|
|
4
6
|
import {
|
|
5
7
|
type as pick,
|
|
6
8
|
number,
|
|
@@ -22,7 +24,6 @@ import {
|
|
|
22
24
|
import type {Struct} from 'superstruct';
|
|
23
25
|
import {Client as RpcWebSocketClient} from 'rpc-websockets';
|
|
24
26
|
import RpcClient from 'jayson/lib/client/browser';
|
|
25
|
-
import {IWSRequestParams} from 'rpc-websockets/dist/lib/client';
|
|
26
27
|
|
|
27
28
|
import {AgentManager} from './agent-manager';
|
|
28
29
|
import {EpochSchedule} from './epoch-schedule';
|
|
@@ -63,6 +64,130 @@ const BufferFromRawAccountData = coerce(
|
|
|
63
64
|
*/
|
|
64
65
|
export const BLOCKHASH_CACHE_TIMEOUT_MS = 30 * 1000;
|
|
65
66
|
|
|
67
|
+
/**
|
|
68
|
+
* HACK.
|
|
69
|
+
* Copied from rpc-websockets/dist/lib/client.
|
|
70
|
+
* Otherwise, `yarn build` fails with:
|
|
71
|
+
* https://gist.github.com/steveluscher/c057eca81d479ef705cdb53162f9971d
|
|
72
|
+
*/
|
|
73
|
+
interface IWSRequestParams {
|
|
74
|
+
[x: string]: any;
|
|
75
|
+
[x: number]: any;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type ClientSubscriptionId = number;
|
|
79
|
+
/** @internal */ type ServerSubscriptionId = number;
|
|
80
|
+
/** @internal */ type SubscriptionConfigHash = string;
|
|
81
|
+
/** @internal */ type SubscriptionDisposeFn = () => Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* @internal
|
|
84
|
+
* Every subscription contains the args used to open the subscription with
|
|
85
|
+
* the server, and a list of callers interested in notifications.
|
|
86
|
+
*/
|
|
87
|
+
type BaseSubscription<TMethod = SubscriptionConfig['method']> = Readonly<{
|
|
88
|
+
args: IWSRequestParams;
|
|
89
|
+
callbacks: Set<Extract<SubscriptionConfig, {method: TMethod}>['callback']>;
|
|
90
|
+
}>;
|
|
91
|
+
/**
|
|
92
|
+
* @internal
|
|
93
|
+
* A subscription may be in various states of connectedness. Only when it is
|
|
94
|
+
* fully connected will it have a server subscription id associated with it.
|
|
95
|
+
* This id can be returned to the server to unsubscribe the client entirely.
|
|
96
|
+
*/
|
|
97
|
+
type StatefulSubscription = Readonly<
|
|
98
|
+
// New subscriptions that have not yet been
|
|
99
|
+
// sent to the server start in this state.
|
|
100
|
+
| {
|
|
101
|
+
state: 'pending';
|
|
102
|
+
}
|
|
103
|
+
// These subscriptions have been sent to the server
|
|
104
|
+
// and are waiting for the server to acknowledge them.
|
|
105
|
+
| {
|
|
106
|
+
state: 'subscribing';
|
|
107
|
+
}
|
|
108
|
+
// These subscriptions have been acknowledged by the
|
|
109
|
+
// server and have been assigned server subscription ids.
|
|
110
|
+
| {
|
|
111
|
+
serverSubscriptionId: ServerSubscriptionId;
|
|
112
|
+
state: 'subscribed';
|
|
113
|
+
}
|
|
114
|
+
// These subscriptions are intended to be torn down and
|
|
115
|
+
// are waiting on an acknowledgement from the server.
|
|
116
|
+
| {
|
|
117
|
+
serverSubscriptionId: ServerSubscriptionId;
|
|
118
|
+
state: 'unsubscribing';
|
|
119
|
+
}
|
|
120
|
+
// The request to tear down these subscriptions has been
|
|
121
|
+
// acknowledged by the server. The `serverSubscriptionId`
|
|
122
|
+
// is the id of the now-dead subscription.
|
|
123
|
+
| {
|
|
124
|
+
serverSubscriptionId: ServerSubscriptionId;
|
|
125
|
+
state: 'unsubscribed';
|
|
126
|
+
}
|
|
127
|
+
>;
|
|
128
|
+
/**
|
|
129
|
+
* A type that encapsulates a subscription's RPC method
|
|
130
|
+
* names and notification (callback) signature.
|
|
131
|
+
*/
|
|
132
|
+
type SubscriptionConfig = Readonly<
|
|
133
|
+
| {
|
|
134
|
+
callback: AccountChangeCallback;
|
|
135
|
+
method: 'accountSubscribe';
|
|
136
|
+
unsubscribeMethod: 'accountUnsubscribe';
|
|
137
|
+
}
|
|
138
|
+
| {
|
|
139
|
+
callback: LogsCallback;
|
|
140
|
+
method: 'logsSubscribe';
|
|
141
|
+
unsubscribeMethod: 'logsUnsubscribe';
|
|
142
|
+
}
|
|
143
|
+
| {
|
|
144
|
+
callback: ProgramAccountChangeCallback;
|
|
145
|
+
method: 'programSubscribe';
|
|
146
|
+
unsubscribeMethod: 'programUnsubscribe';
|
|
147
|
+
}
|
|
148
|
+
| {
|
|
149
|
+
callback: RootChangeCallback;
|
|
150
|
+
method: 'rootSubscribe';
|
|
151
|
+
unsubscribeMethod: 'rootUnsubscribe';
|
|
152
|
+
}
|
|
153
|
+
| {
|
|
154
|
+
callback: SignatureSubscriptionCallback;
|
|
155
|
+
method: 'signatureSubscribe';
|
|
156
|
+
unsubscribeMethod: 'signatureUnsubscribe';
|
|
157
|
+
}
|
|
158
|
+
| {
|
|
159
|
+
callback: SlotChangeCallback;
|
|
160
|
+
method: 'slotSubscribe';
|
|
161
|
+
unsubscribeMethod: 'slotUnsubscribe';
|
|
162
|
+
}
|
|
163
|
+
| {
|
|
164
|
+
callback: SlotUpdateCallback;
|
|
165
|
+
method: 'slotsUpdatesSubscribe';
|
|
166
|
+
unsubscribeMethod: 'slotsUpdatesUnsubscribe';
|
|
167
|
+
}
|
|
168
|
+
>;
|
|
169
|
+
/**
|
|
170
|
+
* @internal
|
|
171
|
+
* Utility type that keeps tagged unions intact while omitting properties.
|
|
172
|
+
*/
|
|
173
|
+
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown
|
|
174
|
+
? Omit<T, K>
|
|
175
|
+
: never;
|
|
176
|
+
/**
|
|
177
|
+
* @internal
|
|
178
|
+
* This type represents a single subscribable 'topic.' It's made up of:
|
|
179
|
+
*
|
|
180
|
+
* - The args used to open the subscription with the server,
|
|
181
|
+
* - The state of the subscription, in terms of its connectedness, and
|
|
182
|
+
* - The set of callbacks to call when the server publishes notifications
|
|
183
|
+
*
|
|
184
|
+
* This record gets indexed by `SubscriptionConfigHash` and is used to
|
|
185
|
+
* set up subscriptions, fan out notifications, and track subscription state.
|
|
186
|
+
*/
|
|
187
|
+
type Subscription = BaseSubscription &
|
|
188
|
+
StatefulSubscription &
|
|
189
|
+
DistributiveOmit<SubscriptionConfig, 'callback'>;
|
|
190
|
+
|
|
66
191
|
type RpcRequest = (methodName: string, args: Array<any>) => any;
|
|
67
192
|
|
|
68
193
|
type RpcBatchRequest = (requests: RpcParams[]) => any;
|
|
@@ -1863,21 +1988,6 @@ export type AccountChangeCallback = (
|
|
|
1863
1988
|
context: Context,
|
|
1864
1989
|
) => void;
|
|
1865
1990
|
|
|
1866
|
-
/**
|
|
1867
|
-
* @internal
|
|
1868
|
-
*/
|
|
1869
|
-
type SubscriptionId = 'subscribing' | number;
|
|
1870
|
-
|
|
1871
|
-
/**
|
|
1872
|
-
* @internal
|
|
1873
|
-
*/
|
|
1874
|
-
type AccountSubscriptionInfo = {
|
|
1875
|
-
publicKey: string; // PublicKey of the account as a base 58 string
|
|
1876
|
-
callback: AccountChangeCallback;
|
|
1877
|
-
commitment?: Commitment;
|
|
1878
|
-
subscriptionId: SubscriptionId | null; // null when there's no current server subscription id
|
|
1879
|
-
};
|
|
1880
|
-
|
|
1881
1991
|
/**
|
|
1882
1992
|
* Callback function for program account change notifications
|
|
1883
1993
|
*/
|
|
@@ -1886,43 +1996,16 @@ export type ProgramAccountChangeCallback = (
|
|
|
1886
1996
|
context: Context,
|
|
1887
1997
|
) => void;
|
|
1888
1998
|
|
|
1889
|
-
/**
|
|
1890
|
-
* @internal
|
|
1891
|
-
*/
|
|
1892
|
-
type ProgramAccountSubscriptionInfo = {
|
|
1893
|
-
programId: string; // PublicKey of the program as a base 58 string
|
|
1894
|
-
callback: ProgramAccountChangeCallback;
|
|
1895
|
-
commitment?: Commitment;
|
|
1896
|
-
subscriptionId: SubscriptionId | null; // null when there's no current server subscription id
|
|
1897
|
-
filters?: GetProgramAccountsFilter[];
|
|
1898
|
-
};
|
|
1899
|
-
|
|
1900
1999
|
/**
|
|
1901
2000
|
* Callback function for slot change notifications
|
|
1902
2001
|
*/
|
|
1903
2002
|
export type SlotChangeCallback = (slotInfo: SlotInfo) => void;
|
|
1904
2003
|
|
|
1905
|
-
/**
|
|
1906
|
-
* @internal
|
|
1907
|
-
*/
|
|
1908
|
-
type SlotSubscriptionInfo = {
|
|
1909
|
-
callback: SlotChangeCallback;
|
|
1910
|
-
subscriptionId: SubscriptionId | null; // null when there's no current server subscription id
|
|
1911
|
-
};
|
|
1912
|
-
|
|
1913
2004
|
/**
|
|
1914
2005
|
* Callback function for slot update notifications
|
|
1915
2006
|
*/
|
|
1916
2007
|
export type SlotUpdateCallback = (slotUpdate: SlotUpdate) => void;
|
|
1917
2008
|
|
|
1918
|
-
/**
|
|
1919
|
-
* @private
|
|
1920
|
-
*/
|
|
1921
|
-
type SlotUpdateSubscriptionInfo = {
|
|
1922
|
-
callback: SlotUpdateCallback;
|
|
1923
|
-
subscriptionId: SubscriptionId | null; // null when there's no current server subscription id
|
|
1924
|
-
};
|
|
1925
|
-
|
|
1926
2009
|
/**
|
|
1927
2010
|
* Callback function for signature status notifications
|
|
1928
2011
|
*/
|
|
@@ -1962,29 +2045,11 @@ export type SignatureSubscriptionOptions = {
|
|
|
1962
2045
|
enableReceivedNotification?: boolean;
|
|
1963
2046
|
};
|
|
1964
2047
|
|
|
1965
|
-
/**
|
|
1966
|
-
* @internal
|
|
1967
|
-
*/
|
|
1968
|
-
type SignatureSubscriptionInfo = {
|
|
1969
|
-
signature: TransactionSignature; // TransactionSignature as a base 58 string
|
|
1970
|
-
callback: SignatureSubscriptionCallback;
|
|
1971
|
-
options?: SignatureSubscriptionOptions;
|
|
1972
|
-
subscriptionId: SubscriptionId | null; // null when there's no current server subscription id
|
|
1973
|
-
};
|
|
1974
|
-
|
|
1975
2048
|
/**
|
|
1976
2049
|
* Callback function for root change notifications
|
|
1977
2050
|
*/
|
|
1978
2051
|
export type RootChangeCallback = (root: number) => void;
|
|
1979
2052
|
|
|
1980
|
-
/**
|
|
1981
|
-
* @internal
|
|
1982
|
-
*/
|
|
1983
|
-
type RootSubscriptionInfo = {
|
|
1984
|
-
callback: RootChangeCallback;
|
|
1985
|
-
subscriptionId: SubscriptionId | null; // null when there's no current server subscription id
|
|
1986
|
-
};
|
|
1987
|
-
|
|
1988
2053
|
/**
|
|
1989
2054
|
* @internal
|
|
1990
2055
|
*/
|
|
@@ -2021,16 +2086,6 @@ export type LogsFilter = PublicKey | 'all' | 'allWithVotes';
|
|
|
2021
2086
|
*/
|
|
2022
2087
|
export type LogsCallback = (logs: Logs, ctx: Context) => void;
|
|
2023
2088
|
|
|
2024
|
-
/**
|
|
2025
|
-
* @private
|
|
2026
|
-
*/
|
|
2027
|
-
type LogsSubscriptionInfo = {
|
|
2028
|
-
callback: LogsCallback;
|
|
2029
|
-
filter: LogsFilter;
|
|
2030
|
-
subscriptionId: SubscriptionId | null; // null when there's no current server subscription id
|
|
2031
|
-
commitment?: Commitment;
|
|
2032
|
-
};
|
|
2033
|
-
|
|
2034
2089
|
/**
|
|
2035
2090
|
* Signature result
|
|
2036
2091
|
*/
|
|
@@ -2120,13 +2175,6 @@ export type ConnectionConfig = {
|
|
|
2120
2175
|
confirmTransactionInitialTimeout?: number;
|
|
2121
2176
|
};
|
|
2122
2177
|
|
|
2123
|
-
function createSubscriptionWarningMessage(id: number, label: string): string {
|
|
2124
|
-
return (
|
|
2125
|
-
'Ignored unsubscribe request because an active subscription ' +
|
|
2126
|
-
`with id \`${id}\` for '${label}' events could not be found.`
|
|
2127
|
-
);
|
|
2128
|
-
}
|
|
2129
|
-
|
|
2130
2178
|
/**
|
|
2131
2179
|
* A connection to a fullnode JSON RPC endpoint
|
|
2132
2180
|
*/
|
|
@@ -2146,6 +2194,13 @@ export class Connection {
|
|
|
2146
2194
|
/** @internal */ _rpcWebSocketIdleTimeout: ReturnType<
|
|
2147
2195
|
typeof setTimeout
|
|
2148
2196
|
> | null = null;
|
|
2197
|
+
/** @internal
|
|
2198
|
+
* A number that we increment every time an active connection closes.
|
|
2199
|
+
* Used to determine whether the same socket connection that was open
|
|
2200
|
+
* when an async operation started is the same one that's active when
|
|
2201
|
+
* its continuation fires.
|
|
2202
|
+
*
|
|
2203
|
+
*/ private _rpcWebSocketGeneration: number = 0;
|
|
2149
2204
|
|
|
2150
2205
|
/** @internal */ _disableBlockhashCaching: boolean = false;
|
|
2151
2206
|
/** @internal */ _pollingBlockhash: boolean = false;
|
|
@@ -2161,40 +2216,35 @@ export class Connection {
|
|
|
2161
2216
|
simulatedSignatures: [],
|
|
2162
2217
|
};
|
|
2163
2218
|
|
|
2164
|
-
/** @internal */
|
|
2165
|
-
/** @internal */
|
|
2166
|
-
[
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
/** @internal */ _programAccountChangeSubscriptionCounter: number = 0;
|
|
2170
|
-
/** @internal */ _programAccountChangeSubscriptions: {
|
|
2171
|
-
[id: number]: ProgramAccountSubscriptionInfo;
|
|
2172
|
-
} = {};
|
|
2173
|
-
|
|
2174
|
-
/** @internal */ _rootSubscriptionCounter: number = 0;
|
|
2175
|
-
/** @internal */ _rootSubscriptions: {
|
|
2176
|
-
[id: number]: RootSubscriptionInfo;
|
|
2219
|
+
/** @internal */ private _nextClientSubscriptionId: ClientSubscriptionId = 0;
|
|
2220
|
+
/** @internal */ private _subscriptionDisposeFunctionsByClientSubscriptionId: {
|
|
2221
|
+
[clientSubscriptionId: ClientSubscriptionId]:
|
|
2222
|
+
| SubscriptionDisposeFn
|
|
2223
|
+
| undefined;
|
|
2177
2224
|
} = {};
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
} = {};
|
|
2183
|
-
|
|
2184
|
-
/** @internal */ _slotSubscriptionCounter: number = 0;
|
|
2185
|
-
/** @internal */ _slotSubscriptions: {
|
|
2186
|
-
[id: number]: SlotSubscriptionInfo;
|
|
2187
|
-
} = {};
|
|
2188
|
-
|
|
2189
|
-
/** @internal */ _logsSubscriptionCounter: number = 0;
|
|
2190
|
-
/** @internal */ _logsSubscriptions: {
|
|
2191
|
-
[id: number]: LogsSubscriptionInfo;
|
|
2225
|
+
/** @internal */ private _subscriptionCallbacksByServerSubscriptionId: {
|
|
2226
|
+
[serverSubscriptionId: ServerSubscriptionId]:
|
|
2227
|
+
| Set<SubscriptionConfig['callback']>
|
|
2228
|
+
| undefined;
|
|
2192
2229
|
} = {};
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
/** @internal */ _slotUpdateSubscriptions: {
|
|
2196
|
-
[id: number]: SlotUpdateSubscriptionInfo;
|
|
2230
|
+
/** @internal */ private _subscriptionsByHash: {
|
|
2231
|
+
[hash: SubscriptionConfigHash]: Subscription | undefined;
|
|
2197
2232
|
} = {};
|
|
2233
|
+
/**
|
|
2234
|
+
* Special case.
|
|
2235
|
+
* After a signature is processed, RPCs automatically dispose of the
|
|
2236
|
+
* subscription on the server side. We need to track which of these
|
|
2237
|
+
* subscriptions have been disposed in such a way, so that we know
|
|
2238
|
+
* whether the client is dealing with a not-yet-processed signature
|
|
2239
|
+
* (in which case we must tear down the server subscription) or an
|
|
2240
|
+
* already-processed signature (in which case the client can simply
|
|
2241
|
+
* clear out the subscription locally without telling the server).
|
|
2242
|
+
*
|
|
2243
|
+
* NOTE: There is a proposal to eliminate this special case, here:
|
|
2244
|
+
* https://github.com/solana-labs/solana/issues/18892
|
|
2245
|
+
*/
|
|
2246
|
+
/** @internal */ private _subscriptionsAutoDisposedByRpc: Set<ServerSubscriptionId> =
|
|
2247
|
+
new Set();
|
|
2198
2248
|
|
|
2199
2249
|
/**
|
|
2200
2250
|
* Establish a JSON RPC connection
|
|
@@ -4131,6 +4181,7 @@ export class Connection {
|
|
|
4131
4181
|
* @internal
|
|
4132
4182
|
*/
|
|
4133
4183
|
_wsOnClose(code: number) {
|
|
4184
|
+
this._rpcWebSocketGeneration++;
|
|
4134
4185
|
if (this._rpcWebSocketHeartbeat) {
|
|
4135
4186
|
clearInterval(this._rpcWebSocketHeartbeat);
|
|
4136
4187
|
this._rpcWebSocketHeartbeat = null;
|
|
@@ -4143,114 +4194,22 @@ export class Connection {
|
|
|
4143
4194
|
}
|
|
4144
4195
|
|
|
4145
4196
|
// implicit close, prepare subscriptions for auto-reconnect
|
|
4146
|
-
this.
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
rpcArgs: IWSRequestParams,
|
|
4156
|
-
) {
|
|
4157
|
-
if (sub.subscriptionId == null) {
|
|
4158
|
-
sub.subscriptionId = 'subscribing';
|
|
4159
|
-
try {
|
|
4160
|
-
const id = await this._rpcWebSocket.call(rpcMethod, rpcArgs);
|
|
4161
|
-
if (typeof id === 'number' && sub.subscriptionId === 'subscribing') {
|
|
4162
|
-
// eslint-disable-next-line require-atomic-updates
|
|
4163
|
-
sub.subscriptionId = id;
|
|
4164
|
-
}
|
|
4165
|
-
} catch (err) {
|
|
4166
|
-
if (sub.subscriptionId === 'subscribing') {
|
|
4167
|
-
// eslint-disable-next-line require-atomic-updates
|
|
4168
|
-
sub.subscriptionId = null;
|
|
4169
|
-
}
|
|
4170
|
-
if (err instanceof Error) {
|
|
4171
|
-
console.error(
|
|
4172
|
-
`${rpcMethod} error for argument`,
|
|
4173
|
-
rpcArgs,
|
|
4174
|
-
err.message,
|
|
4175
|
-
);
|
|
4176
|
-
}
|
|
4177
|
-
}
|
|
4178
|
-
}
|
|
4179
|
-
}
|
|
4180
|
-
|
|
4181
|
-
/**
|
|
4182
|
-
* @internal
|
|
4183
|
-
*/
|
|
4184
|
-
async _unsubscribe(
|
|
4185
|
-
sub: {subscriptionId: SubscriptionId | null},
|
|
4186
|
-
rpcMethod: string,
|
|
4187
|
-
) {
|
|
4188
|
-
const subscriptionId = sub.subscriptionId;
|
|
4189
|
-
if (subscriptionId != null && typeof subscriptionId != 'string') {
|
|
4190
|
-
const unsubscribeId: number = subscriptionId;
|
|
4191
|
-
try {
|
|
4192
|
-
await this._rpcWebSocket.call(rpcMethod, [unsubscribeId]);
|
|
4193
|
-
} catch (err) {
|
|
4194
|
-
if (err instanceof Error) {
|
|
4195
|
-
console.error(`${rpcMethod} error:`, err.message);
|
|
4196
|
-
}
|
|
4197
|
-
}
|
|
4198
|
-
}
|
|
4199
|
-
}
|
|
4200
|
-
|
|
4201
|
-
/**
|
|
4202
|
-
* @internal
|
|
4203
|
-
*/
|
|
4204
|
-
_resetSubscriptions() {
|
|
4205
|
-
Object.values(this._accountChangeSubscriptions).forEach(
|
|
4206
|
-
s => (s.subscriptionId = null),
|
|
4207
|
-
);
|
|
4208
|
-
Object.values(this._logsSubscriptions).forEach(
|
|
4209
|
-
s => (s.subscriptionId = null),
|
|
4210
|
-
);
|
|
4211
|
-
Object.values(this._programAccountChangeSubscriptions).forEach(
|
|
4212
|
-
s => (s.subscriptionId = null),
|
|
4213
|
-
);
|
|
4214
|
-
Object.values(this._rootSubscriptions).forEach(
|
|
4215
|
-
s => (s.subscriptionId = null),
|
|
4216
|
-
);
|
|
4217
|
-
Object.values(this._signatureSubscriptions).forEach(
|
|
4218
|
-
s => (s.subscriptionId = null),
|
|
4219
|
-
);
|
|
4220
|
-
Object.values(this._slotSubscriptions).forEach(
|
|
4221
|
-
s => (s.subscriptionId = null),
|
|
4222
|
-
);
|
|
4223
|
-
Object.values(this._slotUpdateSubscriptions).forEach(
|
|
4224
|
-
s => (s.subscriptionId = null),
|
|
4225
|
-
);
|
|
4197
|
+
this._subscriptionCallbacksByServerSubscriptionId = {};
|
|
4198
|
+
Object.entries(
|
|
4199
|
+
this._subscriptionsByHash as Record<SubscriptionConfigHash, Subscription>,
|
|
4200
|
+
).forEach(([hash, subscription]) => {
|
|
4201
|
+
this._subscriptionsByHash[hash] = {
|
|
4202
|
+
...subscription,
|
|
4203
|
+
state: 'pending',
|
|
4204
|
+
};
|
|
4205
|
+
});
|
|
4226
4206
|
}
|
|
4227
4207
|
|
|
4228
4208
|
/**
|
|
4229
4209
|
* @internal
|
|
4230
4210
|
*/
|
|
4231
|
-
_updateSubscriptions() {
|
|
4232
|
-
|
|
4233
|
-
Number,
|
|
4234
|
-
);
|
|
4235
|
-
const programKeys = Object.keys(
|
|
4236
|
-
this._programAccountChangeSubscriptions,
|
|
4237
|
-
).map(Number);
|
|
4238
|
-
const slotKeys = Object.keys(this._slotSubscriptions).map(Number);
|
|
4239
|
-
const slotUpdateKeys = Object.keys(this._slotUpdateSubscriptions).map(
|
|
4240
|
-
Number,
|
|
4241
|
-
);
|
|
4242
|
-
const signatureKeys = Object.keys(this._signatureSubscriptions).map(Number);
|
|
4243
|
-
const rootKeys = Object.keys(this._rootSubscriptions).map(Number);
|
|
4244
|
-
const logsKeys = Object.keys(this._logsSubscriptions).map(Number);
|
|
4245
|
-
if (
|
|
4246
|
-
accountKeys.length === 0 &&
|
|
4247
|
-
programKeys.length === 0 &&
|
|
4248
|
-
slotKeys.length === 0 &&
|
|
4249
|
-
slotUpdateKeys.length === 0 &&
|
|
4250
|
-
signatureKeys.length === 0 &&
|
|
4251
|
-
rootKeys.length === 0 &&
|
|
4252
|
-
logsKeys.length === 0
|
|
4253
|
-
) {
|
|
4211
|
+
async _updateSubscriptions() {
|
|
4212
|
+
if (Object.keys(this._subscriptionsByHash).length === 0) {
|
|
4254
4213
|
if (this._rpcWebSocketConnected) {
|
|
4255
4214
|
this._rpcWebSocketConnected = false;
|
|
4256
4215
|
this._rpcWebSocketIdleTimeout = setTimeout(() => {
|
|
@@ -4281,75 +4240,255 @@ export class Connection {
|
|
|
4281
4240
|
return;
|
|
4282
4241
|
}
|
|
4283
4242
|
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
this.
|
|
4287
|
-
|
|
4288
|
-
'accountSubscribe',
|
|
4289
|
-
this._buildArgs([sub.publicKey], sub.commitment, 'base64'),
|
|
4290
|
-
);
|
|
4291
|
-
}
|
|
4292
|
-
|
|
4293
|
-
for (let id of programKeys) {
|
|
4294
|
-
const sub = this._programAccountChangeSubscriptions[id];
|
|
4295
|
-
this._subscribe(
|
|
4296
|
-
sub,
|
|
4297
|
-
'programSubscribe',
|
|
4298
|
-
this._buildArgs([sub.programId], sub.commitment, 'base64', {
|
|
4299
|
-
filters: sub.filters,
|
|
4300
|
-
}),
|
|
4301
|
-
);
|
|
4302
|
-
}
|
|
4303
|
-
|
|
4304
|
-
for (let id of slotKeys) {
|
|
4305
|
-
const sub = this._slotSubscriptions[id];
|
|
4306
|
-
this._subscribe(sub, 'slotSubscribe', []);
|
|
4307
|
-
}
|
|
4308
|
-
|
|
4309
|
-
for (let id of slotUpdateKeys) {
|
|
4310
|
-
const sub = this._slotUpdateSubscriptions[id];
|
|
4311
|
-
this._subscribe(sub, 'slotsUpdatesSubscribe', []);
|
|
4312
|
-
}
|
|
4243
|
+
const activeWebSocketGeneration = this._rpcWebSocketGeneration;
|
|
4244
|
+
const isCurrentConnectionStillActive = () => {
|
|
4245
|
+
return activeWebSocketGeneration === this._rpcWebSocketGeneration;
|
|
4246
|
+
};
|
|
4313
4247
|
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4248
|
+
await Promise.all(
|
|
4249
|
+
// Don't be tempted to change this to `Object.entries`. We call
|
|
4250
|
+
// `_updateSubscriptions` recursively when processing the state,
|
|
4251
|
+
// so it's important that we look up the *current* version of
|
|
4252
|
+
// each subscription, every time we process a hash.
|
|
4253
|
+
Object.keys(this._subscriptionsByHash).map(async hash => {
|
|
4254
|
+
const subscription = this._subscriptionsByHash[hash];
|
|
4255
|
+
if (subscription === undefined) {
|
|
4256
|
+
// This entry has since been deleted. Skip.
|
|
4257
|
+
return;
|
|
4258
|
+
}
|
|
4259
|
+
switch (subscription.state) {
|
|
4260
|
+
case 'pending':
|
|
4261
|
+
case 'unsubscribed':
|
|
4262
|
+
if (subscription.callbacks.size === 0) {
|
|
4263
|
+
/**
|
|
4264
|
+
* You can end up here when:
|
|
4265
|
+
*
|
|
4266
|
+
* - a subscription has recently unsubscribed
|
|
4267
|
+
* without having new callbacks added to it
|
|
4268
|
+
* while the unsubscribe was in flight, or
|
|
4269
|
+
* - when a pending subscription has its
|
|
4270
|
+
* listeners removed before a request was
|
|
4271
|
+
* sent to the server.
|
|
4272
|
+
*
|
|
4273
|
+
* Being that nobody is interested in this
|
|
4274
|
+
* subscription any longer, delete it.
|
|
4275
|
+
*/
|
|
4276
|
+
delete this._subscriptionsByHash[hash];
|
|
4277
|
+
if (subscription.state === 'unsubscribed') {
|
|
4278
|
+
delete this._subscriptionCallbacksByServerSubscriptionId[
|
|
4279
|
+
subscription.serverSubscriptionId
|
|
4280
|
+
];
|
|
4281
|
+
}
|
|
4282
|
+
await this._updateSubscriptions();
|
|
4283
|
+
return;
|
|
4284
|
+
}
|
|
4285
|
+
await (async () => {
|
|
4286
|
+
const {args, method} = subscription;
|
|
4287
|
+
try {
|
|
4288
|
+
this._subscriptionsByHash[hash] = {
|
|
4289
|
+
...subscription,
|
|
4290
|
+
state: 'subscribing',
|
|
4291
|
+
};
|
|
4292
|
+
const serverSubscriptionId: ServerSubscriptionId =
|
|
4293
|
+
(await this._rpcWebSocket.call(method, args)) as number;
|
|
4294
|
+
this._subscriptionsByHash[hash] = {
|
|
4295
|
+
...subscription,
|
|
4296
|
+
serverSubscriptionId,
|
|
4297
|
+
state: 'subscribed',
|
|
4298
|
+
};
|
|
4299
|
+
this._subscriptionCallbacksByServerSubscriptionId[
|
|
4300
|
+
serverSubscriptionId
|
|
4301
|
+
] = subscription.callbacks;
|
|
4302
|
+
await this._updateSubscriptions();
|
|
4303
|
+
} catch (e) {
|
|
4304
|
+
if (e instanceof Error) {
|
|
4305
|
+
console.error(
|
|
4306
|
+
`${method} error for argument`,
|
|
4307
|
+
args,
|
|
4308
|
+
e.message,
|
|
4309
|
+
);
|
|
4310
|
+
}
|
|
4311
|
+
if (!isCurrentConnectionStillActive()) {
|
|
4312
|
+
return;
|
|
4313
|
+
}
|
|
4314
|
+
// TODO: Maybe add an 'errored' state or a retry limit?
|
|
4315
|
+
this._subscriptionsByHash[hash] = {
|
|
4316
|
+
...subscription,
|
|
4317
|
+
state: 'pending',
|
|
4318
|
+
};
|
|
4319
|
+
await this._updateSubscriptions();
|
|
4320
|
+
}
|
|
4321
|
+
})();
|
|
4322
|
+
break;
|
|
4323
|
+
case 'subscribed':
|
|
4324
|
+
if (subscription.callbacks.size === 0) {
|
|
4325
|
+
// By the time we successfully set up a subscription
|
|
4326
|
+
// with the server, the client stopped caring about it.
|
|
4327
|
+
// Tear it down now.
|
|
4328
|
+
await (async () => {
|
|
4329
|
+
const {serverSubscriptionId, unsubscribeMethod} = subscription;
|
|
4330
|
+
if (
|
|
4331
|
+
this._subscriptionsAutoDisposedByRpc.has(serverSubscriptionId)
|
|
4332
|
+
) {
|
|
4333
|
+
/**
|
|
4334
|
+
* Special case.
|
|
4335
|
+
* If we're dealing with a subscription that has been auto-
|
|
4336
|
+
* disposed by the RPC, then we can skip the RPC call to
|
|
4337
|
+
* tear down the subscription here.
|
|
4338
|
+
*
|
|
4339
|
+
* NOTE: There is a proposal to eliminate this special case, here:
|
|
4340
|
+
* https://github.com/solana-labs/solana/issues/18892
|
|
4341
|
+
*/
|
|
4342
|
+
this._subscriptionsAutoDisposedByRpc.delete(
|
|
4343
|
+
serverSubscriptionId,
|
|
4344
|
+
);
|
|
4345
|
+
} else {
|
|
4346
|
+
this._subscriptionsByHash[hash] = {
|
|
4347
|
+
...subscription,
|
|
4348
|
+
state: 'unsubscribing',
|
|
4349
|
+
};
|
|
4350
|
+
try {
|
|
4351
|
+
await this._rpcWebSocket.call(unsubscribeMethod, [
|
|
4352
|
+
serverSubscriptionId,
|
|
4353
|
+
]);
|
|
4354
|
+
} catch (e) {
|
|
4355
|
+
if (e instanceof Error) {
|
|
4356
|
+
console.error(`${unsubscribeMethod} error:`, e.message);
|
|
4357
|
+
}
|
|
4358
|
+
if (!isCurrentConnectionStillActive()) {
|
|
4359
|
+
return;
|
|
4360
|
+
}
|
|
4361
|
+
// TODO: Maybe add an 'errored' state or a retry limit?
|
|
4362
|
+
this._subscriptionsByHash[hash] = {
|
|
4363
|
+
...subscription,
|
|
4364
|
+
state: 'subscribed',
|
|
4365
|
+
};
|
|
4366
|
+
await this._updateSubscriptions();
|
|
4367
|
+
return;
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4370
|
+
this._subscriptionsByHash[hash] = {
|
|
4371
|
+
...subscription,
|
|
4372
|
+
state: 'unsubscribed',
|
|
4373
|
+
};
|
|
4374
|
+
await this._updateSubscriptions();
|
|
4375
|
+
})();
|
|
4376
|
+
}
|
|
4377
|
+
break;
|
|
4378
|
+
case 'subscribing':
|
|
4379
|
+
case 'unsubscribing':
|
|
4380
|
+
break;
|
|
4381
|
+
}
|
|
4382
|
+
}),
|
|
4383
|
+
);
|
|
4384
|
+
}
|
|
4320
4385
|
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4386
|
+
/**
|
|
4387
|
+
* @internal
|
|
4388
|
+
*/
|
|
4389
|
+
private _handleServerNotification<
|
|
4390
|
+
TCallback extends SubscriptionConfig['callback'],
|
|
4391
|
+
>(
|
|
4392
|
+
serverSubscriptionId: ServerSubscriptionId,
|
|
4393
|
+
callbackArgs: Parameters<TCallback>,
|
|
4394
|
+
): void {
|
|
4395
|
+
const callbacks =
|
|
4396
|
+
this._subscriptionCallbacksByServerSubscriptionId[serverSubscriptionId];
|
|
4397
|
+
if (callbacks === undefined) {
|
|
4398
|
+
return;
|
|
4324
4399
|
}
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4400
|
+
callbacks.forEach(cb => {
|
|
4401
|
+
try {
|
|
4402
|
+
cb(
|
|
4403
|
+
// I failed to find a way to convince TypeScript that `cb` is of type
|
|
4404
|
+
// `TCallback` which is certainly compatible with `Parameters<TCallback>`.
|
|
4405
|
+
// See https://github.com/microsoft/TypeScript/issues/47615
|
|
4406
|
+
// @ts-ignore
|
|
4407
|
+
...callbackArgs,
|
|
4408
|
+
);
|
|
4409
|
+
} catch (e) {
|
|
4410
|
+
console.error(e);
|
|
4333
4411
|
}
|
|
4334
|
-
|
|
4335
|
-
sub,
|
|
4336
|
-
'logsSubscribe',
|
|
4337
|
-
this._buildArgs([filter], sub.commitment),
|
|
4338
|
-
);
|
|
4339
|
-
}
|
|
4412
|
+
});
|
|
4340
4413
|
}
|
|
4341
4414
|
|
|
4342
4415
|
/**
|
|
4343
4416
|
* @internal
|
|
4344
4417
|
*/
|
|
4345
4418
|
_wsOnAccountNotification(notification: object) {
|
|
4346
|
-
const
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4419
|
+
const {result, subscription} = create(
|
|
4420
|
+
notification,
|
|
4421
|
+
AccountNotificationResult,
|
|
4422
|
+
);
|
|
4423
|
+
this._handleServerNotification<AccountChangeCallback>(subscription, [
|
|
4424
|
+
result.value,
|
|
4425
|
+
result.context,
|
|
4426
|
+
]);
|
|
4427
|
+
}
|
|
4428
|
+
|
|
4429
|
+
/**
|
|
4430
|
+
* @internal
|
|
4431
|
+
*/
|
|
4432
|
+
private _makeSubscription(
|
|
4433
|
+
subscriptionConfig: SubscriptionConfig,
|
|
4434
|
+
/**
|
|
4435
|
+
* When preparing `args` for a call to `_makeSubscription`, be sure
|
|
4436
|
+
* to carefully apply a default `commitment` property, if necessary.
|
|
4437
|
+
*
|
|
4438
|
+
* - If the user supplied a `commitment` use that.
|
|
4439
|
+
* - Otherwise, if the `Connection::commitment` is set, use that.
|
|
4440
|
+
* - Otherwise, set it to the RPC server default: `finalized`.
|
|
4441
|
+
*
|
|
4442
|
+
* This is extremely important to ensure that these two fundamentally
|
|
4443
|
+
* identical subscriptions produce the same identifying hash:
|
|
4444
|
+
*
|
|
4445
|
+
* - A subscription made without specifying a commitment.
|
|
4446
|
+
* - A subscription made where the commitment specified is the same
|
|
4447
|
+
* as the default applied to the subscription above.
|
|
4448
|
+
*
|
|
4449
|
+
* Example; these two subscriptions must produce the same hash:
|
|
4450
|
+
*
|
|
4451
|
+
* - An `accountSubscribe` subscription for `'PUBKEY'`
|
|
4452
|
+
* - An `accountSubscribe` subscription for `'PUBKEY'` with commitment
|
|
4453
|
+
* `'finalized'`.
|
|
4454
|
+
*
|
|
4455
|
+
* See the 'making a subscription with defaulted params omitted' test
|
|
4456
|
+
* in `connection-subscriptions.ts` for more.
|
|
4457
|
+
*/
|
|
4458
|
+
args: IWSRequestParams,
|
|
4459
|
+
): ClientSubscriptionId {
|
|
4460
|
+
const clientSubscriptionId = this._nextClientSubscriptionId++;
|
|
4461
|
+
const hash = fastStableStringify(
|
|
4462
|
+
[subscriptionConfig.method, args],
|
|
4463
|
+
true /* isArrayProp */,
|
|
4464
|
+
);
|
|
4465
|
+
const existingSubscription = this._subscriptionsByHash[hash];
|
|
4466
|
+
if (existingSubscription === undefined) {
|
|
4467
|
+
this._subscriptionsByHash[hash] = {
|
|
4468
|
+
...subscriptionConfig,
|
|
4469
|
+
args,
|
|
4470
|
+
callbacks: new Set([subscriptionConfig.callback]),
|
|
4471
|
+
state: 'pending',
|
|
4472
|
+
};
|
|
4473
|
+
} else {
|
|
4474
|
+
existingSubscription.callbacks.add(subscriptionConfig.callback);
|
|
4352
4475
|
}
|
|
4476
|
+
this._subscriptionDisposeFunctionsByClientSubscriptionId[
|
|
4477
|
+
clientSubscriptionId
|
|
4478
|
+
] = async () => {
|
|
4479
|
+
delete this._subscriptionDisposeFunctionsByClientSubscriptionId[
|
|
4480
|
+
clientSubscriptionId
|
|
4481
|
+
];
|
|
4482
|
+
const subscription = this._subscriptionsByHash[hash];
|
|
4483
|
+
assert(
|
|
4484
|
+
subscription !== undefined,
|
|
4485
|
+
`Could not find a \`Subscription\` when tearing down client subscription #${clientSubscriptionId}`,
|
|
4486
|
+
);
|
|
4487
|
+
subscription.callbacks.delete(subscriptionConfig.callback);
|
|
4488
|
+
await this._updateSubscriptions();
|
|
4489
|
+
};
|
|
4490
|
+
this._updateSubscriptions();
|
|
4491
|
+
return clientSubscriptionId;
|
|
4353
4492
|
}
|
|
4354
4493
|
|
|
4355
4494
|
/**
|
|
@@ -4364,52 +4503,51 @@ export class Connection {
|
|
|
4364
4503
|
publicKey: PublicKey,
|
|
4365
4504
|
callback: AccountChangeCallback,
|
|
4366
4505
|
commitment?: Commitment,
|
|
4367
|
-
):
|
|
4368
|
-
const
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4506
|
+
): ClientSubscriptionId {
|
|
4507
|
+
const args = this._buildArgs(
|
|
4508
|
+
[publicKey.toBase58()],
|
|
4509
|
+
commitment || this._commitment || 'finalized', // Apply connection/server default.
|
|
4510
|
+
'base64',
|
|
4511
|
+
);
|
|
4512
|
+
return this._makeSubscription(
|
|
4513
|
+
{
|
|
4514
|
+
callback,
|
|
4515
|
+
method: 'accountSubscribe',
|
|
4516
|
+
unsubscribeMethod: 'accountUnsubscribe',
|
|
4517
|
+
},
|
|
4518
|
+
args,
|
|
4519
|
+
);
|
|
4377
4520
|
}
|
|
4378
4521
|
|
|
4379
4522
|
/**
|
|
4380
4523
|
* Deregister an account notification callback
|
|
4381
4524
|
*
|
|
4382
|
-
* @param id subscription id to deregister
|
|
4525
|
+
* @param id client subscription id to deregister
|
|
4383
4526
|
*/
|
|
4384
|
-
async removeAccountChangeListener(
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
console.warn(createSubscriptionWarningMessage(id, 'account change'));
|
|
4392
|
-
}
|
|
4527
|
+
async removeAccountChangeListener(
|
|
4528
|
+
clientSubscriptionId: ClientSubscriptionId,
|
|
4529
|
+
): Promise<void> {
|
|
4530
|
+
await this._unsubscribeClientSubscription(
|
|
4531
|
+
clientSubscriptionId,
|
|
4532
|
+
'account change',
|
|
4533
|
+
);
|
|
4393
4534
|
}
|
|
4394
4535
|
|
|
4395
4536
|
/**
|
|
4396
4537
|
* @internal
|
|
4397
4538
|
*/
|
|
4398
4539
|
_wsOnProgramAccountNotification(notification: Object) {
|
|
4399
|
-
const
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
return;
|
|
4411
|
-
}
|
|
4412
|
-
}
|
|
4540
|
+
const {result, subscription} = create(
|
|
4541
|
+
notification,
|
|
4542
|
+
ProgramAccountNotificationResult,
|
|
4543
|
+
);
|
|
4544
|
+
this._handleServerNotification<ProgramAccountChangeCallback>(subscription, [
|
|
4545
|
+
{
|
|
4546
|
+
accountId: result.value.pubkey,
|
|
4547
|
+
accountInfo: result.value.account,
|
|
4548
|
+
},
|
|
4549
|
+
result.context,
|
|
4550
|
+
]);
|
|
4413
4551
|
}
|
|
4414
4552
|
|
|
4415
4553
|
/**
|
|
@@ -4427,35 +4565,35 @@ export class Connection {
|
|
|
4427
4565
|
callback: ProgramAccountChangeCallback,
|
|
4428
4566
|
commitment?: Commitment,
|
|
4429
4567
|
filters?: GetProgramAccountsFilter[],
|
|
4430
|
-
):
|
|
4431
|
-
const
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4568
|
+
): ClientSubscriptionId {
|
|
4569
|
+
const args = this._buildArgs(
|
|
4570
|
+
[programId.toBase58()],
|
|
4571
|
+
commitment || this._commitment || 'finalized', // Apply connection/server default.
|
|
4572
|
+
'base64' /* encoding */,
|
|
4573
|
+
filters ? {filters: filters} : undefined /* extra */,
|
|
4574
|
+
);
|
|
4575
|
+
return this._makeSubscription(
|
|
4576
|
+
{
|
|
4577
|
+
callback,
|
|
4578
|
+
method: 'programSubscribe',
|
|
4579
|
+
unsubscribeMethod: 'programUnsubscribe',
|
|
4580
|
+
},
|
|
4581
|
+
args,
|
|
4582
|
+
);
|
|
4441
4583
|
}
|
|
4442
4584
|
|
|
4443
4585
|
/**
|
|
4444
4586
|
* Deregister an account notification callback
|
|
4445
4587
|
*
|
|
4446
|
-
* @param id subscription id to deregister
|
|
4588
|
+
* @param id client subscription id to deregister
|
|
4447
4589
|
*/
|
|
4448
|
-
async removeProgramAccountChangeListener(
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
console.warn(
|
|
4456
|
-
createSubscriptionWarningMessage(id, 'program account change'),
|
|
4457
|
-
);
|
|
4458
|
-
}
|
|
4590
|
+
async removeProgramAccountChangeListener(
|
|
4591
|
+
clientSubscriptionId: ClientSubscriptionId,
|
|
4592
|
+
): Promise<void> {
|
|
4593
|
+
await this._unsubscribeClientSubscription(
|
|
4594
|
+
clientSubscriptionId,
|
|
4595
|
+
'program account change',
|
|
4596
|
+
);
|
|
4459
4597
|
}
|
|
4460
4598
|
|
|
4461
4599
|
/**
|
|
@@ -4465,60 +4603,49 @@ export class Connection {
|
|
|
4465
4603
|
filter: LogsFilter,
|
|
4466
4604
|
callback: LogsCallback,
|
|
4467
4605
|
commitment?: Commitment,
|
|
4468
|
-
):
|
|
4469
|
-
const
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4606
|
+
): ClientSubscriptionId {
|
|
4607
|
+
const args = this._buildArgs(
|
|
4608
|
+
[typeof filter === 'object' ? {mentions: [filter.toString()]} : filter],
|
|
4609
|
+
commitment || this._commitment || 'finalized', // Apply connection/server default.
|
|
4610
|
+
);
|
|
4611
|
+
return this._makeSubscription(
|
|
4612
|
+
{
|
|
4613
|
+
callback,
|
|
4614
|
+
method: 'logsSubscribe',
|
|
4615
|
+
unsubscribeMethod: 'logsUnsubscribe',
|
|
4616
|
+
},
|
|
4617
|
+
args,
|
|
4618
|
+
);
|
|
4478
4619
|
}
|
|
4479
4620
|
|
|
4480
4621
|
/**
|
|
4481
4622
|
* Deregister a logs callback.
|
|
4482
4623
|
*
|
|
4483
|
-
* @param id subscription id to deregister.
|
|
4624
|
+
* @param id client subscription id to deregister.
|
|
4484
4625
|
*/
|
|
4485
|
-
async removeOnLogsListener(
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
await this._unsubscribe(subInfo, 'logsUnsubscribe');
|
|
4490
|
-
this._updateSubscriptions();
|
|
4491
|
-
} else {
|
|
4492
|
-
console.warn(createSubscriptionWarningMessage(id, 'logs'));
|
|
4493
|
-
}
|
|
4626
|
+
async removeOnLogsListener(
|
|
4627
|
+
clientSubscriptionId: ClientSubscriptionId,
|
|
4628
|
+
): Promise<void> {
|
|
4629
|
+
await this._unsubscribeClientSubscription(clientSubscriptionId, 'logs');
|
|
4494
4630
|
}
|
|
4495
4631
|
|
|
4496
4632
|
/**
|
|
4497
4633
|
* @internal
|
|
4498
4634
|
*/
|
|
4499
4635
|
_wsOnLogsNotification(notification: Object) {
|
|
4500
|
-
const
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
sub.callback(res.result.value, res.result.context);
|
|
4506
|
-
return;
|
|
4507
|
-
}
|
|
4508
|
-
}
|
|
4636
|
+
const {result, subscription} = create(notification, LogsNotificationResult);
|
|
4637
|
+
this._handleServerNotification<LogsCallback>(subscription, [
|
|
4638
|
+
result.value,
|
|
4639
|
+
result.context,
|
|
4640
|
+
]);
|
|
4509
4641
|
}
|
|
4510
4642
|
|
|
4511
4643
|
/**
|
|
4512
4644
|
* @internal
|
|
4513
4645
|
*/
|
|
4514
4646
|
_wsOnSlotNotification(notification: Object) {
|
|
4515
|
-
const
|
|
4516
|
-
|
|
4517
|
-
if (sub.subscriptionId === res.subscription) {
|
|
4518
|
-
sub.callback(res.result);
|
|
4519
|
-
return;
|
|
4520
|
-
}
|
|
4521
|
-
}
|
|
4647
|
+
const {result, subscription} = create(notification, SlotNotificationResult);
|
|
4648
|
+
this._handleServerNotification<SlotChangeCallback>(subscription, [result]);
|
|
4522
4649
|
}
|
|
4523
4650
|
|
|
4524
4651
|
/**
|
|
@@ -4527,43 +4654,40 @@ export class Connection {
|
|
|
4527
4654
|
* @param callback Function to invoke whenever the slot changes
|
|
4528
4655
|
* @return subscription id
|
|
4529
4656
|
*/
|
|
4530
|
-
onSlotChange(callback: SlotChangeCallback):
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4657
|
+
onSlotChange(callback: SlotChangeCallback): ClientSubscriptionId {
|
|
4658
|
+
return this._makeSubscription(
|
|
4659
|
+
{
|
|
4660
|
+
callback,
|
|
4661
|
+
method: 'slotSubscribe',
|
|
4662
|
+
unsubscribeMethod: 'slotUnsubscribe',
|
|
4663
|
+
},
|
|
4664
|
+
[] /* args */,
|
|
4665
|
+
);
|
|
4538
4666
|
}
|
|
4539
4667
|
|
|
4540
4668
|
/**
|
|
4541
4669
|
* Deregister a slot notification callback
|
|
4542
4670
|
*
|
|
4543
|
-
* @param id subscription id to deregister
|
|
4671
|
+
* @param id client subscription id to deregister
|
|
4544
4672
|
*/
|
|
4545
|
-
async removeSlotChangeListener(
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
console.warn(createSubscriptionWarningMessage(id, 'slot change'));
|
|
4553
|
-
}
|
|
4673
|
+
async removeSlotChangeListener(
|
|
4674
|
+
clientSubscriptionId: ClientSubscriptionId,
|
|
4675
|
+
): Promise<void> {
|
|
4676
|
+
await this._unsubscribeClientSubscription(
|
|
4677
|
+
clientSubscriptionId,
|
|
4678
|
+
'slot change',
|
|
4679
|
+
);
|
|
4554
4680
|
}
|
|
4555
4681
|
|
|
4556
4682
|
/**
|
|
4557
4683
|
* @internal
|
|
4558
4684
|
*/
|
|
4559
4685
|
_wsOnSlotUpdatesNotification(notification: Object) {
|
|
4560
|
-
const
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
}
|
|
4566
|
-
}
|
|
4686
|
+
const {result, subscription} = create(
|
|
4687
|
+
notification,
|
|
4688
|
+
SlotUpdateNotificationResult,
|
|
4689
|
+
);
|
|
4690
|
+
this._handleServerNotification<SlotUpdateCallback>(subscription, [result]);
|
|
4567
4691
|
}
|
|
4568
4692
|
|
|
4569
4693
|
/**
|
|
@@ -4573,29 +4697,51 @@ export class Connection {
|
|
|
4573
4697
|
* @param callback Function to invoke whenever the slot updates
|
|
4574
4698
|
* @return subscription id
|
|
4575
4699
|
*/
|
|
4576
|
-
onSlotUpdate(callback: SlotUpdateCallback):
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4700
|
+
onSlotUpdate(callback: SlotUpdateCallback): ClientSubscriptionId {
|
|
4701
|
+
return this._makeSubscription(
|
|
4702
|
+
{
|
|
4703
|
+
callback,
|
|
4704
|
+
method: 'slotsUpdatesSubscribe',
|
|
4705
|
+
unsubscribeMethod: 'slotsUpdatesUnsubscribe',
|
|
4706
|
+
},
|
|
4707
|
+
[] /* args */,
|
|
4708
|
+
);
|
|
4584
4709
|
}
|
|
4585
4710
|
|
|
4586
4711
|
/**
|
|
4587
4712
|
* Deregister a slot update notification callback
|
|
4588
4713
|
*
|
|
4589
|
-
* @param id subscription id to deregister
|
|
4714
|
+
* @param id client subscription id to deregister
|
|
4590
4715
|
*/
|
|
4591
|
-
async removeSlotUpdateListener(
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4716
|
+
async removeSlotUpdateListener(
|
|
4717
|
+
clientSubscriptionId: ClientSubscriptionId,
|
|
4718
|
+
): Promise<void> {
|
|
4719
|
+
await this._unsubscribeClientSubscription(
|
|
4720
|
+
clientSubscriptionId,
|
|
4721
|
+
'slot update',
|
|
4722
|
+
);
|
|
4723
|
+
}
|
|
4724
|
+
|
|
4725
|
+
/**
|
|
4726
|
+
* @internal
|
|
4727
|
+
*/
|
|
4728
|
+
|
|
4729
|
+
private async _unsubscribeClientSubscription(
|
|
4730
|
+
clientSubscriptionId: ClientSubscriptionId,
|
|
4731
|
+
subscriptionName: string,
|
|
4732
|
+
) {
|
|
4733
|
+
const dispose =
|
|
4734
|
+
this._subscriptionDisposeFunctionsByClientSubscriptionId[
|
|
4735
|
+
clientSubscriptionId
|
|
4736
|
+
];
|
|
4737
|
+
if (dispose) {
|
|
4738
|
+
await dispose();
|
|
4597
4739
|
} else {
|
|
4598
|
-
console.warn(
|
|
4740
|
+
console.warn(
|
|
4741
|
+
'Ignored unsubscribe request because an active subscription with id ' +
|
|
4742
|
+
`\`${clientSubscriptionId}\` for '${subscriptionName}' events ` +
|
|
4743
|
+
'could not be found.',
|
|
4744
|
+
);
|
|
4599
4745
|
}
|
|
4600
4746
|
}
|
|
4601
4747
|
|
|
@@ -4646,32 +4792,32 @@ export class Connection {
|
|
|
4646
4792
|
* @internal
|
|
4647
4793
|
*/
|
|
4648
4794
|
_wsOnSignatureNotification(notification: Object) {
|
|
4649
|
-
const
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
},
|
|
4669
|
-
res.result.context,
|
|
4670
|
-
);
|
|
4671
|
-
}
|
|
4672
|
-
return;
|
|
4673
|
-
}
|
|
4795
|
+
const {result, subscription} = create(
|
|
4796
|
+
notification,
|
|
4797
|
+
SignatureNotificationResult,
|
|
4798
|
+
);
|
|
4799
|
+
if (result.value !== 'receivedSignature') {
|
|
4800
|
+
/**
|
|
4801
|
+
* Special case.
|
|
4802
|
+
* After a signature is processed, RPCs automatically dispose of the
|
|
4803
|
+
* subscription on the server side. We need to track which of these
|
|
4804
|
+
* subscriptions have been disposed in such a way, so that we know
|
|
4805
|
+
* whether the client is dealing with a not-yet-processed signature
|
|
4806
|
+
* (in which case we must tear down the server subscription) or an
|
|
4807
|
+
* already-processed signature (in which case the client can simply
|
|
4808
|
+
* clear out the subscription locally without telling the server).
|
|
4809
|
+
*
|
|
4810
|
+
* NOTE: There is a proposal to eliminate this special case, here:
|
|
4811
|
+
* https://github.com/solana-labs/solana/issues/18892
|
|
4812
|
+
*/
|
|
4813
|
+
this._subscriptionsAutoDisposedByRpc.add(subscription);
|
|
4674
4814
|
}
|
|
4815
|
+
this._handleServerNotification<SignatureSubscriptionCallback>(
|
|
4816
|
+
subscription,
|
|
4817
|
+
result.value === 'receivedSignature'
|
|
4818
|
+
? [{type: 'received'}, result.context]
|
|
4819
|
+
: [{type: 'status', result: result.value}, result.context],
|
|
4820
|
+
);
|
|
4675
4821
|
}
|
|
4676
4822
|
|
|
4677
4823
|
/**
|
|
@@ -4686,20 +4832,32 @@ export class Connection {
|
|
|
4686
4832
|
signature: TransactionSignature,
|
|
4687
4833
|
callback: SignatureResultCallback,
|
|
4688
4834
|
commitment?: Commitment,
|
|
4689
|
-
):
|
|
4690
|
-
const
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4835
|
+
): ClientSubscriptionId {
|
|
4836
|
+
const args = this._buildArgs(
|
|
4837
|
+
[signature],
|
|
4838
|
+
commitment || this._commitment || 'finalized', // Apply connection/server default.
|
|
4839
|
+
);
|
|
4840
|
+
const clientSubscriptionId = this._makeSubscription(
|
|
4841
|
+
{
|
|
4842
|
+
callback: (notification, context) => {
|
|
4843
|
+
if (notification.type === 'status') {
|
|
4844
|
+
callback(notification.result, context);
|
|
4845
|
+
// Signatures subscriptions are auto-removed by the RPC service
|
|
4846
|
+
// so no need to explicitly send an unsubscribe message.
|
|
4847
|
+
try {
|
|
4848
|
+
this.removeSignatureListener(clientSubscriptionId);
|
|
4849
|
+
// eslint-disable-next-line no-empty
|
|
4850
|
+
} catch {
|
|
4851
|
+
// Already removed.
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4854
|
+
},
|
|
4855
|
+
method: 'signatureSubscribe',
|
|
4856
|
+
unsubscribeMethod: 'signatureUnsubscribe',
|
|
4697
4857
|
},
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
this._updateSubscriptions();
|
|
4702
|
-
return id;
|
|
4858
|
+
args,
|
|
4859
|
+
);
|
|
4860
|
+
return clientSubscriptionId;
|
|
4703
4861
|
}
|
|
4704
4862
|
|
|
4705
4863
|
/**
|
|
@@ -4716,45 +4874,59 @@ export class Connection {
|
|
|
4716
4874
|
signature: TransactionSignature,
|
|
4717
4875
|
callback: SignatureSubscriptionCallback,
|
|
4718
4876
|
options?: SignatureSubscriptionOptions,
|
|
4719
|
-
):
|
|
4720
|
-
const
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
options,
|
|
4725
|
-
subscriptionId: null,
|
|
4877
|
+
): ClientSubscriptionId {
|
|
4878
|
+
const {commitment, ...extra} = {
|
|
4879
|
+
...options,
|
|
4880
|
+
commitment:
|
|
4881
|
+
(options && options.commitment) || this._commitment || 'finalized', // Apply connection/server default.
|
|
4726
4882
|
};
|
|
4727
|
-
this.
|
|
4728
|
-
|
|
4883
|
+
const args = this._buildArgs(
|
|
4884
|
+
[signature],
|
|
4885
|
+
commitment,
|
|
4886
|
+
undefined /* encoding */,
|
|
4887
|
+
extra,
|
|
4888
|
+
);
|
|
4889
|
+
const clientSubscriptionId = this._makeSubscription(
|
|
4890
|
+
{
|
|
4891
|
+
callback: (notification, context) => {
|
|
4892
|
+
callback(notification, context);
|
|
4893
|
+
// Signatures subscriptions are auto-removed by the RPC service
|
|
4894
|
+
// so no need to explicitly send an unsubscribe message.
|
|
4895
|
+
try {
|
|
4896
|
+
this.removeSignatureListener(clientSubscriptionId);
|
|
4897
|
+
// eslint-disable-next-line no-empty
|
|
4898
|
+
} catch {
|
|
4899
|
+
// Already removed.
|
|
4900
|
+
}
|
|
4901
|
+
},
|
|
4902
|
+
method: 'signatureSubscribe',
|
|
4903
|
+
unsubscribeMethod: 'signatureUnsubscribe',
|
|
4904
|
+
},
|
|
4905
|
+
args,
|
|
4906
|
+
);
|
|
4907
|
+
return clientSubscriptionId;
|
|
4729
4908
|
}
|
|
4730
4909
|
|
|
4731
4910
|
/**
|
|
4732
4911
|
* Deregister a signature notification callback
|
|
4733
4912
|
*
|
|
4734
|
-
* @param id subscription id to deregister
|
|
4913
|
+
* @param id client subscription id to deregister
|
|
4735
4914
|
*/
|
|
4736
|
-
async removeSignatureListener(
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
console.warn(createSubscriptionWarningMessage(id, 'signature result'));
|
|
4744
|
-
}
|
|
4915
|
+
async removeSignatureListener(
|
|
4916
|
+
clientSubscriptionId: ClientSubscriptionId,
|
|
4917
|
+
): Promise<void> {
|
|
4918
|
+
await this._unsubscribeClientSubscription(
|
|
4919
|
+
clientSubscriptionId,
|
|
4920
|
+
'signature result',
|
|
4921
|
+
);
|
|
4745
4922
|
}
|
|
4746
4923
|
|
|
4747
4924
|
/**
|
|
4748
4925
|
* @internal
|
|
4749
4926
|
*/
|
|
4750
4927
|
_wsOnRootNotification(notification: Object) {
|
|
4751
|
-
const
|
|
4752
|
-
|
|
4753
|
-
if (sub.subscriptionId === res.subscription) {
|
|
4754
|
-
sub.callback(res.result);
|
|
4755
|
-
return;
|
|
4756
|
-
}
|
|
4757
|
-
}
|
|
4928
|
+
const {result, subscription} = create(notification, RootNotificationResult);
|
|
4929
|
+
this._handleServerNotification<RootChangeCallback>(subscription, [result]);
|
|
4758
4930
|
}
|
|
4759
4931
|
|
|
4760
4932
|
/**
|
|
@@ -4763,29 +4935,28 @@ export class Connection {
|
|
|
4763
4935
|
* @param callback Function to invoke whenever the root changes
|
|
4764
4936
|
* @return subscription id
|
|
4765
4937
|
*/
|
|
4766
|
-
onRootChange(callback: RootChangeCallback):
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4938
|
+
onRootChange(callback: RootChangeCallback): ClientSubscriptionId {
|
|
4939
|
+
return this._makeSubscription(
|
|
4940
|
+
{
|
|
4941
|
+
callback,
|
|
4942
|
+
method: 'rootSubscribe',
|
|
4943
|
+
unsubscribeMethod: 'rootUnsubscribe',
|
|
4944
|
+
},
|
|
4945
|
+
[] /* args */,
|
|
4946
|
+
);
|
|
4774
4947
|
}
|
|
4775
4948
|
|
|
4776
4949
|
/**
|
|
4777
4950
|
* Deregister a root notification callback
|
|
4778
4951
|
*
|
|
4779
|
-
* @param id subscription id to deregister
|
|
4952
|
+
* @param id client subscription id to deregister
|
|
4780
4953
|
*/
|
|
4781
|
-
async removeRootChangeListener(
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
console.warn(createSubscriptionWarningMessage(id, 'root change'));
|
|
4789
|
-
}
|
|
4954
|
+
async removeRootChangeListener(
|
|
4955
|
+
clientSubscriptionId: ClientSubscriptionId,
|
|
4956
|
+
): Promise<void> {
|
|
4957
|
+
await this._unsubscribeClientSubscription(
|
|
4958
|
+
clientSubscriptionId,
|
|
4959
|
+
'root change',
|
|
4960
|
+
);
|
|
4790
4961
|
}
|
|
4791
4962
|
}
|