FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

View File

@@ -0,0 +1,95 @@
import type { ErrorType } from '../../errors/utils.js'
import type { Account } from '../../types/account.js'
import type { Chain } from '../../types/chain.js'
import type { EIP1193RequestFn } from '../../types/eip1193.js'
import type { OneOf } from '../../types/utils.js'
import { buildRequest } from '../../utils/buildRequest.js'
import { uid as uid_ } from '../../utils/uid.js'
import type { ClientConfig } from '../createClient.js'
export type TransportConfig<
type extends string = string,
eip1193RequestFn extends EIP1193RequestFn = EIP1193RequestFn,
> = {
/** The name of the transport. */
name: string
/** The key of the transport. */
key: string
/** Methods to include or exclude from executing RPC requests. */
methods?:
| OneOf<
| {
include?: string[] | undefined
}
| {
exclude?: string[] | undefined
}
>
| undefined
/** The JSON-RPC request function that matches the EIP-1193 request spec. */
request: eip1193RequestFn
/** The base delay (in ms) between retries. */
retryDelay?: number | undefined
/** The max number of times to retry. */
retryCount?: number | undefined
/** The timeout (in ms) for requests. */
timeout?: number | undefined
/** The type of the transport. */
type: type
}
export type Transport<
type extends string = string,
rpcAttributes = Record<string, any>,
eip1193RequestFn extends EIP1193RequestFn = EIP1193RequestFn,
> = <chain extends Chain | undefined = Chain>({
chain,
}: {
account?: Account | undefined
chain?: chain | undefined
pollingInterval?: ClientConfig['pollingInterval'] | undefined
retryCount?: TransportConfig['retryCount'] | undefined
timeout?: TransportConfig['timeout'] | undefined
}) => {
config: TransportConfig<type>
request: eip1193RequestFn
value?: rpcAttributes | undefined
}
export type CreateTransportErrorType = ErrorType
/**
* @description Creates an transport intended to be used with a client.
*/
export function createTransport<
type extends string,
rpcAttributes extends Record<string, any>,
>(
{
key,
methods,
name,
request,
retryCount = 3,
retryDelay = 150,
timeout,
type,
}: TransportConfig<type>,
value?: rpcAttributes | undefined,
): ReturnType<Transport<type, rpcAttributes>> {
const uid = uid_()
return {
config: {
key,
methods,
name,
request,
retryCount,
retryDelay,
timeout,
type,
},
request: buildRequest(request, { methods, retryCount, retryDelay, uid }),
value,
}
}

55
node_modules/viem/clients/transports/custom.ts generated vendored Normal file
View File

@@ -0,0 +1,55 @@
import type { ErrorType } from '../../errors/utils.js'
import {
type CreateTransportErrorType,
createTransport,
type Transport,
type TransportConfig,
} from './createTransport.js'
type EthereumProvider = { request(...args: any): Promise<any> }
export type CustomTransportConfig = {
/** The key of the transport. */
key?: TransportConfig['key'] | undefined
/** Methods to include or exclude from executing RPC requests. */
methods?: TransportConfig['methods'] | undefined
/** The name of the transport. */
name?: TransportConfig['name'] | undefined
/** The max number of times to retry. */
retryCount?: TransportConfig['retryCount'] | undefined
/** The base delay (in ms) between retries. */
retryDelay?: TransportConfig['retryDelay'] | undefined
}
export type CustomTransport = Transport<
'custom',
{},
EthereumProvider['request']
>
export type CustomTransportErrorType = CreateTransportErrorType | ErrorType
/**
* @description Creates a custom transport given an EIP-1193 compliant `request` attribute.
*/
export function custom<provider extends EthereumProvider>(
provider: provider,
config: CustomTransportConfig = {},
): CustomTransport {
const {
key = 'custom',
methods,
name = 'Custom Provider',
retryDelay,
} = config
return ({ retryCount: defaultRetryCount }) =>
createTransport({
key,
methods,
name,
request: provider.request.bind(provider),
retryCount: config.retryCount ?? defaultRetryCount,
retryDelay,
type: 'custom',
})
}

317
node_modules/viem/clients/transports/fallback.ts generated vendored Normal file
View File

@@ -0,0 +1,317 @@
import { ExecutionRevertedError } from '../../errors/node.js'
import {
TransactionRejectedRpcError,
UserRejectedRequestError,
WalletConnectSessionSettlementError,
} from '../../errors/rpc.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Chain } from '../../types/chain.js'
import { wait } from '../../utils/wait.js'
import {
type CreateTransportErrorType,
createTransport,
type Transport,
type TransportConfig,
} from './createTransport.js'
// TODO: Narrow `method` & `params` types.
export type OnResponseFn = (
args: {
method: string
params: unknown[]
transport: ReturnType<Transport>
} & (
| {
error?: undefined
response: unknown
status: 'success'
}
| {
error: Error
response?: undefined
status: 'error'
}
),
) => void
type RankOptions = {
/**
* The polling interval (in ms) at which the ranker should ping the RPC URL.
* @default client.pollingInterval
*/
interval?: number | undefined
/**
* Ping method to determine latency.
*/
ping?: (parameters: {
transport: ReturnType<Transport>
}) => Promise<unknown> | undefined
/**
* The number of previous samples to perform ranking on.
* @default 10
*/
sampleCount?: number | undefined
/**
* Timeout when sampling transports.
* @default 1_000
*/
timeout?: number | undefined
/**
* Weights to apply to the scores. Weight values are proportional.
*/
weights?:
| {
/**
* The weight to apply to the latency score.
* @default 0.3
*/
latency?: number | undefined
/**
* The weight to apply to the stability score.
* @default 0.7
*/
stability?: number | undefined
}
| undefined
}
export type FallbackTransportConfig = {
/** The key of the Fallback transport. */
key?: TransportConfig['key'] | undefined
/** The name of the Fallback transport. */
name?: TransportConfig['name'] | undefined
/** Toggle to enable ranking, or rank options. */
rank?: boolean | RankOptions | undefined
/** The max number of times to retry. */
retryCount?: TransportConfig['retryCount'] | undefined
/** The base delay (in ms) between retries. */
retryDelay?: TransportConfig['retryDelay'] | undefined
/** Callback on whether an error should throw or try the next transport in the fallback. */
shouldThrow?: (error: Error) => boolean | undefined
}
export type FallbackTransport<
transports extends readonly Transport[] = readonly Transport[],
> = Transport<
'fallback',
{
onResponse: (fn: OnResponseFn) => void
transports: {
[key in keyof transports]: ReturnType<transports[key]>
}
}
>
export type FallbackTransportErrorType = CreateTransportErrorType | ErrorType
export function fallback<const transports extends readonly Transport[]>(
transports_: transports,
config: FallbackTransportConfig = {},
): FallbackTransport<transports> {
const {
key = 'fallback',
name = 'Fallback',
rank = false,
shouldThrow: shouldThrow_ = shouldThrow,
retryCount,
retryDelay,
} = config
return (({ chain, pollingInterval = 4_000, timeout, ...rest }) => {
let transports = transports_
let onResponse: OnResponseFn = () => {}
const transport = createTransport(
{
key,
name,
async request({ method, params }) {
let includes: boolean | undefined
const fetch = async (i = 0): Promise<any> => {
const transport = transports[i]({
...rest,
chain,
retryCount: 0,
timeout,
})
try {
const response = await transport.request({
method,
params,
} as any)
onResponse({
method,
params: params as unknown[],
response,
transport,
status: 'success',
})
return response
} catch (err) {
onResponse({
error: err as Error,
method,
params: params as unknown[],
transport,
status: 'error',
})
if (shouldThrow_(err as Error)) throw err
// If we've reached the end of the fallbacks, throw the error.
if (i === transports.length - 1) throw err
// Check if at least one other transport includes the method
includes ??= transports.slice(i + 1).some((transport) => {
const { include, exclude } =
transport({ chain }).config.methods || {}
if (include) return include.includes(method)
if (exclude) return !exclude.includes(method)
return true
})
if (!includes) throw err
// Otherwise, try the next fallback.
return fetch(i + 1)
}
}
return fetch()
},
retryCount,
retryDelay,
type: 'fallback',
},
{
onResponse: (fn: OnResponseFn) => (onResponse = fn),
transports: transports.map((fn) => fn({ chain, retryCount: 0 })),
},
)
if (rank) {
const rankOptions = (typeof rank === 'object' ? rank : {}) as RankOptions
rankTransports({
chain,
interval: rankOptions.interval ?? pollingInterval,
onTransports: (transports_) => (transports = transports_ as transports),
ping: rankOptions.ping,
sampleCount: rankOptions.sampleCount,
timeout: rankOptions.timeout,
transports,
weights: rankOptions.weights,
})
}
return transport
}) as FallbackTransport<transports>
}
export function shouldThrow(error: Error) {
if ('code' in error && typeof error.code === 'number') {
if (
error.code === TransactionRejectedRpcError.code ||
error.code === UserRejectedRequestError.code ||
error.code === WalletConnectSessionSettlementError.code ||
ExecutionRevertedError.nodeMessage.test(error.message) ||
error.code === 5000 // CAIP UserRejectedRequestError
)
return true
}
return false
}
/** @internal */
export function rankTransports({
chain,
interval = 4_000,
onTransports,
ping,
sampleCount = 10,
timeout = 1_000,
transports,
weights = {},
}: {
chain?: Chain | undefined
interval: RankOptions['interval']
onTransports: (transports: readonly Transport[]) => void
ping?: RankOptions['ping'] | undefined
sampleCount?: RankOptions['sampleCount'] | undefined
timeout?: RankOptions['timeout'] | undefined
transports: readonly Transport[]
weights?: RankOptions['weights'] | undefined
}) {
const { stability: stabilityWeight = 0.7, latency: latencyWeight = 0.3 } =
weights
type SampleData = { latency: number; success: number }
type Sample = SampleData[]
const samples: Sample[] = []
const rankTransports_ = async () => {
// 1. Take a sample from each Transport.
const sample: Sample = await Promise.all(
transports.map(async (transport) => {
const transport_ = transport({ chain, retryCount: 0, timeout })
const start = Date.now()
let end: number
let success: number
try {
await (ping
? ping({ transport: transport_ })
: transport_.request({ method: 'net_listening' }))
success = 1
} catch {
success = 0
} finally {
end = Date.now()
}
const latency = end - start
return { latency, success }
}),
)
// 2. Store the sample. If we have more than `sampleCount` samples, remove
// the oldest sample.
samples.push(sample)
if (samples.length > sampleCount) samples.shift()
// 3. Calculate the max latency from samples.
const maxLatency = Math.max(
...samples.map((sample) =>
Math.max(...sample.map(({ latency }) => latency)),
),
)
// 4. Calculate the score for each Transport.
const scores = transports
.map((_, i) => {
const latencies = samples.map((sample) => sample[i].latency)
const meanLatency =
latencies.reduce((acc, latency) => acc + latency, 0) /
latencies.length
const latencyScore = 1 - meanLatency / maxLatency
const successes = samples.map((sample) => sample[i].success)
const stabilityScore =
successes.reduce((acc, success) => acc + success, 0) /
successes.length
if (stabilityScore === 0) return [0, i]
return [
latencyWeight * latencyScore + stabilityWeight * stabilityScore,
i,
]
})
.sort((a, b) => b[0] - a[0])
// 5. Sort the Transports by score.
onTransports(scores.map(([, i]) => transports[i]))
// 6. Wait, and then rank again.
await wait(interval)
rankTransports_()
}
rankTransports_()
}

175
node_modules/viem/clients/transports/http.ts generated vendored Normal file
View File

@@ -0,0 +1,175 @@
import { RpcRequestError } from '../../errors/request.js'
import {
UrlRequiredError,
type UrlRequiredErrorType,
} from '../../errors/transport.js'
import type { ErrorType } from '../../errors/utils.js'
import type { EIP1193RequestFn, RpcSchema } from '../../types/eip1193.js'
import type { RpcRequest } from '../../types/rpc.js'
import { createBatchScheduler } from '../../utils/promise/createBatchScheduler.js'
import {
getHttpRpcClient,
type HttpRpcClientOptions,
} from '../../utils/rpc/http.js'
import {
type CreateTransportErrorType,
createTransport,
type Transport,
type TransportConfig,
} from './createTransport.js'
export type HttpTransportConfig<
rpcSchema extends RpcSchema | undefined = undefined,
raw extends boolean = false,
> = {
/**
* Whether to enable Batch JSON-RPC.
* @link https://www.jsonrpc.org/specification#batch
*/
batch?:
| boolean
| {
/** The maximum number of JSON-RPC requests to send in a batch. @default 1_000 */
batchSize?: number | undefined
/** The maximum number of milliseconds to wait before sending a batch. @default 0 */
wait?: number | undefined
}
| undefined
fetchFn?: HttpRpcClientOptions['fetchFn'] | undefined
/**
* Request configuration to pass to `fetch`.
* @link https://developer.mozilla.org/en-US/docs/Web/API/fetch
*/
fetchOptions?: HttpRpcClientOptions['fetchOptions'] | undefined
/** A callback to handle the response from `fetch`. */
onFetchRequest?: HttpRpcClientOptions['onRequest'] | undefined
/** A callback to handle the response from `fetch`. */
onFetchResponse?: HttpRpcClientOptions['onResponse'] | undefined
/** The key of the HTTP transport. */
key?: TransportConfig['key'] | undefined
/** Methods to include or exclude from executing RPC requests. */
methods?: TransportConfig['methods'] | undefined
/** The name of the HTTP transport. */
name?: TransportConfig['name'] | undefined
/** Whether to return JSON RPC errors as responses instead of throwing. */
raw?: raw | boolean | undefined
/** The max number of times to retry. */
retryCount?: TransportConfig['retryCount'] | undefined
/** The base delay (in ms) between retries. */
retryDelay?: TransportConfig['retryDelay'] | undefined
/** Typed JSON-RPC schema for the transport. */
rpcSchema?: rpcSchema | RpcSchema | undefined
/** The timeout (in ms) for the HTTP request. Default: 10_000 */
timeout?: TransportConfig['timeout'] | undefined
}
export type HttpTransport<
rpcSchema extends RpcSchema | undefined = undefined,
raw extends boolean = false,
> = Transport<
'http',
{
fetchOptions?: HttpTransportConfig['fetchOptions'] | undefined
url?: string | undefined
},
EIP1193RequestFn<rpcSchema, raw>
>
export type HttpTransportErrorType =
| CreateTransportErrorType
| UrlRequiredErrorType
| ErrorType
/**
* @description Creates a HTTP transport that connects to a JSON-RPC API.
*/
export function http<
rpcSchema extends RpcSchema | undefined = undefined,
raw extends boolean = false,
>(
/** URL of the JSON-RPC API. Defaults to the chain's public RPC URL. */
url?: string | undefined,
config: HttpTransportConfig<rpcSchema, raw> = {},
): HttpTransport<rpcSchema, raw> {
const {
batch,
fetchFn,
fetchOptions,
key = 'http',
methods,
name = 'HTTP JSON-RPC',
onFetchRequest,
onFetchResponse,
retryDelay,
raw,
} = config
return ({ chain, retryCount: retryCount_, timeout: timeout_ }) => {
const { batchSize = 1000, wait = 0 } =
typeof batch === 'object' ? batch : {}
const retryCount = config.retryCount ?? retryCount_
const timeout = timeout_ ?? config.timeout ?? 10_000
const url_ = url || chain?.rpcUrls.default.http[0]
if (!url_) throw new UrlRequiredError()
const rpcClient = getHttpRpcClient(url_, {
fetchFn,
fetchOptions,
onRequest: onFetchRequest,
onResponse: onFetchResponse,
timeout,
})
return createTransport(
{
key,
methods,
name,
async request({ method, params }) {
const body = { method, params }
const { schedule } = createBatchScheduler({
id: url_,
wait,
shouldSplitBatch(requests) {
return requests.length > batchSize
},
fn: (body: RpcRequest[]) =>
rpcClient.request({
body,
}),
sort: (a, b) => a.id - b.id,
})
const fn = async (body: RpcRequest) =>
batch
? schedule(body)
: [
await rpcClient.request({
body,
}),
]
const [{ error, result }] = await fn(body)
if (raw) return { error, result }
if (error)
throw new RpcRequestError({
body,
error,
url: url_,
})
return result
},
retryCount,
retryDelay,
timeout,
type: 'http',
},
{
fetchOptions,
url: url_,
},
)
}
}

177
node_modules/viem/clients/transports/ipc.ts generated vendored Normal file
View File

@@ -0,0 +1,177 @@
import type { Address } from 'abitype'
import { RpcRequestError } from '../../errors/request.js'
import type { UrlRequiredErrorType } from '../../errors/transport.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Hash, LogTopic } from '../../types/misc.js'
import type { RpcResponse } from '../../types/rpc.js'
import {
type GetIpcRpcClientOptions,
getIpcRpcClient,
type IpcRpcClient,
} from '../../utils/rpc/ipc.js'
import {
type CreateTransportErrorType,
createTransport,
type Transport,
type TransportConfig,
} from './createTransport.js'
type IpcTransportSubscribeParameters = {
onData: (data: RpcResponse) => void
onError?: ((error: any) => void) | undefined
}
type IpcTransportSubscribeReturnType = {
subscriptionId: Hash
unsubscribe: () => Promise<RpcResponse<boolean>>
}
type IpcTransportSubscribe = {
subscribe(
args: IpcTransportSubscribeParameters &
(
| {
params: ['newHeads']
}
| {
params: ['newPendingTransactions']
}
| {
params: [
'logs',
{
address?: Address | Address[]
topics?: LogTopic[]
},
]
}
| {
params: ['syncing']
}
),
): Promise<IpcTransportSubscribeReturnType>
}
export type IpcTransportConfig = {
/** The key of the Ipc transport. */
key?: TransportConfig['key'] | undefined
/** Methods to include or exclude from executing RPC requests. */
methods?: TransportConfig['methods'] | undefined
/** The name of the Ipc transport. */
name?: TransportConfig['name'] | undefined
/**
* Whether or not to attempt to reconnect on socket failure.
* @default true
*/
reconnect?: GetIpcRpcClientOptions['reconnect'] | undefined
/** The max number of times to retry. */
retryCount?: TransportConfig['retryCount'] | undefined
/** The base delay (in ms) between retries. */
retryDelay?: TransportConfig['retryDelay'] | undefined
/** The timeout (in ms) for async Ipc requests. Default: 10_000 */
timeout?: TransportConfig['timeout'] | undefined
}
export type IpcTransport = Transport<
'ipc',
{
getRpcClient(): Promise<IpcRpcClient>
subscribe: IpcTransportSubscribe['subscribe']
}
>
export type IpcTransportErrorType =
| CreateTransportErrorType
| UrlRequiredErrorType
| ErrorType
/**
* @description Creates an IPC transport that connects to a JSON-RPC API.
*/
export function ipc(
path: string,
config: IpcTransportConfig = {},
): IpcTransport {
const {
key = 'ipc',
methods,
name = 'IPC JSON-RPC',
reconnect,
retryDelay,
} = config
return ({ retryCount: retryCount_, timeout: timeout_ }) => {
const retryCount = config.retryCount ?? retryCount_
const timeout = timeout_ ?? config.timeout ?? 10_000
return createTransport(
{
key,
methods,
name,
async request({ method, params }) {
const body = { method, params }
const rpcClient = await getIpcRpcClient(path, { reconnect })
const { error, result } = await rpcClient.requestAsync({
body,
timeout,
})
if (error)
throw new RpcRequestError({
body,
error,
url: path,
})
return result
},
retryCount,
retryDelay,
timeout,
type: 'ipc',
},
{
getRpcClient() {
return getIpcRpcClient(path)
},
async subscribe({ params, onData, onError }: any) {
const rpcClient = await getIpcRpcClient(path)
const { result: subscriptionId } = await new Promise<any>(
(resolve, reject) =>
rpcClient.request({
body: {
method: 'eth_subscribe',
params,
},
onResponse(response) {
if (response.error) {
reject(response.error)
onError?.(response.error)
return
}
if (typeof response.id === 'number') {
resolve(response)
return
}
if (response.method !== 'eth_subscription') return
onData(response.params)
},
}),
)
return {
subscriptionId,
async unsubscribe() {
return new Promise<any>((resolve) =>
rpcClient.request({
body: {
method: 'eth_unsubscribe',
params: [subscriptionId],
},
onResponse: resolve,
}),
)
},
}
},
},
)
}
}

203
node_modules/viem/clients/transports/webSocket.ts generated vendored Normal file
View File

@@ -0,0 +1,203 @@
import type { Address } from 'abitype'
import { RpcRequestError } from '../../errors/request.js'
import {
UrlRequiredError,
type UrlRequiredErrorType,
} from '../../errors/transport.js'
import type { ErrorType } from '../../errors/utils.js'
import type { Hash, LogTopic } from '../../types/misc.js'
import type { RpcResponse } from '../../types/rpc.js'
import { getSocket } from '../../utils/rpc/compat.js'
import type { SocketRpcClient } from '../../utils/rpc/socket.js'
import {
type GetWebSocketRpcClientOptions,
getWebSocketRpcClient,
} from '../../utils/rpc/webSocket.js'
import {
type CreateTransportErrorType,
createTransport,
type Transport,
type TransportConfig,
} from './createTransport.js'
type WebSocketTransportSubscribeParameters = {
onData: (data: RpcResponse) => void
onError?: ((error: any) => void) | undefined
}
type WebSocketTransportSubscribeReturnType = {
subscriptionId: Hash
unsubscribe: () => Promise<RpcResponse<boolean>>
}
type WebSocketTransportSubscribe = {
subscribe(
args: WebSocketTransportSubscribeParameters &
(
| {
params: ['newHeads']
}
| {
params: ['newPendingTransactions']
}
| {
params: [
'logs',
{
address?: Address | Address[]
topics?: LogTopic[]
},
]
}
| {
params: ['syncing']
}
),
): Promise<WebSocketTransportSubscribeReturnType>
}
export type WebSocketTransportConfig = {
/**
* Whether or not to send keep-alive ping messages.
* @default true
*/
keepAlive?: GetWebSocketRpcClientOptions['keepAlive'] | undefined
/** The key of the WebSocket transport. */
key?: TransportConfig['key'] | undefined
/** Methods to include or exclude from executing RPC requests. */
methods?: TransportConfig['methods'] | undefined
/** The name of the WebSocket transport. */
name?: TransportConfig['name'] | undefined
/**
* Whether or not to attempt to reconnect on socket failure.
* @default true
*/
reconnect?: GetWebSocketRpcClientOptions['reconnect'] | undefined
/** The max number of times to retry. */
retryCount?: TransportConfig['retryCount'] | undefined
/** The base delay (in ms) between retries. */
retryDelay?: TransportConfig['retryDelay'] | undefined
/** The timeout (in ms) for async WebSocket requests. Default: 10_000 */
timeout?: TransportConfig['timeout'] | undefined
}
export type WebSocketTransport = Transport<
'webSocket',
{
/**
* @deprecated use `getRpcClient` instead.
*/
getSocket(): Promise<WebSocket>
getRpcClient(): Promise<SocketRpcClient<WebSocket>>
subscribe: WebSocketTransportSubscribe['subscribe']
}
>
export type WebSocketTransportErrorType =
| CreateTransportErrorType
| UrlRequiredErrorType
| ErrorType
/**
* @description Creates a WebSocket transport that connects to a JSON-RPC API.
*/
export function webSocket(
/** URL of the JSON-RPC API. Defaults to the chain's public RPC URL. */
url?: string,
config: WebSocketTransportConfig = {},
): WebSocketTransport {
const {
keepAlive,
key = 'webSocket',
methods,
name = 'WebSocket JSON-RPC',
reconnect,
retryDelay,
} = config
return ({ chain, retryCount: retryCount_, timeout: timeout_ }) => {
const retryCount = config.retryCount ?? retryCount_
const timeout = timeout_ ?? config.timeout ?? 10_000
const url_ = url || chain?.rpcUrls.default.webSocket?.[0]
const wsRpcClientOpts = { keepAlive, reconnect }
if (!url_) throw new UrlRequiredError()
return createTransport(
{
key,
methods,
name,
async request({ method, params }) {
const body = { method, params }
const rpcClient = await getWebSocketRpcClient(url_, wsRpcClientOpts)
const { error, result } = await rpcClient.requestAsync({
body,
timeout,
})
if (error)
throw new RpcRequestError({
body,
error,
url: url_,
})
return result
},
retryCount,
retryDelay,
timeout,
type: 'webSocket',
},
{
getSocket() {
return getSocket(url_)
},
getRpcClient() {
return getWebSocketRpcClient(url_, wsRpcClientOpts)
},
async subscribe({ params, onData, onError }: any) {
const rpcClient = await getWebSocketRpcClient(url_, wsRpcClientOpts)
const { result: subscriptionId } = await new Promise<any>(
(resolve, reject) =>
rpcClient.request({
body: {
method: 'eth_subscribe',
params,
},
onError(error) {
reject(error)
onError?.(error)
return
},
onResponse(response) {
if (response.error) {
reject(response.error)
onError?.(response.error)
return
}
if (typeof response.id === 'number') {
resolve(response)
return
}
if (response.method !== 'eth_subscription') return
onData(response.params)
},
}),
)
return {
subscriptionId,
async unsubscribe() {
return new Promise<any>((resolve) =>
rpcClient.request({
body: {
method: 'eth_unsubscribe',
params: [subscriptionId],
},
onResponse: resolve,
}),
)
},
}
},
},
)
}
}