# All files / src Detector.ts

92% Statements 23/25
78.57% Branches 11/14
75% Functions 3/4
92% Lines 23/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130

    6x

    6x
    6x
    6x

    6x
    6x

    6x
    6x

    6x
    4x

    4x
    4x
    4x

    2x
    1x
    1x
    1x

    5x

    5x
    5x

    6x
    4x
    4x

     import { ApiPromise } from '@polkadot/api';
    import { ApiOptions } from '@polkadot/api/types';
    import { ProviderInterface } from '@polkadot/rpc-provider/types';
    import { SmoldotProvider }  from './SmoldotProvider/SmoldotProvider.js';
    import { ExtensionProvider } from './ExtensionProvider/ExtensionProvider.js';
    import westend from './specs/westend.json';
    import kusama from './specs/kusama.json';
    import polkadot from './specs/polkadot.json';

    /**
     * Detector is an API for providing an instance of the PolkadotJS API configured
     * to use a WASM-based light client.  It takes care of detecting whether the
     * user has the substrate connect browser extension installed or not and
     * configures the PolkadotJS API appropriately; falling back to a provider
     * which instantiates the WASM light client in the page when the extension is
     * not available.
     *
     * @example
     *
     * ```
     * import { Detector } from '@substrate/connect';
     *
     * // Create a new UApp with a unique name
     * const app = new Detector('burnr-wallet');
     * const westend = await app.detect('westend');
     * const kusama = await app.detect('kusama');
     *
     * await westend.rpc.chain.subscribeNewHeads((lastHeader) => {
     *   console.log(lastHeader.hash);
     * );
     * await kusama.rpc.chain.subscribeNewHeads((lastHeader) => {
     *   console.log(lastHeader.hash);
     * });
     *
     * // Interact with westend and kusama APIs ...
     *
     * await westend.disconnect();
     * await kusama.disconnect();
     * ```
     */
    export class Detector {
      #chainSpecs: Record<string, unknown> = {
        'polkadot': polkadot,
        'kusama': kusama,
        'westend': westend
      }
      #name: string;
      #isExtension: boolean;
      #providers: Record<string, ProviderInterface> = {};

      /**
       * name is the name of the app. This is used by the extension to identify
       * your app in the user interface
       */
      get name(): string {
        return this.#name;
      }

      /**
       * @param name - the name of your app.
       *
       * @remarks
       *
       * You should make a best effort to make your app name unique to avoid
       * confusion for users in the extension user interface
       */
      public constructor (name: string) {
        this.#isExtension = !!document.getElementById('substrateExtension');
        this.#name = name;
      }

      /**
       * connect attempts to detect the extension and configures the PolkadotJS
       * API instance to return appropriately.
       *
       * There are 3 bundled networks: "polkadot", "kusama" and "westend" which
       * require no further configuration.
       *
       * Alternatively you may supply a chain spec and options to connect to a
       * custom chain with a light client (in-page only).
       *
       * @param chainName - the name of the blockchain network to connect to
       * @param chainSpec - an optional chainSpec to connect to a different network
       * @param options - any extra API options to passed to PolkadotJS when
       * constructing it.
       * @returns a promise that resolves to an instance of the PolkadotJS API
       *
       * @remarks
       *
       * When providing a custom chain spec, detect will always configure and return
       * an in-page light client and not use the extension
       *
       * Typically options is used to pass custom types to PolkadotJS API.
       *
       * See the PolkadotJS docs for documentation on using PolkadotJS.
       *
       * {@link https://polkadot.js.org/docs/}
       */
      public connect = async (chainName: string, chainSpec?: string, options?: ApiOptions): Promise<ApiPromise> => {
        let provider: ExtensionProvider | SmoldotProvider = {} as ExtensionProvider | SmoldotProvider;

        if (Object.keys(this.#chainSpecs).includes(chainName)) {
          Iif (this.#isExtension) {
            provider = new ExtensionProvider(this.#name, chainName);
          } else Eif (!this.#isExtension) {
            const chainSpec = JSON.stringify(this.#chainSpecs[chainName]);
            provider = new SmoldotProvider(chainSpec);
          }
        } else if (chainSpec) {
            provider = new SmoldotProvider(chainSpec);
        } else Eif (!chainSpec) {
          throw new Error(`No known Chain was detected and no chainSpec was provided. Either give a known chain name ('${Object.keys(this.#chainSpecs).join('\', \'')}') or provide valid chainSpecs.`)
        }
        await provider.connect();

        this.#providers[chainName] = provider as ProviderInterface;
        return await ApiPromise.create(Object.assign(options ?? {}, {provider}));
      }

      /**
       * disconnect disconnects the PolkadotJS API isntance
       *
       * @param chainName - the name of the blockchain network to disconnect from
       */
      public disconnect = async (chainName: string): Promise<void> => {
        await this.#providers[chainName].disconnect();
        delete this.#providers[chainName];
      };
    }