Awesome
<div align="center"> <picture> <img src="https://github.com/sodazone/ocelloids-sdk/blob/main/.github/assets/oc_sdk.png?raw=true" width="330" height="auto" alt="" /> </picture>Packages | Documentation | Examples
<p align="center"> <a href="https://github.com/sodazone/ocelloids-sdk/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/sodazone/ocelloids-sdk/ci.yml?branch=main&color=69D2E7&labelColor=A7DBD8" alt="CI" /></a> <a href="https://github.com/sodazone/ocelloids-sdk/blob/main/LICENSE"><img src="https://img.shields.io/github/license/sodazone/ocelloids-sdk?color=69D2E7&labelColor=A7DBD8" alt="License" /></a> </p> </div>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.
Features
- 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.
Quickstart
Check our Quickstart Guide to set up your first Ocelloids program.
Installation
Ocelloids Core
<a href="https://www.npmjs.com/package/@sodazone/ocelloids-sdk"> <img src="https://img.shields.io/npm/v/@sodazone/ocelloids-sdk?color=69D2E7&labelColor=69D2E7&logo=npm&logoColor=333333" alt="npm @sodazone/ocelloids-sdk" /> </a>npm i @sodazone/ocelloids-sdk
Provides essential abstractions, reactive operators, base type converters, and pallet-independent functionality.
Source code packages/core.
Ocelloids Contracts
<a href="https://www.npmjs.com/package/@sodazone/ocelloids-sdk-contracts"> <img src="https://img.shields.io/npm/v/@sodazone/ocelloids-sdk-contracts?color=69D2E7&labelColor=69D2E7&logo=npm&logoColor=333333" alt="npm @sodazone/ocelloids-sdk-contracts" /> </a>npm i @sodazone/ocelloids-sdk-contracts
Provides operators and type converters for the contracts pallet.
Source code packages/pallets/contracts.
Usage
Refer to the SDK documentation.
You can also explore some example applications in the examples/ folder.
Example: Filtering Transfer Events
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://rpc.polkadot.io')
}
});
apis.rx.polkadot.pipe(
finalizedBlocks(),
filterEvents({
section: 'balances',
method: 'Transfer',
'data.amount': { $bn_gte: '50000000000' }
})
).subscribe(
x => console.log(x.toHuman())
);
<details>
<summary>Extended event output - click to expand</summary>
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'
}
}
</details>
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.
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()
)
</details>
Example: Account Balance
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://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()));
Example: Dynamic Query
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:
<details> <summary>Dynamic query example - click to expand</summary>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://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]));
}
});
</details>
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.
Development
Requirements
To contribute to the development of Ocelloids, ensure that you have the following requirements installed:
Install
To set up the development environment, follow these steps:
corepack enable
- Install dependencies:
yarn install
- Build Ocelloids libraries:
yarn build
Project Layout
The Ocelloids repository utilizes workspaces for modularization.
The repository contains three main folders: packages
, examples
and tools
.
Packages
The packages
folder contains the Ocelloids SDK implementation, which is further divided into core
, pallets
, and test
modules.
Tools
The development support tools include functionalities such as chain data capture to assist in the development of the SDK.
Examples
The examples/ folder contains example applications.
Testing
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.
Troubleshooting
Visual Studio Code
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.” ↩