Skip to content

Instantly share code, notes, and snippets.

@josepot
Created April 4, 2025 12:58
Show Gist options
  • Save josepot/e38606a1f423b0940a3ea3bf50702b0a to your computer and use it in GitHub Desktop.
Save josepot/e38606a1f423b0940a3ea3bf50702b0a to your computer and use it in GitHub Desktop.
validator-of-a-block.ts
import { dot, IdentityData, ppl } from "@polkadot-api/descriptors"
import { createClient } from "polkadot-api"
import { getSmProvider } from "polkadot-api/sm-provider"
import { chainSpec } from "polkadot-api/chains/polkadot"
import { chainSpec as chainSpecPpl } from "polkadot-api/chains/polkadot_people"
import { start } from "polkadot-api/smoldot"
import {
Struct,
u32,
u8,
type SS58String,
} from "@polkadot-api/substrate-bindings"
import { concatMapEager } from "@polkadot-api/observable-client"
const smoldot = start()
const relayChain = await smoldot.addChain({ chainSpec })
const pplChain = smoldot.addChain({
chainSpec: chainSpecPpl,
potentialRelayChains: [relayChain],
})
const babeHeaderDec = Struct({ type: u8, idx: u32 }).dec
// Connect to the polkadot relay chain.
const relayClient = createClient(getSmProvider(relayChain))
const dotApi = relayClient.getTypedApi(dot)
const pplClient = createClient(getSmProvider(pplChain))
const pplChainApi = pplClient.getTypedApi(ppl)
const getNameFromIdentityData = (input: IdentityData) => {
if (input.type === "None" || input.type === "Raw0") return null
if (input.type === "Raw1") return String.fromCharCode(input.value)
return input.value.asText()
}
const getDisplayNames = async (address: SS58String): Promise<string[]> => {
let identityOf = await pplChainApi.query.Identity.IdentityOf.getValue(address)
if (identityOf) {
const display = getNameFromIdentityData(identityOf[0].info.display)
return display ? [display] : []
}
const superOf = await pplChainApi.query.Identity.SuperOf.getValue(address)
if (!superOf) return []
const [parentAddress, childIdentity] = superOf
const childDisplay = getNameFromIdentityData(childIdentity)
const parentDisplays = await getDisplayNames(parentAddress)
return childDisplay ? [...parentDisplays, childDisplay] : parentDisplays
}
let latestValidators: Array<{ address: SS58String; identity: string }> = []
let latestEpoch = 0n
const getValidators = async (blockHash: string) => {
const epoch = await dotApi.query.Babe.EpochIndex.getValue({ at: blockHash })
if (epoch === latestEpoch) return latestValidators
const validators = await dotApi.query.Session.Validators.getValue({
at: blockHash,
})
latestValidators = (await Promise.all(validators.map(getDisplayNames))).map(
(identity, idx) => ({
identity: identity.join("/"),
address: validators[idx],
}),
)
latestEpoch = epoch
return latestValidators
}
relayClient.finalizedBlock$
.pipe(
concatMapEager(async (block) => {
const [header, validators] = await Promise.all([
relayClient.getBlockHeader(block.hash),
getValidators(block.hash),
])
const preRuntime = header.digests.find(
(digest) => digest.type === "preRuntime",
)!
const { idx } = babeHeaderDec(preRuntime.value.payload)
return {
...block,
...validators[idx],
}
}),
)
.subscribe(console.log, console.error)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment