Sentio Processor logo

Sentio Processor

Organization
sentioxyz
sentio-processor

Use when initializing Sentio projects, writing blockchain processor code, adding contracts/ABIs, testing processors, or deploying to the Sentio platform. Triggers on sentio CLI usage, processor development, multi-chain indexing, blockchain data analytics with Sentio SDK, DeFi protocols, points systems, or store entities.

Overview

Publishersentioxyz
Repositorysentio-ai-kit
Skill namesentio-processor
Stars
84
Forks
1
Bundled files
6
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 6 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by sentioxyz on GitHub. Read the source before you install it.

Installation

Install the Sentio Processor AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/sentioxyz/sentio-ai-kit/tree/main/skills/sentio-processor
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/sentioxyz/sentio-ai-kit.git /tmp/sentio-ai-kit
mkdir -p .claude/skills
cp -r /tmp/sentio-ai-kit/skills/sentio-processor .claude/skills/sentio-processor
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Sentio Processor in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Sentio Processor on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Sentio Processor is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Managing Sentio Processors

Overview

Sentio SDK is a TypeScript blockchain data indexing platform. Processors handle on-chain events, transactions, and state changes across Ethereum, Aptos, Sui, Solana, Starknet, Bitcoin, Cosmos, Fuel, and IOTA.

Reference examples: See sentioxyz/sentio-processors for 120+ production processors.

When to Use

  • Creating/initializing a Sentio indexing project
  • Adding contracts or ABIs to a project
  • Writing processor code for any supported chain
  • Testing processors with TestProcessorServer
  • Deploying processors with sentio upload
  • Configuring sentio.yaml
  • Multi-chain or multi-contract setups
  • DeFi protocols (DEX, lending, staking, points systems)

Progressive Disclosure

This skill follows a layered approach:

  1. This file — CLI commands, core patterns, all handler types, metrics/events/store overview, best practices
  2. references/advanced-patterns.md — Multi-contract binding, GlobalProcessor, lazy caching, view calls, partition keys, baseLabels
  3. references/defi-patterns.md — Price feeds, DEX/AMM, lending protocols, TVL tracking, Aptos/Sui DEX helpers
  4. references/store-and-points.md — Store schema advanced features (relationships, timeseries, indexes, iterators, filter operators)
  5. references/position-tracking-templates.md — 10 protocol-specific points/position tracking templates (Simple Holding, Aave, Morpho, Vault/LP, Uni V3, Pendle, Compound, NFT, Uni V2, Uni V4)
  6. references/production-examples.md — 7 complete production processor examples (Uniswap, AAVE, Cetus, LiquidSwap, Lombard points, Scallop)
  7. references/sui-sdk4-migration.md — Upgrading to SDK 4: cross-chain breaking changes (dep bumps, Solana @anchor-lang/core, proto/RPC stack, dropped v3 back-compat) plus the Sui gRPC deep dive — raw data shapes (transaction/object/event, protobuf oneof, Move type-string comma spacing + address form), with Sui source citations

Project Lifecycle

sentio create → sentio add → sentio gen → write processor → sentio test → sentio upload → sentio processor status

1. Initialize Project

bash
npx @sentio/cli create <project-name> --chain-type <type> --chain-id <id>
FlagDescriptionDefault
--chain-typeeth, aptos, sui, solana, iota, fuel, starknet, raweth
--chain-idEVM chain ID (only for eth)1
--subprojectMonorepo mode (no root dependencies)false

2. Add Contracts & Generate Types

bash
sentio add <address> --chain <chain> --name <Name>   # Downloads ABI, updates sentio.yaml
sentio gen                                             # Generate TypeScript bindings
sentio build                                           # Full: gen + typecheck + bundle
FlagDescription
-c, --chainChain ID (1, 56) or name (aptos_mainnet, sui_mainnet)
-n, --nameContract/module display name
--api-keyExplorer API key (Etherscan)

3. Login & Deploy

bash
sentio login                              # OAuth browser flow (prod)
sentio login --api-key <key>              # API key auth

sentio upload                             # Build + deploy
sentio upload --skip-build                # Deploy only
sentio upload --continue-from <ver>       # Hot-swap without re-indexing
sentio upload --checkpoint "1:18000000"   # Rollback to specific block
sentio upload --num-workers=4             # Parallel workers for compute-heavy processors

4. Verify Processor is Running

After upload, use sentio processor status --project <owner>/<slug> to check the processor status. If the processor is in ERROR state, use sentio processor logs --project <owner>/<slug> --level ERROR to get error details, diagnose the issue, fix the code, and re-upload.

package.json

json
{
  "name": "project-name",
  "type": "module",
  "scripts": {
    "build": "sentio build",
    "gen": "sentio gen",
    "upload": "sentio upload",
    "test": "sentio test"
  },
  "dependencies": {
    "@sentio/sdk": "^4.0.0"
  },
  "devDependencies": {
    "@sentio/cli": "^4.0.0",
    "typescript": "^6.0.3"
  }
}

ethers: SDK 4 uses upstream ethers 6.x — the old @sentio/ethers fork has been dropped. Do not pin or override ethers to @sentio/ethers. When migrating an older project, remove any leftover override such as:

json
// ❌ remove this from older projects — it reintroduces the fork and causes
//    duplicate-ethers type conflicts (BaseContract / DeferredTopicFilter, TS2344)
"overrides": {
  "ethers": "npm:@sentio/ethers@6.13.1-patch.4"
}

sentio.yaml Quick Reference

yaml
project: owner/project-name     # Required
host: prod                       # prod | test | staging | local

contracts:
  - chain: "1"                   # EVM: numeric IDs ("1", "56", "137", "42161")
    address: "0x..."
    name: "USDC"
  - chain: aptos_mainnet         # Move: aptos_mainnet, sui_mainnet, iota_mainnet
    address: "0x1"
  - chain: sui_mainnet
    address: "0xdee9..."
    name: "deepbook"

networkOverrides:                # Redirect chain ID to custom RPC
  - chain: '2390'
    host: "https://rpc.tac.build"

variables:                       # Runtime env vars
  - key: API_KEY
    value: xxx
    isSecret: true

debug: false
numWorkers: 1

Processor Patterns by Chain

Proxy contracts: Use the implementation contract's ABI while binding to the proxy address.

Ethereum / EVM

typescript
import { Counter, Gauge } from '@sentio/sdk'
import { EthChainId } from '@sentio/chain'
import { ERC20Processor } from '@sentio/sdk/eth/builtin'
// Or generated: import { MyContractProcessor } from './types/eth/mycontract.js'

const transfers = Counter.register('transfers')

ERC20Processor.bind({
  address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
  network: EthChainId.ETHEREUM,
  startBlock: 6082465,
})
  .onEventTransfer(async (event, ctx) => {
    transfers.add(ctx, 1)
    ctx.eventLogger.emit('Transfer', {
      distinctId: event.args.from,
      from: event.args.from,
      to: event.args.to,
      amount: event.args.value.scaleDown(6),
    })
  })
  .onTimeInterval(async (block, ctx) => {}, 60, 240)

Handlers: onEvent*, onBlockInterval, onTimeInterval, onTransaction, onTrace, onCallXxx

Event Filtering

Filter events to only process matching ones (e.g., only mints):

typescript
const mintFilter = ERC20Processor.filters.Transfer(
  "0x0000000000000000000000000000000000000000", // from = null address (mint)
  null  // any recipient
);

ERC20Processor.bind({ address: TOKEN, network: NETWORK })
  .onEventTransfer(async (event, ctx) => {
    // only mint events reach here
  }, mintFilter);
Generic Event Handler

Catches any event from the contract:

typescript
.onEvent(async (event, ctx) => {
  // event.eventName to determine which event
});
Function Call Handler (onCallXxx)

Triggers on internal function calls (trace-based). Access args and return values:

typescript
MyContractProcessor.bind({ address: CONTRACT, network: NETWORK })
  .onCallDeposit(async (call, ctx) => {
    // call.args, call.returnValue, call.error
  });

GlobalProcessor: GlobalProcessor.bind({ network }).onBlockInterval(handler)

Aptos

typescript
import { AptosNetwork } from '@sentio/sdk/aptos'
import { coin } from '@sentio/sdk/aptos/builtin/0x1'

coin.bind({ network: AptosNetwork.MAIN_NET })
  .onEventWithdrawEvent(async (evt, ctx) => {
    ctx.meter.Counter('withdrawals').add(1)
    ctx.eventLogger.emit('Withdraw', {
      distinctId: evt.guid.account_address,
      amount: evt.data_decoded.amount,
    })
  })

Handlers: onEvent*, onEntry*, onTransaction, onTimeInterval, onVersionInterval Resources: AptosResourcesProcessor.bind({ address }).onResourceChange(handler, typeString)

Sui

typescript
import { SuiNetwork, SuiObjectTypeProcessor } from '@sentio/sdk/sui'
import { pool } from './types/sui/deepbook.js'

pool.bind({ network: SuiNetwork.MAIN_NET })
  .onEventSwap(async (evt, ctx) => {
    ctx.meter.Counter('swaps').add(1)
  })

SuiObjectTypeProcessor.bind({ objectType: pool.Pool.type() })
  .onObjectChange((changes, ctx) => {
    ctx.meter.Counter('pool_updates').add(changes.length)
  })

Handlers: onEvent*, onEntryFunctionCall, onObjectChange, onTimeInterval Note: Use startCheckpoint: 8500000n (BigInt) instead of startBlock for Sui.

Solana

On SDK 4, Solana uses @anchor-lang/core + @solana/kit. Import BorshInstructionCoder (and Idl/Instruction) from @sentio/sdk/solana, not from @coral-xyz/anchor:

typescript
import { SolanaGlobalProcessor } from '@sentio/sdk/solana'
import { BorshInstructionCoder, type Idl } from '@sentio/sdk/solana'

SolanaGlobalProcessor.bind({
  name: 'my-program',
  instructionCoder: new BorshInstructionCoder(idl as Idl),
}).onInstruction('transfer', async (instruction, ctx, accounts) => {
  ctx.meter.Counter('transfers').add(1)
})

Starknet

typescript
StarknetProcessor.bind({ address: '0x...', network: StarknetChainId.STARKNET_MAINNET })
  .onEvent('Transfer', async (events, ctx) => {
    ctx.meter.Counter('transfers').add(events.length)
  })

Factory Pattern (ProcessorTemplate)

For dynamically-created contracts (e.g., DEX pair factory):

typescript
import { UniswapV2PairProcessorTemplate } from "./types/eth/uniswapv2pair.js";
import { UniswapV2FactoryProcessor } from "./types/eth/uniswapv2factory.js";

// 1. Define template with handlers
const pairTemplate = new UniswapV2PairProcessorTemplate()
  .onEventSwap(async (event, ctx) => {
    ctx.meter.Counter("swap_volume").add(event.args.amount0Out);
  })
  .onEventSync(async (event, ctx) => {
    ctx.meter.Gauge("reserve0").record(event.args.reserve0);
  });

// 2. Bind factory and dynamically bind new pairs
UniswapV2FactoryProcessor.bind({
  address: FACTORY_ADDRESS,
  network: NETWORK,
})
  .onEventPairCreated(async (event, ctx) => {
    pairTemplate.bind(
      { address: event.args.pair, startBlock: ctx.blockNumber },
      ctx
    );
  });

Metrics & Events

CRITICAL — Metric Naming: Names MUST NOT end with reserved suffixes: _count, _sum, _avg, _min, _max, _last. These are appended automatically by Sentio. Error: COUNTER name "X" is invalid, cannot use [_count _sum _avg _min _max _last] as the suffix: invalid meta

❌ Counter.register('transfer_count')    → invalid suffix _count
❌ ctx.meter.Counter('pool_sum')         → invalid suffix _sum
✅ Counter.register('transfers')
✅ ctx.meter.Counter('pools_created')
typescript
// Global registration (recommended)
const counter = Counter.register('name', { unit: 'token' })
counter.add(ctx, value, { label: 'value' })

// Inline via context
ctx.meter.Counter('name').add(value, { label: 'value' })
ctx.meter.Gauge('name').record(value, { label: 'value' })

// Sparse gauge (high cardinality — many pools/tokens)
const vol = Gauge.register("vol", {
  sparse: true,
  aggregationConfig: {
    intervalInMinutes: [60],
    discardOrigin: true,  // only keep aggregated vol_count, vol_sum
  },
})

// Event logging
ctx.eventLogger.emit('EventName', {
  distinctId: address,     // Primary entity ID (used for user-level analytics)
  message: 'description',
  // ... arbitrary key-value attributes
  // IMPORTANT: Numeric attributes used as time series metrics must be number or BigDecimal.
  // Passing string/undefined/null for numeric fields causes "invalid time series data" errors.
})

// Exporter (export to external systems via webhook)
ctx.exporter.emit({ key: "value" })

Label cardinality warning: ~10,000 unique series limit per metric. NEVER use wallet addresses, tx hashes, or token IDs as labels. Use event logs or entities for high-cardinality data instead.

Store (Database) API

Define entities in schema.graphql, generated by sentio gen:

graphql
type AccountSnapshot @entity {
  id: ID!
  timestampMilli: BigInt!
  balance: BigInt!
}
typescript
import { AccountSnapshot } from "./schema/store.js"

await ctx.store.get(AccountSnapshot, id)
await ctx.store.upsert(new AccountSnapshot({ id, timestampMilli, balance }))
await ctx.store.list(AccountSnapshot, [])  // List all
await ctx.store.delete(AccountSnapshot, id)

Critical: Enable sequential execution when using store to prevent race conditions:

typescript
import { GLOBAL_CONFIG } from "@sentio/runtime"
GLOBAL_CONFIG.execution = { sequential: true }

See references/store-and-points.md for points system patterns.

Price Feeds

typescript
import { getPriceByType, token } from "@sentio/sdk/utils"

const info = await token.getERC20TokenInfo(EthChainId.ETHEREUM, tokenAddr)
const price = await getPriceByType(EthChainId.ETHEREUM, tokenAddr, ctx.timestamp) || 0
const usdValue = amount.scaleDown(info.decimal).multipliedBy(price)

See references/defi-patterns.md for caching patterns and DeFi examples.

Testing

typescript
import { TestProcessorServer, firstCounterValue } from '@sentio/sdk/testing'

const service = new TestProcessorServer(() => import('./processor.js'))
before(async () => { await service.start() })

// Ethereum
const resp = await service.eth.testLog(mockTransferLog('0x...', { from, to, value }))
assert.equal(firstCounterValue(resp.result, 'transfers'), 1n)
ChainFacetKey Methods
Ethereumservice.ethtestLog(), testBlock(), testTransaction(), testTrace()
Aptosservice.aptostestEvent(), testCall(), testResourceChange()
Suiservice.suitestEvent(), testObjectChange()
Solanaservice.solanatestInstruction()
bash
sentio test                              # All tests
sentio test --test-name-pattern="swap"   # Filter by name

Project Structure

my-project/
  sentio.yaml              # Project config
  schema.graphql           # Store entity definitions (optional)
  package.json             # @sentio/sdk + @sentio/cli deps
  tsconfig.json
  abis/{chain}/            # Contract ABIs
  src/
    processor.ts           # Main processor code
    processor.test.ts      # Tests
    types/                 # Auto-generated (sentio gen)
    schema/                # Auto-generated store entities
  dist/lib.js              # Bundled output (sentio build)

Best Practices

  1. Use sequential: true when handlers share state via entity store
  2. Do NOT use global variables for persistent state — use ctx.store
  3. Use startBlock to skip irrelevant history and reduce backfill cost
  4. Prefer Metrics over Logs/Entities for aggregated numerical data (lower cost)
  5. Never use addresses/hashes as metric labels — ~10k series limit per metric
  6. Use event filters (Processor.filters) to reduce unnecessary handler executions
  7. Increase backfill intervals to the largest acceptable value for cost reduction
  8. Use listIterator() instead of list() for large entity datasets
  9. Use Promise.all for parallel contract calls and entity processing
  10. Use BigDecimal and scaleDown() for precision — avoid floating point
  11. Filter null addresses with isNullAddress() for mint/burn events
  12. Skip self-transfers — if (from == to) return;

Common Mistakes

MistakeFix
Metric name ends with _count, _sum, _avg, _min, _max, _lastUse a different name: transfers not transfer_count, pools_created not pool_count
"invalid time series data" in eventLoggerEnsure numeric fields are number or BigDecimal, not string/undefined/null; use || 0 for fallible prices
Writing processor before sentio genAlways sentio add then sentio gen first
Wrong import path for generated typesUse ./types/{chain}/{name}.js (.js extension for ESM)
Forgetting .scaleDown(decimals).scaleDown(18) for ETH, .scaleDown(6) for USDC
Not setting startBlock / startCheckpointProcessor starts from genesis without it
Store entities without sequential executionAdd GLOBAL_CONFIG.execution = { sequential: true }
Deploying without loginRun sentio login first
Caching resolved values instead of PromisesCache the Promise to prevent duplicate concurrent RPC calls

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Sentio Processor AI skill do?

Use when initializing Sentio projects, writing blockchain processor code, adding contracts/ABIs, testing processors, or deploying to the Sentio platform. Triggers on sentio CLI usage, processor development, multi-chain indexing, blockchain data analytics with Sentio SDK, DeFi protocols, points systems, or store entities.

Why use Sentio Processor on TypingMind?

Because you install it once and use it with any model. Sentio Processor is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Sentio Processor in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/sentioxyz/sentio-ai-kit/tree/main/skills/sentio-processor. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Sentio Processor?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Sentio Processor?

As many as you like. As long as a model supports skills, you can use Sentio Processor with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Sentio Processor AI skill free?

It is published on GitHub by sentioxyz. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇