Provider Injection (#253)
* Add PostMessageProvider * Remove coder * Add provier in state * Add msg handling * Fix lint * Fix little bugs * Make send work * Small tweaks * Make subscriptions work * Add readme * Remove useless file * Roder alphabet * Fix lint * List & start provider * Add unsubscribe * List and Use providers * Update packages/extension/src/background/types.ts Co-Authored-By: Jaco Greeff <jacogr@gmail.com> * Update packages/extension/src/background/handlers/State.ts Co-Authored-By: Jaco Greeff <jacogr@gmail.com> * Update packages/extension/src/background/handlers/State.ts Co-Authored-By: Jaco Greeff <jacogr@gmail.com> * Update packages/extension/src/background/handlers/State.ts Co-Authored-By: Jaco Greeff <jacogr@gmail.com> * Update packages/extension/src/background/handlers/State.ts Co-Authored-By: Jaco Greeff <jacogr@gmail.com> * Update packages/extension-dapp/src/index.ts Co-Authored-By: Jaco Greeff <jacogr@gmail.com> * Update README * Remove circular dep * Remove useless function * Update README Co-authored-by: Jaco Greeff <jacogr@gmail.com>
This commit is contained in:
co-authored by
Jaco Greeff
parent
602dfdc026
commit
452ce41b71
@@ -13,7 +13,7 @@ A basic extractor that manipulates the `window.injectedWeb3` to retrieve all the
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { web3Accounts, web3Enable, web3FromAddress } from '@polkadot/extension-dapp';
|
||||
import { web3Accounts, web3Enable, web3FromAddress, web3ListRpcProviders, web3UseRpcProvider } from '@polkadot/extension-dapp';
|
||||
|
||||
// returns an array of all the injected sources
|
||||
// (this needs to be called first, before other requests)
|
||||
@@ -35,4 +35,12 @@ api.setSigner(injector.signer);
|
||||
api.tx.balances
|
||||
.transfer('5C5555yEXUcmEJ5kkcCMvdZjUo7NGJiQJMS7vZXEeoMhj3VQ', 123456)
|
||||
.signAndSend('5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFabHE', (status) => { ... });
|
||||
|
||||
// retrieve all the RPC providers from a particular source. this returns a map
|
||||
// of string->ProviderMeta, where the string represents an unique key to identify
|
||||
// a provider, and ProviderMeta shows metadata about this provider
|
||||
const allProviders = web3ListRpcProviders('polkadot-js');
|
||||
// assuming one of the keys in `allProviders` is 'kusama-cc3', we can then use that provider
|
||||
const { provider } = web3UseRpcProvider('polkadot-js', 'kusama-cc3');
|
||||
const head = await provider.send('chain_getHeader', []); // shows latest header
|
||||
```
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Injected, InjectedAccount, InjectedAccountWithMeta, InjectedExtension, InjectedExtensionInfo, InjectedWindow, Unsubcall } from '@polkadot/extension-inject/types';
|
||||
import { Injected, InjectedAccount, InjectedAccountWithMeta, InjectedExtension, InjectedExtensionInfo, InjectedProviderWithMeta, InjectedWindow, Unsubcall, ProviderList } from '@polkadot/extension-inject/types';
|
||||
|
||||
// just a helper (otherwise we cast all-over, so shorter and more readable)
|
||||
const win = window as Window & InjectedWindow;
|
||||
@@ -176,3 +176,29 @@ export async function web3FromAddress (address: string): Promise<InjectedExtensi
|
||||
|
||||
return web3FromSource(found.meta.source);
|
||||
}
|
||||
|
||||
// retrieve all providers exposed by one source
|
||||
export async function web3ListRpcProviders (source: string): Promise<ProviderList | null> {
|
||||
const { provider } = await web3FromSource(source);
|
||||
|
||||
if (!provider) {
|
||||
console.warn(`Extension ${source} does not expose any provider`);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return provider.listProviders();
|
||||
}
|
||||
|
||||
// retrieve all providers exposed by one source
|
||||
export async function web3UseRpcProvider (source: string, key: string): Promise<InjectedProviderWithMeta> {
|
||||
const { provider } = await web3FromSource(source);
|
||||
|
||||
if (!provider) {
|
||||
throw new Error(`Extension ${source} does not expose any provider`);
|
||||
}
|
||||
|
||||
const meta = await provider.startProvider(key);
|
||||
|
||||
return { provider, meta };
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Signer as InjectedSigner } from '@polkadot/api/types';
|
||||
import { ProviderInterface } from '@polkadot/rpc-provider/types';
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
type This = typeof globalThis;
|
||||
@@ -34,9 +35,35 @@ export interface InjectedExtensionInfo {
|
||||
version: string;
|
||||
}
|
||||
|
||||
// Metadata about a provider
|
||||
export interface ProviderMeta {
|
||||
// Network of the provider
|
||||
network: string;
|
||||
// Light or full node
|
||||
node: 'full' | 'light';
|
||||
// The extension source
|
||||
source: string;
|
||||
// Provider transport: 'WsProvider' etc.
|
||||
transport: string;
|
||||
}
|
||||
|
||||
export type ProviderList = Record<string, ProviderMeta>
|
||||
|
||||
export interface InjectedProvider extends ProviderInterface {
|
||||
listProviders: () => Promise<ProviderList>;
|
||||
startProvider: (key: string) => Promise<ProviderMeta>;
|
||||
}
|
||||
|
||||
export interface InjectedProviderWithMeta {
|
||||
// provider will actually always be a PostMessageProvider, which implements InjectedProvider
|
||||
provider: InjectedProvider;
|
||||
meta: ProviderMeta;
|
||||
}
|
||||
|
||||
export interface Injected {
|
||||
accounts: InjectedAccounts;
|
||||
signer: InjectedSigner;
|
||||
provider?: InjectedProvider;
|
||||
}
|
||||
|
||||
export interface InjectedWindowProvider {
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AccountJson, AuthorizeRequest, RequestAuthorizeTab, RequestSign, ResponseSigning, SigningRequest } from '../types';
|
||||
import { ProviderMeta } from '@polkadot/extension-inject/types';
|
||||
import { JsonRpcResponse, ProviderInterface, ProviderInterfaceCallback } from '@polkadot/rpc-provider/types';
|
||||
|
||||
import { AccountJson, AuthorizeRequest, RequestAuthorizeTab, RequestRpcSend, RequestRpcSubscribe, RequestSign, ResponseRpcListProviders, ResponseSigning, SigningRequest, RequestRpcUnsubscribe } from '../types';
|
||||
|
||||
import extension from 'extensionizer';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
@@ -25,6 +28,15 @@ type AuthUrls = Record<string, {
|
||||
url: string;
|
||||
}>;
|
||||
|
||||
// List of providers passed into constructor. This is the list of providers
|
||||
// exposed by the extension.
|
||||
type Providers = Record<string, {
|
||||
meta: ProviderMeta;
|
||||
// The provider is not running at init, calling this will instantiate the
|
||||
// provider.
|
||||
start: () => ProviderInterface;
|
||||
}>
|
||||
|
||||
interface SignRequest {
|
||||
account: AccountJson;
|
||||
id: string;
|
||||
@@ -56,6 +68,12 @@ export default class State {
|
||||
|
||||
readonly #authRequests: Record<string, AuthRequest> = {};
|
||||
|
||||
// Map of providers currently injected in tabs
|
||||
readonly #injectedProviders: Map<chrome.runtime.Port, ProviderInterface> = new Map();
|
||||
|
||||
// Map of all providers exposed by the extension, they are retrievable by key
|
||||
readonly #providers: Providers;
|
||||
|
||||
readonly #signRequests: Record<string, SignRequest> = {};
|
||||
|
||||
#windows: number[] = [];
|
||||
@@ -64,6 +82,10 @@ export default class State {
|
||||
|
||||
public readonly signSubject: BehaviorSubject<SigningRequest[]> = new BehaviorSubject([] as SigningRequest[]);
|
||||
|
||||
constructor (providers: Providers = {}) {
|
||||
this.#providers = providers;
|
||||
}
|
||||
|
||||
public get hasAuthRequests (): boolean {
|
||||
return this.numAuthRequests === 0;
|
||||
}
|
||||
@@ -213,6 +235,69 @@ export default class State {
|
||||
return this.#signRequests[id];
|
||||
}
|
||||
|
||||
// List all providers the extension is exposing
|
||||
public rpcListProviders (): Promise<ResponseRpcListProviders> {
|
||||
return Promise.resolve(Object.keys(this.#providers).reduce((acc, key) => {
|
||||
acc[key] = this.#providers[key].meta;
|
||||
|
||||
return acc;
|
||||
}, {} as ResponseRpcListProviders));
|
||||
}
|
||||
|
||||
public rpcSend (request: RequestRpcSend, port: chrome.runtime.Port): Promise<JsonRpcResponse> {
|
||||
const provider = this.#injectedProviders.get(port);
|
||||
|
||||
assert(provider, 'Cannot call pub(rpc.subscribe) before provider has been set');
|
||||
|
||||
return provider.send(request.method, request.params);
|
||||
}
|
||||
|
||||
// Start a provider, return its meta
|
||||
public rpcStartProvider (key: string, port: chrome.runtime.Port): Promise<ProviderMeta> {
|
||||
assert(Object.keys(this.#providers).includes(key), `Provider ${key} is not exposed by extension`);
|
||||
|
||||
if (this.#injectedProviders.get(port)) {
|
||||
return Promise.resolve(this.#providers[key].meta);
|
||||
}
|
||||
|
||||
// Instantiate the provider
|
||||
this.#injectedProviders.set(port, this.#providers[key].start());
|
||||
|
||||
// Close provider connection when page is closed
|
||||
port.onDisconnect.addListener((): void => {
|
||||
const provider = this.#injectedProviders.get(port);
|
||||
if (provider) {
|
||||
provider.disconnect();
|
||||
}
|
||||
this.#injectedProviders.delete(port);
|
||||
});
|
||||
|
||||
return Promise.resolve(this.#providers[key].meta);
|
||||
}
|
||||
|
||||
public rpcSubscribe (request: RequestRpcSubscribe, cb: ProviderInterfaceCallback, port: chrome.runtime.Port): Promise<number> {
|
||||
const provider = this.#injectedProviders.get(port);
|
||||
|
||||
assert(provider, 'Cannot call pub(rpc.subscribe) before provider has been set');
|
||||
|
||||
return provider.subscribe(
|
||||
request.type,
|
||||
request.method,
|
||||
request.params,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
||||
// @ts-ignore WsProvider gives me (error, result)=>void, whereas (result)=>void is expected
|
||||
(_error, res) => { cb(res); }
|
||||
);
|
||||
}
|
||||
|
||||
public rpcUnsubscribe (request: RequestRpcUnsubscribe, port: chrome.runtime.Port): Promise<boolean> {
|
||||
const provider = this.#injectedProviders.get(port);
|
||||
|
||||
assert(provider, 'Cannot call pub(rpc.unsubscribe) before provider has been set');
|
||||
|
||||
return provider.unsubscribe(request.type, request.method, request.subscriptionId);
|
||||
}
|
||||
|
||||
public sign (url: string, request: RequestSign, account: AccountJson): Promise<ResponseSigning> {
|
||||
const id = getId();
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { InjectedAccount } from '@polkadot/extension-inject/types';
|
||||
import { InjectedAccount, ProviderMeta } from '@polkadot/extension-inject/types';
|
||||
import { KeyringPair } from '@polkadot/keyring/types';
|
||||
import { RequestAuthorizeTab, ResponseSigning, RequestTypes, ResponseTypes, MessageTypes } from '../types';
|
||||
import { JsonRpcResponse } from '@polkadot/rpc-provider/types';
|
||||
import { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types';
|
||||
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||
import { RequestAuthorizeTab, ResponseSigning, RequestTypes, ResponseTypes, MessageTypes, ResponseRpcListProviders, RequestRpcSend, RequestRpcSubscribe, RequestRpcUnsubscribe } from '../types';
|
||||
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import accountsObservable from '@polkadot/ui-keyring/observable/accounts';
|
||||
@@ -72,6 +73,34 @@ export default class Tabs {
|
||||
return this.#state.sign(url, new RequestExtrinsicSign(request), { address, ...pair.meta });
|
||||
}
|
||||
|
||||
private rpcListProviders (): Promise<ResponseRpcListProviders> {
|
||||
return this.#state.rpcListProviders();
|
||||
}
|
||||
|
||||
private rpcSend (request: RequestRpcSend, port: chrome.runtime.Port): Promise<JsonRpcResponse> {
|
||||
return this.#state.rpcSend(request, port);
|
||||
}
|
||||
|
||||
private rpcStartProvider (key: string, port: chrome.runtime.Port): Promise<ProviderMeta> {
|
||||
return this.#state.rpcStartProvider(key, port);
|
||||
}
|
||||
|
||||
private async rpcSubscribe (request: RequestRpcSubscribe, id: string, port: chrome.runtime.Port): Promise<boolean> {
|
||||
const cb = createSubscription<'pub(rpc.subscribe)'>(id, port);
|
||||
const subscriptionId = await this.#state.rpcSubscribe(request, cb, port);
|
||||
|
||||
port.onDisconnect.addListener((): void => {
|
||||
unsubscribe(id);
|
||||
this.rpcUnsubscribe({ ...request, subscriptionId }, port);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async rpcUnsubscribe (request: RequestRpcUnsubscribe, port: chrome.runtime.Port): Promise<boolean> {
|
||||
return this.#state.rpcUnsubscribe(request, port);
|
||||
}
|
||||
|
||||
public async handle<TMessageType extends MessageTypes> (id: string, type: TMessageType, request: RequestTypes[TMessageType], url: string, port: chrome.runtime.Port): Promise<ResponseTypes[keyof ResponseTypes]> {
|
||||
if (type !== 'pub(authorize.tab)') {
|
||||
this.#state.ensureUrlAuthorized(url);
|
||||
@@ -93,6 +122,21 @@ export default class Tabs {
|
||||
case 'pub(extrinsic.sign)':
|
||||
return this.extrinsicSign(url, request as SignerPayloadJSON);
|
||||
|
||||
case 'pub(rpc.listProviders)':
|
||||
return this.rpcListProviders();
|
||||
|
||||
case 'pub(rpc.send)':
|
||||
return this.rpcSend(request as RequestRpcSend, port);
|
||||
|
||||
case 'pub(rpc.startProvider)':
|
||||
return this.rpcStartProvider(request as string, port);
|
||||
|
||||
case 'pub(rpc.subscribe)':
|
||||
return this.rpcSubscribe(request as RequestRpcSubscribe, id, port);
|
||||
|
||||
case 'pub(rpc.unsubscribe)':
|
||||
return this.rpcUnsubscribe(request as RequestRpcUnsubscribe, port);
|
||||
|
||||
default:
|
||||
throw new Error(`Unable to handle message of type ${type}`);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { InjectedAccount } from '@polkadot/extension-inject/types';
|
||||
import { InjectedAccount, ProviderList, ProviderMeta } from '@polkadot/extension-inject/types';
|
||||
import { JsonRpcResponse } from '@polkadot/rpc-provider/types';
|
||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||
import { KeyringPair } from '@polkadot/keyring/types';
|
||||
import { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types';
|
||||
@@ -67,6 +68,11 @@ export interface RequestSignatures {
|
||||
'pub(authorize.tab)': [RequestAuthorizeTab, null];
|
||||
'pub(bytes.sign)': [SignerPayloadRaw, ResponseSigning];
|
||||
'pub(extrinsic.sign)': [SignerPayloadJSON, ResponseSigning];
|
||||
'pub(rpc.listProviders)': [void, ResponseRpcListProviders];
|
||||
'pub(rpc.send)': [RequestRpcSend, JsonRpcResponse];
|
||||
'pub(rpc.startProvider)': [string, ProviderMeta];
|
||||
'pub(rpc.subscribe)': [RequestRpcSubscribe, number, JsonRpcResponse];
|
||||
'pub(rpc.unsubscribe)': [RequestRpcUnsubscribe, boolean];
|
||||
}
|
||||
|
||||
export type MessageTypes = keyof RequestSignatures;
|
||||
@@ -133,6 +139,22 @@ export type RequestAccountList = null;
|
||||
|
||||
export type RequestAccountSubscribe = null;
|
||||
|
||||
export interface RequestRpcSend {
|
||||
method: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
params: any[];
|
||||
}
|
||||
|
||||
export interface RequestRpcSubscribe extends RequestRpcSend {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface RequestRpcUnsubscribe {
|
||||
method: string;
|
||||
subscriptionId: number;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface RequestSigningApprovePassword {
|
||||
id: string;
|
||||
password: string;
|
||||
@@ -204,6 +226,8 @@ export interface ResponseAccountExport {
|
||||
exportedJson: string;
|
||||
}
|
||||
|
||||
export type ResponseRpcListProviders = ProviderList;
|
||||
|
||||
// Subscriptions
|
||||
|
||||
export type SubscriptionMessageTypes = NoUndefinedValues<{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import PostMessageProvider from './PostMessageProvider';
|
||||
import { Injected } from '@polkadot/extension-inject/types';
|
||||
import { SendRequest } from './types';
|
||||
|
||||
@@ -11,10 +12,13 @@ import Signer from './Signer';
|
||||
export default class implements Injected {
|
||||
public readonly accounts: Accounts;
|
||||
|
||||
public readonly provider: PostMessageProvider;
|
||||
|
||||
public readonly signer: Signer;
|
||||
|
||||
constructor (sendRequest: SendRequest) {
|
||||
this.accounts = new Accounts(sendRequest);
|
||||
this.provider = new PostMessageProvider(sendRequest);
|
||||
this.signer = new Signer(sendRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright 2019 @polkadot/extension authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ProviderInterfaceEmitCb, ProviderInterfaceEmitted } from '@polkadot/rpc-provider/types';
|
||||
import { AnyFunction } from '@polkadot/types/types';
|
||||
import { isUndefined, logger } from '@polkadot/util';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
|
||||
import { SendRequest } from './types';
|
||||
import { ProviderList, ProviderMeta, InjectedProvider } from '@polkadot/extension-inject/types';
|
||||
|
||||
const l = logger('PostMessageProvider');
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type CallbackHandler = (error?: null | Error, value?: any) => void;
|
||||
|
||||
// Same as https://github.com/polkadot-js/api/blob/57ca9a9c3204339e1e1f693fcacc33039868dc27/packages/rpc-provider/src/ws/Provider.ts#L17
|
||||
interface SubscriptionHandler {
|
||||
callback: CallbackHandler;
|
||||
type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name PostMessageProvider
|
||||
*
|
||||
* @description Extension provider to be used by dapps
|
||||
*/
|
||||
export default class PostMessageProvider implements InjectedProvider {
|
||||
readonly #eventemitter: EventEmitter;
|
||||
|
||||
readonly #sendRequest: SendRequest;
|
||||
|
||||
// Subscription IDs are (historically) not guaranteed to be globally unique;
|
||||
// only unique for a given subscription method; which is why we identify
|
||||
// the subscriptions based on subscription id + type
|
||||
readonly #subscriptions: Record<string, AnyFunction> = {}; // {[(type,subscriptionId)]: callback}
|
||||
|
||||
/**
|
||||
* @param {function} sendRequest The function to be called to send requests to the node
|
||||
* @param {function} subscriptionNotificationHandler Channel for receiving subscription messages
|
||||
*/
|
||||
public constructor (sendRequest: SendRequest) {
|
||||
this.#eventemitter = new EventEmitter();
|
||||
this.#sendRequest = sendRequest;
|
||||
|
||||
// Give subscribers time to subscribe
|
||||
setTimeout((): void => {
|
||||
this.#eventemitter.emit('connected');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a clone of the object
|
||||
*/
|
||||
public clone (): PostMessageProvider {
|
||||
return new PostMessageProvider(this.#sendRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Manually disconnect from the connection, clearing autoconnect logic
|
||||
*/
|
||||
public disconnect (): void {
|
||||
// FIXME This should see if the extension's state's provider can disconnect
|
||||
console.error('PostMessageProvider.disconnect() is not implemented.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary `true` when this provider supports subscriptions
|
||||
*/
|
||||
public get hasSubscriptions (): boolean {
|
||||
// FIXME This should see if the extension's state's provider has subscriptions
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Whether the node is connected or not.
|
||||
* @return {boolean} true if connected
|
||||
*/
|
||||
public isConnected (): boolean {
|
||||
// FIXME This should see if the extension's state's provider is connected
|
||||
return true;
|
||||
}
|
||||
|
||||
public listProviders (): Promise<ProviderList> {
|
||||
return this.#sendRequest('pub(rpc.listProviders)', undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Listens on events after having subscribed using the [[subscribe]] function.
|
||||
* @param {ProviderInterfaceEmitted} type Event
|
||||
* @param {ProviderInterfaceEmitCb} sub Callback
|
||||
* @return unsubscribe function
|
||||
*/
|
||||
public on (type: ProviderInterfaceEmitted, sub: ProviderInterfaceEmitCb): () => void {
|
||||
this.#eventemitter.on(type, sub);
|
||||
|
||||
return (): void => {
|
||||
this.#eventemitter.removeListener(type, sub);
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public send (method: string, params: any[], subscription?: SubscriptionHandler): Promise<any> {
|
||||
if (subscription) {
|
||||
const { callback, type } = subscription;
|
||||
|
||||
return this.#sendRequest(
|
||||
'pub(rpc.subscribe)',
|
||||
{ type, method, params },
|
||||
(res) => {
|
||||
subscription.callback(null, res);
|
||||
}
|
||||
).then((id): number => {
|
||||
this.#subscriptions[`${type}::${id}`] = callback;
|
||||
|
||||
return id;
|
||||
});
|
||||
} else {
|
||||
return this.#sendRequest('pub(rpc.send)', { method, params });
|
||||
}
|
||||
}
|
||||
|
||||
public startProvider (key: string): Promise<ProviderMeta> {
|
||||
return this.#sendRequest('pub(rpc.startProvider)', key);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public async subscribe (type: string, method: string, params: any[], callback: AnyFunction): Promise<number> {
|
||||
const id = await this.send(method, params, { type, callback });
|
||||
|
||||
return id as number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Allows unsubscribing to subscriptions made with [[subscribe]].
|
||||
*/
|
||||
public async unsubscribe (type: string, method: string, id: number): Promise<boolean> {
|
||||
const subscription = `${type}::${id}`;
|
||||
|
||||
// FIXME This now could happen with re-subscriptions. The issue is that with a re-sub
|
||||
// the assigned id now does not match what the API user originally received. It has
|
||||
// a slight complication in solving - since we cannot rely on the send id, but rather
|
||||
// need to find the actual subscription id to map it
|
||||
if (isUndefined(this.#subscriptions[subscription])) {
|
||||
l.debug((): string => `Unable to find active subscription=${subscription}`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
delete this.#subscriptions[subscription];
|
||||
|
||||
const result = await this.send(method, [id]);
|
||||
|
||||
return result as boolean;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user