Ocelloids1 SDK is an open-source software development kit written in Typescript for Polkadot and Substrate based networks. It simplifies the implementation of multi-chain programs and provides domain-specific logic for different pallets.
- Composable Reactive Streams — Easily filter blockchain data and react to occurrences of interest using composable reactive streams.
- Data Sources — Retrieve data from extrinsics, blocks, events and storage.
- Powerful Query Operators — Filter data using integrated operators that support complex queries in the Mongo query language. Includes support for big numbers and advanced features such as dynamic queries.
- Flexible Type Conversions — Seamlessly convert data into a terse queryable format.
- Extended Context Types — Extend base generic events and extrinsics with contextual information such as block number, block position and extrinsic position.
- Nested Calls Extraction — Supports recursive flattening with event correlation of nested utility (i.e. batch calls), multisig and proxy calls within an extrinsic.
- Multi-Chain Support — Interact with multiple networks.
- Pallet-Specific Modules — Modules designed to handle use cases related to a particular pallet, such as tracking calls and events from the contracts pallet.
Check our Quickstart Guide to set up your first Ocelloids program.
npm i @sodazone/ocelloids-sdk
Provides essential abstractions, reactive operators, base type converters, and pallet-independent functionality.
Source code packages/core.
npm i @sodazone/ocelloids-sdk-contracts
Provides operators and type converters for the contracts pallet.
Source code packages/pallets/contracts.
Refer to the SDK documentation.
You can also explore some example applications in the examples/ folder.
Here's a basic usage example that filters balance transfer events from finalized blocks above a certain amount:
import { WsProvider } from '@polkadot/api';
import {
SubstrateApis,
finalizedBlocks,
filterEvents
} from '@sodazone/ocelloids-sdk';
const apis = new SubstrateApis({
polkadot: {
provider: new WsProvider('wss:https://rpc.polkadot.io')
}
});
apis.rx.polkadot.pipe(
finalizedBlocks(),
filterEvents({
section: 'balances',
method: 'Transfer',
'data.amount': { $bn_gte: '50000000000' }
})
).subscribe(
x => console.log(x.toHuman())
);
Extended event output - click to expand
Extended event output with contextual information:
{
eventId: '16134479-5-1',
extrinsicId: '16134479-5',
extrinsicPosition: 1,
blockNumber: '16,134,479',
method: 'Transfer',
section: 'balances',
index: '0x0502',
data: {
from: '14GuP6QAfK9uwo3MQ9LrcmEqttcrtoNfDaSHn2BVaYcJJBg6',
to: '12But7r26e2UwZkSYC8bU5nQdyfqWXswZEwS1tbH9nD8CXvK',
amount: '54,719,854,400'
}
}
The event identifier eventId
consists of the block number, the position of the extrinsic within the block,
and the position of the event within the extrinsic.
Filter events operator details - click to expand
The filterEvents
operator used in the example is composed of the following stack:
source.pipe(
// Extracts extrinsics with events
extractTxWithEvents(),
// Flattens and correlates events for nested
// batch, multisig, proxy and derivative calls
flattenCalls(),
// Filters at the extrinsic level
// mainly for success or failure
mongoFilter(extrinsicsCriteria),
// Maps the events with
// block and extrinsic context
extractEventsWithTx(),
// Filters over the events
mongoFilter(eventsQuery),
// Share multicast
share()
)
Here's an example that monitors the balance of an account with an amount threshold condition:
import { WsProvider } from '@polkadot/api';
import { switchMap } from 'rxjs';
import {
SubstrateApis,
mongoFilter
} from '@sodazone/ocelloids-sdk';
const apis = new SubstrateApis({
polkadot: {
provider: new WsProvider('wss:https://rpc.polkadot.io')
}
});
apis.query.polkadot.pipe(
switchMap(q => q.system.account(
'15QFBQY6TF6Abr6vA1r6opRh6RbRSMWgBC1PcCMDDzRSEXf5'
)),
mongoFilter({
'data.free': { $bn_lt: '6038009840776279' }
})
).subscribe(x => console.log('Account Balance:', x.toHuman()));
Now, let's explore a dynamic query example that collects and monitors balance transfer events for a set of addresses, starting from ALICE's address:
Dynamic query example - click to expand
import { WsProvider } from '@polkadot/api';
import '@polkadot/api-augment';
import {
SubstrateApis,
blocksInRange,
filterEvents,
ControlQuery
} from '@sodazone/ocelloids-sdk';
function transfersOf(addresses: string[]) {
return ControlQuery.from({
$and: [
{ section: 'balances' },
{ method: 'Transfer' },
{
$or: [
{ 'data.from': { $in: addresses } },
{ 'data.to': { $in: addresses } }
]
}
]
});
}
const apis = new SubstrateApis({
polkadot: {
provider: new WsProvider('wss:https://rpc.polkadot.io')
}
});
const seenAddresses = new Set<string>([ALICE]);
let dynamicQuery = transfersOf([...seenAddresses]);
apis.rx.polkadot.pipe(
blocksInRange(16134439, 100),
filterEvents(dynamicQuery)
).subscribe(event => {
console.log('Event: ', event.toHuman());
if (apis.promise.polkadot.events.balances.Transfer.is(event) ) {
const transfer = event.data;
const from = transfer.from.toPrimitive();
const to = transfer.to.toPrimitive();
seenAddresses.add(from);
seenAddresses.add(to);
// Updates dynamic query, probably you want
// to update it only for new seen addresses
dynamicQuery.change(transfersOf([...seenAddresses]));
}
});
This example introduces the concept of a dynamic query. As new addresses are encountered, the dynamic query is updated with the newly seen addresses by calling dynamicQuery.change()
. This ensures that future events will be filtered based on the updated set of addresses.
To contribute to the development of Ocelloids, ensure that you have the following requirements installed:
To set up the development environment, follow these steps:
corepack enable
- Install dependencies:
yarn install
- Build Ocelloids libraries:
yarn build
The Ocelloids repository utilizes workspaces for modularization.
The repository contains three main folders: packages
, examples
and tools
.
The packages
folder contains the Ocelloids SDK implementation, which is further divided into core
, pallets
, and test
modules.
The development support tools include functionalities such as chain data capture to assist in the development of the SDK.
The examples/ folder contains example applications.
To run unit tests, use the following command:
yarn test
Additional test data and mocks are available in packages/test/
for your convenience. If necessary, you can capture specific data using the development support tools located in tools/
for testing purposes.
If you encounter the issue of @sodazone/ocelloids-sdk-test
being marked as unresolved
in the spec
test files after building the project, you can resolve it by following these steps:
- Open any typescript file of the project.
- Run the command "TypeScript: Reload project" to reload the TypeScript project configuration.
For further assistance or troubleshooting, please consult the project's documentation or reach out to the Ocelloids community.
Footnotes
-
Noun ocelloid (plural ocelloids): “(microbiology) a cellular structure found in unicellular microorganisms that is analogous in structure and function to eyes, which focus, process and detect light.” ↩