npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@substrate/light-client-extension-helpers

v2.5.2

Published

<br /><br />

Downloads

306,136

Readme

Components

  • backgroundHelper: Manages the core logic running in the background, including Smoldot instance registration, chain synchronization, active connections, and communication with the content script.
  • contentScriptHelper: Handles communication between the tab's webpage and the extension's background script.
  • webPageHelper: Provides an interface for the tab’s webpage to interact with the extension.
  • extensionPagesHelper: A set of functions to manage persisted chains, connections, and boot-nodes within the extension page.
  • smoldot: A thin abstraction over smoldot package that includes a restart method for the smoldot client

Example Usage

backgroundHelper

The module exposes a register function that is meant to be called in your background script. It does all the heavy lifting to manage your extension's connection to smoldot.

How You Can Use It

import { register } from "@substrate/light-client-extension-helpers/background"

const { lightClientPageHelper, addOnAddChainByUserListener } = register({
  smoldotClient: start({ maxLogLevel: 4 }),
  getWellKnownChainSpecs: () =>
    // these well known chains will be connected to when the extension is
    // started and their connection will always be maintained. The location
    // of these chainspecs is relative to your `assets` directory.
    Promise.all(
      [
        "./chainspecs/polkadot.json",
        "./chainspecs/ksmcc3.json",
        "./chainspecs/westend2.json",
      ].map((path) =>
        fetch(chrome.runtime.getURL(path)).then((response) => response.text()),
      ),
    ),
})

addOnAddChainByUserListener(async (inputChain) => {
  // listen for chain updates
})

contentScriptHelper

This module exposes a register function that should be called in your content script. Once registered you can access the light client provider in your inpage script.

This is useful if you are implementing the @substrate/discovery protocol

How You Can Use It

// constants.ts
export const CHANNEL_ID = "substrate-wallet-template"
// content/index.ts
import { register } from "@substrate/light-client-extension-helpers/content-script"
import { CHANNEL_ID } from "../constants"

register(CHANNEL_ID)
// inpage/index.ts
import { getLightClientProvider } from "@substrate/light-client-extension-helpers/web-page"

const provider = getLightClientProvider(CHANNEL_ID)

webPageHelper

The webpage helper allows any dapp to discover the light client provider that was registered using the contentScriptHelper. However this functionality has been superseded by the discovery protocol.

How You Can Use It

// use-provider.ts
import {
  type LightClientProvider,
  getLightClientProvider,
} from "@substrate/light-client-extension-helpers/web-page"
import { useEffect, useState } from "react"
import { useIsMounted } from "./useIsMounted"

const providers = new Map<string, Promise<LightClientProvider>>()

export const useLightClientProvider = (channelId: string) => {
  const [provider, setProvider] = useState<LightClientProvider>()
  const isMounted = useIsMounted()

  useEffect(() => {
    if (!providers.has(channelId))
      providers.set(channelId, getLightClientProvider(channelId))
    providers.get(channelId)?.then((provider) => {
      if (!isMounted()) return
      setProvider(provider)
    })
  }, [channelId, isMounted])

  return { provider }
}

extensionPagesHelper

The extension page helper is conceptually the same as the web page helper, except it is used in the user interface of your extension. By importing helper, you can access light client functionality.

How You Can Use It

Get and set the bootnodes

const getBootNodes = async (chainId: string) =>
  (await helper.getChains()).find(
    ({ genesisHash }) => genesisHash === wellKnownGenesisHashByChainId[chainId],
  )?.bootNodes ?? []

const setBootNodes = async (chainId: string, bootNodes: string[]) =>
  helper.setBootNodes(wellKnownGenesisHashByChainId[chainId], bootNodes)

Decode call data

import { getObservableClient } from "@polkadot-api/observable-client"
import { getViewBuilder } from "@polkadot-api/view-builder"
import { createClient } from "@polkadot-api/substrate-client"
import { helper } from "@substrate/light-client-extension-helpers/extension-page"
import { filter, firstValueFrom } from "rxjs"

export const decodeCallData = async (chainId: string, callData: string) => {
  const chains = await helper.getChains()
  const chain = chains.find(({ genesisHash }) => genesisHash === chainId)
  if (!chain) throw new Error("unknown chain")
  const client = getObservableClient(createClient(chain.provider))
  const { metadata$, unfollow } = client.chainHead$()
  try {
    const metadata = await firstValueFrom(metadata$.pipe(filter(Boolean)))
    return getViewBuilder(metadata).callDecoder(callData)
  } finally {
    unfollow()
    client.destroy()
  }
}

smoldot

The smoldot package is a streamlined version of the original smoldot package. Here are the key differences:

  1. Restarts

    The smoldot client now includes a restart method. This is useful for restarting smoldot if it unexpectedly crashes, allowing you to quickly fire up a new instance.

  2. Modified Add Chain Options

    When smoldot restarts, all prior connections made with client.addChain are lost. To address this, the AddChainOptions has been modified. The potentialRelayChains parameter is now a recursive AddChainOptions array instead of a Chain array. This ensures that when smoldot restarts and you need to call addChain to reconnect a parachain, the associated relaychain is always reconnected first.

  3. Supervision

    This module also provides a supervise method, which checks the health of smoldot and automatically invokes the restart method if it detects an issue. It does this by repeatedly calling addChain with an empty string as the chainspec parameter. If addChain throws an error that isn't an AddChainError, it assumes smoldot is unhealthy and restarts it.


How You Can Use It

import { start } from "@substrate/light-client-extension-helpers/smoldot"

// Initialize the smoldot client
const client = start()

// Add a chain
client.addChain({
  chainSpec: "...",
  potentialRelayChains: [
    {
      chainSpec: "...",
      potentialRelayChains: [], // Recursive AddChainOptions array
    },
  ],
})

supervise(client, { onError: console.error })

// Supervise the client
client.supervise()

tx-helper

The tx-helper package allows you to easily sign transactions. You just need the calldata and an implementation of the @polkadot-api/signer interface.

PJS Example Implementation

import { getLightClientProvider } from "@substrate/light-client-extension-helpers/web-page"
import { connectInjectedExtension } from "@polkadot-api/pjs-signer"
import { fromHex, toHex } from "@polkadot-api/utils"
import { createTx } from "@substrate/light-client-extension-helpers/tx-helper" // 👈 create-tx import

const CHANNEL_ID = "..."
const lightClientProvider = await getLightClientProvider(CHANNEL_ID)

const createTx = async (chainId: string, from: string, callData: string) => {
  const chains = Object.values(lightClientProvider.getChains())
  const chain = chains.find(({ genesisHash }) => genesisHash === chainId)

  if (!chain) {
    throw new Error("unknown chain")
  }

  const injectedExt = await connectInjectedExtension("polkadot-js")

  const account = injectedExt
    .getAccounts()
    .find((account) => toHex(account.polkadotSigner.publicKey) === from)

  if (!account) {
    throw new Error("no account")
  }

  const signer = account.polkadotSigner // 👈 @polkadot-api/signer implementation

  const tx = await createTx(chain.connect)({
    callData: fromHex(callData),
    signer,
  })

  return toHex(tx)
}