Skip to main content

Chain module

Chain module provides a set of utilities related to retrieving, decoding and processing data from the Joystream blockchain.

@polkadot/api utilities

Creating api instance and checking node status

import { SnippetParams } from '../snippet'
// @snippet-begin
import {
createApi,
isMainnet,
isSyncing,
untilSynced,
} from '@joystream/sdk-core/chain'

// @snippet-end
export default async function ({ log }: SnippetParams) {
// @snippet-begin
const api = await createApi(`wss://mainnet.joystream.dev/rpc`)

if (isMainnet(api)) {
log('Connected to Joystream mainnet!')
if (await isSyncing(api)) {
log('The node is still syncing! Waiting until fully synced...')
await untilSynced(api)
}
log('The node is fully synced!')
}
// @snippet-end
}

Retrieving map entries from chain state

import { SnippetParams } from '../snippet'
// @snippet-begin
import { sortedEntries } from '@joystream/sdk-core/chain'

// @snippet-end
export default async function ({ api, log }: SnippetParams) {
// @snippet-begin
const storageBuckets = await sortedEntries(
api.query.storage.storageBucketById
)
for (const [id, bucket] of storageBuckets) {
log(id.toNumber())
log(bucket.toHuman())
log('\n')
}
// @snippet-end
}

Retrieving block data

You can use BlockUtils class from @joystream/sdk-core/chain to retrieve various information about blocks in the Joystream blockchain.

There are currently 2 data sources supported for retrieving this kind of information:

  • Joystream node RPC API (required)
  • Statescan GraphQL API (optional)

Using Statescan API can speed up some operations, but in case it's not available, BlockUtils will always fallback to using the Joystream node RPC API.

Initializing BlockUtils

Without Statescan API:

import { createApi } from '@joystream/sdk-core/chain'

const api = await createApi('wss://mainnet.joystream.dev/rpc')
const blocks = new BlockUtils(api)

With Statescan API:

import { createApi } from '@joystream/sdk-core/chain'
import { BlockUtils } from '@joystream/sdk-core/chain/blocks'
import { createStatescanClient } from '@joystream/sdk-core/query/statescan'

const api = await createApi('wss://mainnet.joystream.dev/rpc')
const statescanClient = createStatescanClient(
'https://explorer-graphql.joystream.org/graphql'
)
const blocks = new BlockUtils(api, statescanClient)

Get block info by hash

import { SnippetParams } from '../snippet'

export default async function ({ blocks, log }: SnippetParams) {
// @snippet-begin
const block = await blocks.blockInfo(
'0x8c0e3bcfcdd99053cbf6ce0e0a2ea229190f2407d823deca0a150996dddfe639'
)
log(block)
// @snippet-end
}

Get block info by block number

import { SnippetParams } from '../snippet'

export default async function ({ blocks, log }: SnippetParams) {
// @snippet-begin
const block = await blocks.blockInfo(12_000_000)
log(block)
// @snippet-end
}

Estimate block number from date

warning

Note that this function only estimates the block number based on a provided staring point (last finalized block by default) and an assumed blockrate of 1 block / 6 sec. If that is not accurate enough for you, consider using .exactBlockAt

import { SnippetParams } from '../snippet'

export default async function ({ blocks, log }: SnippetParams) {
// @snippet-begin
const blockNumber = await blocks.estimateBlockNumberAt(
new Date('2025-01-01 00:00:00')
)
log(blockNumber)
// @snippet-end
}

Using predefined starting point

import { SnippetParams } from '../snippet'

export default async function ({ blocks, log }: SnippetParams) {
// @snippet-begin
const startingPoint = await blocks.blockInfo(10_000_000)
const blockNumber = await blocks.estimateBlockNumberAt(
new Date('2025-01-01 00:00:00'),
startingPoint
)
log(blockNumber)
// @snippet-end
}

Find exact block by date

warning

This method could be slow, especially when using BlockUtils without Statescan API.

import { SnippetParams } from '../snippet'

export default async function ({ blocks, log }: SnippetParams) {
// @snippet-begin
const block = await blocks.exactBlockAt(new Date('2025-01-01 00:00:00'))
log(block)
// @snippet-end
}

Conversion utilities

Time interval as number of blocks

import { asBlocks } from '@joystream/sdk-core/chain/blocks'
import { SnippetParams } from '../snippet'
// @snippet-begin
import { asBlocks } from '@joystream/sdk-core/chain/blocks'

// @snippet-end
export default async function ({ log }: SnippetParams) {
// @snippet-begin
log(`30 seconds is ~${asBlocks(30, 's')} blocks`)
log(`15 minutes is ~${asBlocks(15, 'm')} blocks`)
log(`4 hours is ~${asBlocks(4, 'h')} blocks`)
log(`3 days is ~${asBlocks(3, 'd')} blocks`)
log(`4 weeks is ~${asBlocks(4, 'w')} blocks`)
// @snippet-end
}

Number of blocks as time interval

import { asTime } from '@joystream/sdk-core/chain/blocks'
import { SnippetParams } from '../snippet'
// @snippet-begin
import { asTime } from '@joystream/sdk-core/chain/blocks'

// @snippet-end
export default async function ({ log }: SnippetParams) {
// @snippet-begin
const numbers = [42, 123, 2025]
for (const number of numbers) {
log(`${number} blocks is ${asTime(number, 'ms').toFixed(2)} miliseconds`)
log(`${number} blocks is ${asTime(number, 's').toFixed(2)} seconds`)
log(`${number} blocks is ${asTime(number, 'm').toFixed(2)} minutes`)
log(`${number} blocks is ${asTime(number, 'h').toFixed(2)} hours`)
log(`${number} blocks is ${asTime(number, 'd').toFixed(2)} days`)
log('\n')
}
// @snippet-end
}